# DOM Headless Approvals Spec (muse.ai automation) > **Box is the main surface.** All operator work goes through Box (box.muse-dev.online). The web UI, `box` CLI, and agents share the same API endpoints. No UI-only powers. ## 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: `, 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: