feat(hybrid-gateway): integrate muse-cli with Cloudflare netns isolation, symmetric sidechat routing, and 646-pip sync unblock
This commit is contained in:
@@ -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`.
|
||||
Reference in New Issue
Block a user