QALS Wiki · the qalarc network⌂ qalarc.com/projects/qals
generated 2026-09-16 · qalcode autonomous research

gmux agent fleet ↔ QALS wallets + phone approvals · gmux-bridge/README.md

gmux-bridge — the QALS economy wired into gmux's agent fleet

Created 2026-09-11 by the integrator session (project: crypto_currency). Answer to the question "check that it could be used for the gmux interface also": yes — and here is the bridge that does it.

gmux (terminal/tmux generation, ~/projects/gmux, read-only — this bridge changes nothing there) shows a fleet of AI agents as coloured panes: 🟢 working, 🔴 waiting, ⚠ permission needed, 🟡 idle. What gmux does NOT know is what each agent costs. gmux-bridge adds the money layer: every gmux session maps to a loopd capped wallet, every permission request becomes a message on the owner's phone, and one screen shows status colour AND Qals spent/cap for every agent.

Architecture map

 gmux box (unchanged, read-only for v1)
 ┌────────────────────────────────────────────────────────────┐
 │ tmux panes ── phone_bridge.py :8768  GET /status {panes[]} │
 │              (state from QalCode2 API: /session/status,    │
 │               SSE /event → working/waiting/permission)     │
 │              or monitor state file /tmp/gmux-pane-state.json│
 └──────────────┬─────────────────────────────────────────────┘
                │ ① auto-poll (same box) or ② POST /fleet/report (remote)
                ▼
                gmux-bridge :8842  (this folder, zero-dep)
                │                    │
                │ ③ provision        │ ④ alerts
                ▼                    ▼
        loopd :8823            qalchat relay :8830
        gmux-<session>         identity "gmux-bridge" → owner handle
        capped wallet          → encrypted message → owner's phone
        (200c/day default)       inbox (same UI as normal chat)
                ▲
                │ ⑤ the agent itself pays via MCP:
        qalmcp (--handle gmux-<session>) → send_payment / buy_compute
        → loopd hold → settle — hard-capped server-side, prompt-injection
          can only spend what the cap allows

Mapping table:

gmux concept QALS concept Mechanism
tmux session / window (pane name) loopd agent gmux-<session> POST /agents/provision (idempotent, 200c/day cap, 2000c seed)
Pane state colour (working/waiting/permission) — (display only) merged verbatim from gmux into /fleet/status
Permission request (! orange pane) qalchat alert to owner POST /alerts → encrypted DM from gmux-bridge
Agent pays for tools/compute loopd hold → settle agent's agent_id+api_secret into qalmcp; caps enforced by loopd, not the model
Phone dashboard :8768 (gmux) phone inbox (qalchat/qalbridge) same device, same inbox for messages + money alerts

Run it

# prereqs already running: loopd :8823, qalchat relay :8830
python3 qalchat/qalchat.py init gmux-bridge   # once — the bridge's identity
python3 gmux-bridge/serve.py                  # serves http://127.0.0.1:8842
open http://127.0.0.1:8842/                   # one-screen fleet ⊕ Qals view

API

Endpoint What
GET /fleet/status every gmux pane (state + colour) merged with its wallet (spent/cap today, budget ok/warn/exceeded); usage totals; unprovisioned_sessions list
POST /agents/provision {"gmux_session_name":"doofing"} creates (or returns the existing) gmux-doofing loopd wallet → {agent_id, api_secret, cap_cents_per_day} — hand these to the agent's qalmcp config
POST /alerts {"session":"ai_diary","level":"permission","title":"write tool","detail":"..."} encrypted qalchat message to $GMUXBRIDGE_OWNER; also appended to data/alerts.jsonl
GET /alerts?limit=20 recent alerts (programmatic check)
GET /usage/today sum of all gmux-* wallets' spend + per-wallet rows
POST /fleet/report {"panes":[...]} manual ingest — see below

Config via env: GMUXBRIDGE_PORT (8842), GMUXBRIDGE_LOOPD, GMUXBRIDGE_GMUX_URL (:8768), GMUXBRIDGE_STATE_FILE (/tmp/gmux-pane-state.json), GMUXBRIDGE_OWNER (default owner), GMUXBRIDGE_QALCHAT_IDENTITY (gmux-bridge), GMUXBRIDGE_SEED_CENTS (2000), GMUXBRIDGE_DEFAULT_CAP_CENTS (200).

Wiring the real gmux box (read-only over there)

gmux-bridge auto-detects panes in this order:

  1. HTTPGET http://127.0.0.1:8768/status (gmux's phone_bridge, aiohttp; if that server is up on the same box, zero config).
  2. State file/tmp/gmux-pane-state.json (written by gmux's monitor even when the phone bridge isn't serving; same box only).
  3. Manual ingest — for a REMOTE gmux box (or if neither exists): have any gmux hook (tmux hook, cron, the phone_bridge process itself) post its panes. A manual report is trusted for GMUXBRIDGE_MANUAL_TTL_MS (default 60 s) — past that it goes stale and /fleet/status honestly drops to source: offline rather than showing a ghost fleet. Post at least that often.
# The exact curl a gmux-side hook would call — no gmux code changes needed,
# e.g. a cron'd one-liner or a tmux periodic hook on the gmux box:
curl -s -X POST http://<bridge-box>:8842/fleet/report \
  -H 'Content-Type: application/json' \
  -d "$(curl -s http://127.0.0.1:8768/status)"          # forward gmux verbatim
# or the raw monitor state file (shape is accepted too):
curl -s -X POST http://<bridge-box>:8842/fleet/report \
  -H 'Content-Type: application/json' -d "{\"panes\": $(cat /tmp/gmux-pane-state.json)}"
# (when posting the raw dict shape, wrap it OR post it as-is — both accepted)

Alerts the other way (gmux → phone): anything that can see a permission event can POST /alerts. A natural v2 hook: gmux's pane-watcher notices a permission state and posts the alert; v1 ships the endpoint and documents the call.

Test

bash gmux-bridge/test_bridge.sh    # 30 checks, live loopd+relay, ALL GREEN

Proves: gmux HTTP ingest (against mock_gmux.py, byte-identical /status shape), provisioning + idempotence, real HMAC hold→settle spend, colour/spend merge, alert → owner mailbox via qalchat recv, manual ingest fallback, usage sums, HTML dashboard.

Honest v1 limits