Files
box/docs/META-ACCOUNTS-API.md
T

8.1 KiB

Meta Accounts Center API

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.

Status: active (2026-10-03) Motivation: the phone number used in the muse.ai OTP pilot is linked to a Facebook account with its own credentials. Meta account management must be a separate, isolated API — never tangled with the phone-OTP flow.

What it is

A dedicated operator CLI + privileged gateway for automating Accounts Center operations: the Meta hub where Facebook, Instagram, WhatsApp, and Meta accounts are linked, with shared login, security settings, and connected experiences.

It is not a general Facebook/Instagram automation tool. It manages the account linkage and security surface — which accounts are linked, how they log into each other, 2FA/password state, login activity. Content operations (posting, messaging) are out of scope.

Separation guarantees

  1. Credential namespace: uses meta-creds.sh types facebook / instagram only. The phone number is a contact method on the Facebook account, never a credential identifier. The phone-otp route (muse.ai SMS flow) and Meta credentials never share IDs, profiles, or audit logs.
  2. Browser/session reuse: Meta automation runs in the agent's existing chrome-box profile — the same profile serves muse.ai and accountscenter.meta.com. The 1:1 rule (node == agent == profile) already gives us one egress per agent; no separate Meta node needed. The phone-OTP login that established the muse.ai session also establishes the Meta session, so read ops reuse it without re-authenticating.
  3. Authorization without exposure: follows CREDENTIAL-GATEWAY.md. Agents know credential IDs and target actions; the privileged gateway decrypts from store.age and injects via CDP. Secrets never enter agent context, logs, or chat.
  4. Audit: every call logs {ts, credential_id, action, target} — never values. Separate audit trail from phone-OTP operations.

Operations

Read (operator may run freely)

Command What it returns
meta-acct.sh list-linked <cred-id> Accounts in the Accounts Center (type, username, linked since)
meta-acct.sh login-activity <cred-id> Recent logins: device, IP/geo, time; flags unrecognized
meta-acct.sh security-status <cred-id> 2FA on/off + method, login alerts on/off, password age
meta-acct.sh connected-experiences <cred-id> Per-experience toggles: cross-app login, cross-posting, etc.

Write (human-gated via /etc/netvm/approvals/)

Command Gate reason
meta-acct.sh add-account <cred-id> --type <facebook|instagram> Links a new account; changes login blast radius
meta-acct.sh remove-account <cred-id> --account <id> Unlinks; some connected experiences don't revert cleanly
meta-acct.sh set-login-linking <cred-id> --on|--off Controls cross-app login (see 2026 change below)
meta-acct.sh rotate-password <cred-id> Credential change; invalidates sessions
meta-acct.sh set-2fa <cred-id> --method <sms|app|key> Security-critical

Human gates use the existing netvm-approve.sh flow: the agent prepares the exact action, the human approves the specific (credential_id, action, target) tuple, the gateway executes once.

The early-2026 login change

Meta is removing the "Logging in with accounts" toggle: starting early 2026, all accounts in the same Accounts Center log into each other by default. Accounts Center membership becomes the login boundary.

Implications for this API:

  • list-linked is now security-critical: anyone with one account's credentials can reach the others.
  • The API must surface a "blast radius" view: for a given credential, which accounts are reachable through the Accounts Center.
  • remove-account becomes the only way to revoke cross-app login — it must stay human-gated and be auditable.
  • Recommend: keep each client's accounts in their own Accounts Center; never mix clients (consistent with the one-client-one-credential-set rule).

Architecture

Agent (untrusted) ──▶ meta-acct.sh (operator CLI)
                           │  validates scope, checks human-gate
                           ▼
                     Gateway (privileged, runs as operator)
                           │  meta-creds.sh get <type> <id>
                           │  decrypts store.age, never logs values
                           ▼
                     CDP ──▶ accountscenter.meta.com
                             (in the agent's chrome-box profile —
                              same browser as the muse.ai session)
  • Transport: Unix socket IPC between agent and gateway (not network).
  • Injection: CDP Runtime.evaluate into the Accounts Center DOM; falls back to field-focus + virtual keyboard only if DOM injection fails.
  • 2FA during a write op: the gateway pauses, requests the code through the existing OTP handoff (human relays), resumes. The code is transient — used once, never stored.

Registry

ACCOUNTS.md rows per Meta login, same discipline as today: pending_auth → active, 2fa-pending where applicable. The notes column records the Accounts Center linkage (e.g. "facebook + instagram linked in one Accounts Center; blast radius = 2 accounts"). No credential values, no phone numbers.

Change detection

bin/meta-ac-snapshot.py turns the smoke test into a change detector. Each run captures a structural snapshot via CDP inside the node's netns and diffs against snapshots/meta-ac/baseline.json:

  • redirect_chain (normalized: query params stripped — nonces change every visit): seeded with the navigation target, then polls for where Meta sends us. Catches auth-flow reroutes.
  • dom_markers: form IDs, input names, button labels. Catches page restructures.
  • final_title: sanity signal.

Outcomes: PASS (matches), CHANGED (structural diff — baseline untouched, human reviews then --promote), FAIL (automation broke). Snapshots are timestamped JSON; the diff doubles as the audit trail for what Meta changed and when.

Validated 2026-10-03 on bl: headless Chromium in warp-phone netns via CDP reached accountscenter.meta.com → expected Meta auth redirect (auth.meta.com OIDC), login page renders. Baseline established; second run PASS.

Out of scope (explicit)

  • Posting, messaging, ad management, Page/Portfolio operations.
  • The phone-OTP / muse.ai flow (separate API, separate credentials — see docs/PHONE-OTP.md).
  • WhatsApp linking (not available in all regions; revisit later).
  • Business Suite / Partner access (different trust model; separate spec if needed).

Open questions

  1. Which NetVM node hosts Meta automation — new dedicated node, or reuse an existing client node? (Lean: dedicated meta node, one egress.)
  2. Should read ops also require the approvals flow for high-sensitivity accounts, or is operator-level enough?
  3. Recovery codes: store in store.age (retrievable) or human-only?

Implementation status (2026-10-03)

Read ops implemented in bin/meta-acct.py + bin/meta-acct.sh:

  • list-linked AGENT — linked profiles from account_overview. Returns email (if present), profiles array with name/type (instagram, ai_device). Verified on pip (1 Instagram + Muse) and 646 (Muse only, no email).
  • security-status AGENT — security checkup action count + section list from password_and_security.
  • login-activity AGENT — "Where you're logged in" section text. (Currently returns the section header; the expandable session list needs a click-through — future work.)

Key finding: the phone-OTP login for muse.ai leaves a full Meta session in the browser profile. accountscenter.meta.com loads without a login redirect. No separate Meta login needed — the agent profile IS the Accounts Center session.

Usage: runs inside the agent's netns via meta-acct.sh wrapper:

~/Projects/NetVM/bin/meta-acct.sh list-linked pip

Write ops (add-account, remove-account, etc.) remain human-gated per the spec above. Not yet implemented.