Land Phase 0 launch chain: SSH -> desktop Terminal -> claude -> phone
Phase 0 proven by hand (N=3) across multiple rooms. - scripts/gui-launch.sh: open a desktop Terminal via osascript so claude runs in the GUI session (login Keychain + real TTY), avoiding a long-lived token (D11). - scripts/launch-claude.sh: name the session `claude -n "<repo> - <topic>"` so Remote Control's phone conversation index is readable. - .env.example: bot credential schema (real .env stays gitignored). - AGENTS.md / ROADMAP.md: D11, Phase 0 results, Phase 1 carry-overs.
This commit is contained in:
@@ -98,6 +98,14 @@ Condensed from the scoping workshop. Each: the call, why, what it beat.
|
||||
the bot ever handles sensitive content over untrusted transport.
|
||||
- **D10 — Spark Control manages the bot (Phase 3).** Status on the dashboard + one-click
|
||||
update/restart, the same SSH-behind-buttons pattern Spark Control uses for the Sparks today.
|
||||
- **D11 — Launch into a desktop Terminal, not a headless token (Phase 0).** The SSH session
|
||||
can't reach the GUI login Keychain, so a plain `ssh … claude` reports "Not logged in." Rather
|
||||
than mint a long-lived `claude setup-token`, the launcher (`scripts/gui-launch.sh`) uses
|
||||
`osascript` to open a Terminal.app window in the **GUI session**, where `claude` inherits the
|
||||
existing Keychain login and a real TTY. *Beat:* the long-lived OAuth token (Approach A) — works
|
||||
and is fully unattended, but adds a credential to manage; kept as the documented fallback if the
|
||||
Mac is ever driven headless (logged out). *Cost:* requires the Mac logged in + a one-time
|
||||
Terminal Automation grant.
|
||||
|
||||
## Sovereignty constraint
|
||||
|
||||
@@ -128,9 +136,51 @@ once" is not done.
|
||||
- **Scaffolded 2026-06-15** from a prior scoping package (SPEC/DECISIONS/CLAUDE/KICKOFF),
|
||||
folded into this AGENTS.md (decisions + placement), `ROADMAP.md` (phases), and the wrapper +
|
||||
config skeleton. No bot code yet — by design.
|
||||
- **Next: Phase 0 (manual chain validation, N=3)** — Matrix onboarding (Element, Space, first
|
||||
room, a bot user), write + locally test `scripts/launch-claude.sh`, passwordless SSH from the
|
||||
Spark to the Mac, prove the full chain (message → SSH → wrapper → Claude session → phone
|
||||
notification I can drive) by hand at least 3 times, and record the first room→repo mapping.
|
||||
Bot code starts only after Phase 0 is proven. The original KICKOFF prompt is the step-by-step
|
||||
for Phase 0.
|
||||
- **Phase 0 — SSH leg proven (2026-06-15).** Mac Remote Login is on. The Spark `spark-32d0`
|
||||
(user `modelo`) reaches the Mac over `starttunnel`/WireGuard at `10.59.211.5` — *not* the
|
||||
LAN (the Spark isn't on the Mac's LAN subnet). A dedicated per-machine key
|
||||
(`spark-control@spark-32d0` = `~/.ssh/id_ed25519` on the Spark) is in the Mac's
|
||||
`authorized_keys`. SSH alias **`mac-bridge`** in the Spark's `~/.ssh/config` selects that key
|
||||
(`IdentityFile ~/.ssh/id_ed25519` + `IdentitiesOnly yes`) — required because the pre-existing
|
||||
`Host * → id_ed25519_shared` rule otherwise shadows the default key. The bot's entire Mac hop
|
||||
is therefore `ssh mac-bridge '<command>'`. *Phase 1:* bake the dedicated key + an equivalent
|
||||
alias/config into the bot's Docker image (modelo's `~/.ssh/config` won't exist in the
|
||||
container).
|
||||
- **Phase 0 — launch chain proven end-to-end (2026-06-15).** `ssh mac-bridge → gui-launch.sh
|
||||
→ launch-claude.sh → authenticated claude → phone via Remote Control` works against a real
|
||||
repo (`premier-gunner`). Chose **Approach B (desktop Terminal)** over a headless token — see
|
||||
**D11**. Two pieces it took: (1) `~/.local/bin` (where `claude` lives) had to be added to
|
||||
`~/.zprofile`, because a non-interactive login shell skips `.zshrc`; (2) `scripts/gui-launch.sh`
|
||||
opens a Terminal.app window via `osascript` so `claude` runs inside the GUI session (login
|
||||
Keychain + real TTY) — needed a one-time "Allow ssh to control Terminal" Automation grant.
|
||||
*Known caveats for the bot:* (a) a never-trusted repo stalls at Claude's first-run folder-trust
|
||||
gate — unattended launches must target already-trusted repos or pass a skip flag; (b) if the
|
||||
TCC Automation grant ever resets, a launch stalls until someone clicks Allow — the bot should
|
||||
detect a failed launch and report it back to the room, not hang.
|
||||
- **Phase 0 — Matrix bot user live (2026-06-15).** Homeserver is the StartOS Synapse exposed on
|
||||
**clearnet at `https://matrix.gilliam.ai`** (`server_name` = `matrix.gilliam.ai`, Synapse
|
||||
1.154.0) — *not* the stale `@gilliam:<onion>` account found in Element. Created a dedicated
|
||||
non-admin bot **`@agent:matrix.gilliam.ai`** (type `bot`) via the Synapse Admin Dashboard
|
||||
(StartOS "Create Bot User" is appservice-only/greyed out). Minted a long-lived access token
|
||||
(fixed `device_id` `matrix-bridge-bot`), verified via `whoami`, and stored
|
||||
homeserver/user/token/device_id (+ password for recovery) in the gitignored **`.env`** (chmod
|
||||
600). `config.toml` holds homeserver+user; `.env.example` documents the schema. Bot reuses the
|
||||
stored token — never re-login per start (avoids device churn); no E2EE (D9). *Note:* the
|
||||
bot↔Synapse hop is now public-internet TLS, which softens D9's "transport already WireGuard-
|
||||
private" rationale (still TLS to the user's own server, single-user content) — revisit if it matters.
|
||||
- **Phase 0 — rooms mapped (2026-06-15).** 9 project rooms in `config.toml` (premier-gunner,
|
||||
recap, recap-relay, spark-control, ten31-transcripts, ten31-signal-engine, keysat, proof-of-work,
|
||||
ten31-database), each `room_id → /Users/macpro/Projects/<repo>`. `@agent` is **joined to all 9**
|
||||
(via its token), so the Phase-1 bot will see messages in each. *Manual by-hand launches must keep
|
||||
message text free of `'`/`"`* — the typed SSH command line breaks on them (PS2 `>` hang); the
|
||||
Phase-1 bot avoids this via `shlex.quote`.
|
||||
- **Phase 0 — PROVEN / DONE (2026-06-15).** N=3 by-hand runs succeeded across multiple rooms
|
||||
(recap, spark-control, premier-gunner): each opened a Terminal in the right repo, started `claude`
|
||||
on the message, and pushed a drivable session to the phone. The deterministic core holds.
|
||||
Added session naming: `launch-claude.sh` now runs `claude -n "<repo> - <topic>"` (topic from the
|
||||
message, overridable via `$MB_SESSION_NAME`) so Remote Control's phone index is readable —
|
||||
confirmed `-n` drives the phone app's conversation label.
|
||||
- **Next: Phase 1 — the matrix-nio bot.** Container on the Spark, logged in as `@agent` (token in
|
||||
`.env`), listening in the 9 mapped rooms; on a message it runs `ssh mac-bridge gui-launch.sh
|
||||
<repo_dir> <message>` (built with `shlex.quote`) and reports failures back to the room. See
|
||||
ROADMAP Phase 1 (also: bake key+config into the image, curated `$MB_SESSION_NAME` topic, fail-loud).
|
||||
|
||||
Reference in New Issue
Block a user