feat(hybrid-gateway): integrate muse-cli with Cloudflare netns isolation, symmetric sidechat routing, and 646-pip sync unblock

This commit is contained in:
operator
2026-10-04 22:54:01 +00:00
parent 3d5fbe5aeb
commit 1a271b1bbd
60 changed files with 5068 additions and 55 deletions
+77
View File
@@ -0,0 +1,77 @@
# DOM Headless Approvals Spec (muse.ai automation)
## Overview
When headless automation (`muse-chat-api.py` via CDP) drives a muse.ai
session, the browser may surface permission/confirmation dialogs that block
the flow (e.g. "Allow pip to share information with 34.139.37.135?").
This spec defines how the automation detects, classifies, and handles those
dialogs. Principle (from INFRA.md): **the chat IS the approval interface** —
no file-based queue; the automation signals when stuck and the operator
resolves it in conversation.
## Definitions
- **Approval dialog**: any in-DOM permission/confirmation prompt that gates
the automation's next action.
- **Trusted origin**: an IP in `TRUSTED_IPS` — our own infrastructure, where
auto-approval is safe. Current set:
- `34.139.37.135` — VM (gateway)
- `100.123.153.75` — bl (main compute)
- `100.81.31.9` — VM tailnet
- **APPROVAL_NEEDED**: the escalation signal. Printed to stderr as
`APPROVAL_NEEDED: <dialog summary>`, process exits with code **2**.
## Detection
`check_approvals(ws)` evaluates in the page DOM:
1. Body text containing both `Allow` and `to share` → permission prompt.
Narrows to elements whose innerText contains both and is < 500 chars.
2. Two or more buttons whose text includes `allow`, `deny`, or `block`
→ likely permission dialog; captures the closest container's text.
Returns a list of `(dialog_text, is_trusted, action_taken)`.
## Classification
Extract IPv4 addresses from the dialog text. The dialog is **trusted** iff
any extracted IP is in `TRUSTED_IPS`. Dialogs with no recognizable IP are
**untrusted** (fail closed).
## Handling
- **Trusted**: auto-approve by clicking the button whose text contains
`allow once`, else the button whose text is exactly `allow`.
Records `clicked:<button text>`.
- **Untrusted**: do NOT click. Return `APPROVAL_NEEDED`; the calling command
prints the summary to stderr and exits 2.
## Enforcement points
| Command | Behavior |
|---|---|
| `send` | Checks approvals first. Untrusted dialog → `APPROVAL_NEEDED`, exit 2, message NOT sent. |
| `messages` | Non-blocking check (observes, does not gate). |
| `wait` | Re-checks every 5s during the wait. Untrusted dialog → `APPROVAL_NEEDED`, exit 2. |
| `approvals` | Reports pending dialogs with trusted/action status. Never gates. |
## Exit-code contract
| Code | Meaning |
|---|---|
| `0` | Done. |
| `1` | Failed (bad args, no page, send error, …). |
| `2` | `APPROVAL_NEEDED` — human input required; safe to retry after resolution. |
## Operator flow
1. Automation exits 2 with `APPROVAL_NEEDED: <details>`.
2. Operator relays the details to the user via chat.
3. User provides the needed input (e.g. confirms, provides OTP).
4. Operator re-runs the command; the dialog is either gone or now trusted.
## Non-goals (explicitly out of scope)
- **DM content provenance**: this spec covers *browser dialogs encountered by
automation*. It does NOT authenticate who authored a DM. A message injected
via `dm.py send` carries no signature; a receiving agent cannot verify the
claimed sender from the tooling. (Observed 2026-10-03: pip's agent rightly
refused to act on an unsigned DM.) Provenance is a separate spec.
- **Credential approval**: Meta account write operations use the human-gated
flow in `docs/META-ACCOUNTS-API.md`, not this spec.
## Future work
- Allowlist dialog *types* (not just IPs) for finer auto-approval.
- Structured `APPROVAL_NEEDED` payloads (JSON) for machine-readable relay.
- DONE 2026-10-03: dm-level provenance via ssh-keygen signing. `dm-sign.sh` signs with `ssh-keygen -Y sign -n dm` (file-based); `dm.py send --raw` transports the signed block verbatim; `dm.py verify-sig` verifies against `dm-signers/<sender>.pub` via `ssh-keygen -Y verify`.