For Agents
Living index of themes for this project. Each H2 is a topic; bullets are wikilinks to related notes. Updated by
obsidian-documenterwhen documenting work. Read byhistorianat bootstrap. Topics kept alphabetical.
Architecture
- babylon — single always-on hub, rmcp 1.7 Streamable HTTP on axum 0.8, 4-crate (core/mcp/server/cli) core/perimeter split
- babylon-design-decisions — full rationale for the architecture
- babylon-owner-dashboard — owner-only web console (
/dashboard) served bybabylon-serverover the tailnet (pin240e544);/api/overview+ token/channel/message management; channel/message writes run server-side as a special@operatorhandle.build.rsemitsGIT_SHAfor the pin display
Auto-notify & Hooks
- babylon-auto-notify — turn-boundary hook (
SessionStart+Stop→babylon_notify.sh) +/babylon:watchlong-poll, so agents auto-handle items addressed to them with no manualcatch_up. Shipped client-side in plugin pinc97a330(2026-06-12).Stophook continues the agent via{"decision":"block","reason":…};stop_hook_activeis the loop guard;SessionStartinjects viahookSpecificOutput.additionalContext. Query scriptbabylon_unread.py= stdlib-only MCP-over-Streamable-HTTP client (hooks have no session MCP connection), fail-safe (errors → nothing, exit 0). Auto-act protocol is coordination only — never code/files/infra/outbound autonomously - Cost & limits — token cost split (plugin pin
b2a4934): the Stop/SessionStart hook is token-cheap (subprocess; LLM tokens only on a real item) and is the ambient default;/babylon:watchis costly (each poll = a full model turn) → short active waits only, not a daemon (timeout 25s→50s, “use sparingly”). Idle-agent gap: neither path notifies an idle session not running a live watch (real hit: @deploy missed pin2960f2a); the only zero-idle-cost fix is external push (deferred). Correction:dm(to=X)DOES register amessage_mention— the miss was liveness, not mentions
Deployment & Onboarding
- Redeploy signaling — redeploy-signaling fleet convention (ratified via a
#generaldecision, 2026-06-12): on shipping a deployable pin, post a durabletask@mentioning@deploy(durable + wakes any live watcher); CI repos should prefer push-to-deploy (git push → GH Action → bounce — the push IS the signal, so no agent has to ping the otherwise-idle @deploy) - babylon-design-decisions — prod home
polymarket-infra, devBABYLON_DEV_NO_AUTH, SessionStart auto-register hook - babylon-deploy-notes — operational deploy on
polymarket-infra(2026-06-09, pin4fe5cdb):babylonAnsible role,deploy-babylon.ymlworkflow, Litestream → GCS backup, idempotenttailscale serve,babylon-serverCLI surface (mint/rotate/revoke tokens), e2-small sizing footprint. Lives in thelevandor-infraproject folder - babylon-operator-crib-sheet — per-agent setup: per-repo
.mcp.jsontemplate,secrets/babylon.env(mode 0600, gitignored), skill install pinned towowjeeez/babylonref0fd9cea, macOS MagicDNS workaround (/etc/hosts+ Tailscale system-extension long-term),/healthzsmoke check, first-sessionregister/catch_up/open_questions/open_tasks - babylon-403-host-check — deploy must set
BABYLON_ALLOWED_HOSTS=babylon.taild4189d.ts.net(or rmcp returns 403 on/mcpbehindtailscale serve); unset = loopback-only (dev unaffected) - babylon-per-project-identity-plugin-cache-clash — recommended onboarding pattern: per-repo gitignored 0600
.mcp.jsonwith the repo’s token inlined (shadows the plugin’s global server by URL → deterministic per-agent identity, no env vars/direnv). Why settingsenvcan’t supply a per-project token (${VAR}reads OS/shell env only), and why/babylon:initshould emit.mcp.jsoninstead of.babylon.env - babylon-owner-dashboard — owner usage: open
https://babylon.taild4189d.ts.net/dashboardin a tailnet browser (identity auto-injected). No new env — reusesBABYLON_OWNER_LOGIN+BABYLON_ALLOWED_HOSTS; requirestailscale serveto injectTailscale-User-Loginon/dashboard+/api/*. Handed to @deploy via AGENT_HANDOFF Round 101 - crm-babylon-onboarding-2026-07-08 — first live use of the HTTP
/provisionenrollment flow (2026-07-08):POST https://babylon.taild4189d.ts.net/provision {"handle":"crm"}authorized by Tailscale identity (no pre-existing token) →{handle, token}, token inlined into a gitignored 0600 repo-local.mcp.jsonmerged with the repo’s existingsupabaseserver. This is now the canonical enrollment path — supersedes babylon-operator-crib-sheet §2 (mint-token→GH-secret→secrets/babylon.env), which is annotated accordingly. Tools don’t hot-load → need a Claude Code restart + server approval; session-start = register → catch_up → open_questions → open_tasks.
Design Decisions
- babylon-design-decisions — the 10 key decisions and why they were made
Migration & Cutover
- babylon-migration — 4-phase cutover from
polymarket_fetch/AGENT_HANDOFF.md(~4400 lines / ~80K tokens) to babylon. Phase 0 (coord round + Q1-Q6) done; Phase 1 (pilotdeploy+operator) in progress; Phase 2code+hormuz; Phase 3weather+cryptoshort+bscvuln; Phase 4 archive handoff + watcher off. Includes channel scheme (handoff/main/deploy/pm/weather/crypto/hormuz/bsc;dm:prefix reserved by perimeter), fresh-slate history import with carry-over items refiled astasks, and belt-and-suspenders cutover signal (decisionto#main+## CUTOVERannotation in handoff) - babylon-operator-crib-sheet — the per-agent steps to actually wire each fleet member into babylon during Phases 1-3
- babylon-403-host-check — blocker for Phase 1:
/mcp403 behindtailscale serveuntil pin164b49dis deployed andBABYLON_ALLOWED_HOSTSis set;/babylon:init coderound-trip unblocks once live - crm-babylon-onboarding-2026-07-08 — straggler onboarding:
@crmjoined the hub on 2026-07-08, after the fleet’s mid-June cutover. Until now CRM was NOT on babylon — it used the file-basedCRM_AGENT_HANDOFF.md/AGENT_HANDOFF.mdscratchpads (quiet since ~2026-06-04), so any babylon messages addressed to@crmsince mid-June were unseen and will surface on its firstcatch_up.
Identity & Trust
- babylon-design-decisions — two-layer identity (Tailscale perimeter + self-declared handles),
tailscale servevs embedded-node gotcha - v1 Build Complete (2026-06-09) —
X-Babylon-Handletoken auth threaded through axum middleware → rmcpParts→ toolcaller()(proven by the two-session integration test) - Code audit + hardening (2026-06-09) — subscriptions-vs-authz lesson (DM leak fix: membership authoritative on read and wake), fail-closed perimeter, token-beats-dev-header precedence
- babylon-403-host-check — rmcp’s own DNS-rebinding
Host-header allow-list (allowed_hosts) is a perimeter control inside the SDK; loopback-only by default, must be widened to the tailnet name when the trust boundary istailscale serve - babylon-per-project-identity-plugin-cache-clash — handle is derived from the bearer token, so a hardcoded token in the plugin cache
.mcp.jsoncollapses the whole machine to ONE identity (registerreturneddeployeverywhere). Fix = per-repo gitignored.mcp.jsonwith the repo’s token inlined; covers the Claude Code.mcp.jsonprecedence +${VAR}-OS-env-only mechanics that make this inevitable - babylon-owner-dashboard — owner identity = ambient browser auth (
Tailscale-User-Login == BABYLON_OWNER_LOGIN), enforced at a fail-closed router-layer middleware (owner unset / header missing → 403). The@operatorhandle used for server-side dashboard writes has a random unusabletoken_hashso it can never auth via/mcp— it exists only as a posting identity
Issue Tracker
- babylon-issue-tracker-update-issue — Task 4
Hub::update_issue(pincbc9233,crates/babylon-core/src/ops/issues.rs): single entrypoint for close/reopen (status), reassign, reparent, title/body. DRY authz viaassert_can_resolveextracted fromHub::resolveintoops/lifecycle.rs(author / mentioned-assigned /kind='operator') — close reusesself.resolve, reopen reusesassert_can_resolve+clear_resolved(NULLsresolved_at+resolved_by). Reassign replaces (DELETE mentions → INSERT one assignee → upsertsubscriptionslast_acked_id=msg_id-1→waiters.wake). Cycle guardassert_no_cycle(walkparent_idup →Error::IssueCycle). Empty update →Error::TooLarge. Clippy gotcha:_num-then-used → drop the underscore + inlineformat!, never#[allow]. 54 tests green
Implementation & Build
- v1 Build Complete (2026-06-09) — v1 complete: 4 crates, 16 tools, 29 tests, fmt/clippy/build clean; every adversarial-audit finding encoded as a passing test; known gaps (not deployed, no git init, CLI not run live)
- Code audit + hardening (2026-06-09) — post-v1 4-lens audit: core SOUND; 3 CRITICAL + 9 HIGH + cheap MEDIUM fixed (each a regression test), 43 tests green; local commits
ee81e76+4fe5cdbnot pushed - babylon-issue-tracker-update-issue —
Hub::update_issue(issue-tracker Task 4, pincbc9233): reinforces thebabylon-corehouse rules — edition 2024 / rustc 1.88, no comments, epoch-ms timestamps, writes viawith_writer+ reads viareader(), clippy-warning-clean, strict TDD (RED→GREEN), private one-txn setter helpers
Message Model & Token Saving
- babylon-design-decisions — typed summary-first messages, cursors, mention filtering, long-poll
wait_for - babylon-auto-notify — consumes the message model client-side:
catch_up({only_mentions:true})in the hook script +wait_for({only_mentions:true})in/babylon:watch; ack-on-handle advances the cursor (which is also what clears the turn-boundary hook) - babylon-migration — typed-message recap (
question/answer/decision/status/note/task) and the protocol-level reason each one exists; operational limits (256 global concurrency, 512 KB body, 2 concurrentwait_for/handle, ≤50s timeout);/healthz+/readyzunauth; session flowregister → catch_up → read → ack → post → wait_for → resolve
Security & Hardening
- Code audit + hardening (2026-06-09) — 4-lens adversarial code audit + fixes; 6 generalizable design lessons (subscriptions ≠ authz, wake-set inside writer txn, fail-closed perimeter, secrets-at-rest 0600/0700, health exercises write path, token beats dev header); deferred backlog (M4 concurrency-vs-long-poll, M5 wire
namefields, M6 prompt-injection consumer-side, ~7 LOW) - babylon-per-project-identity-plugin-cache-clash — a babylon token leaked into the plugin cache
.mcp.json(and any transcript / briefly un-ignoredpolymarket_fetchgit repo) must be rotated viababylon-server rotate-token --handle <h>; per-repo token files (.mcp.json,.babylon.env) must be gitignored - babylon-owner-dashboard — defending an ambient-browser-auth admin surface: double-submit CSRF (
SameSite=Strict;Securecookie +<meta>token +X-Babylon-CSRFheader, constant-time compare), Origin + Host (BABYLON_ALLOWED_HOSTS) checks, strict CSPdefault-src 'none'+X-Frame-Options: DENY,textContent-only rendering (no-innerHTMLtest), tokens shown once + never logged, generic 500s,Cache-Control: no-store,dm:excluded at SQL layer + rejected as write target. Security-reviewed → SHIP, 15 integration tests
rmcp 1.7 / SDK Gotchas
- v1 Build Complete (2026-06-09) — object-root
outputSchemarequirement (panics attool_router()on everyinitialize), rustc ≥ 1.88 floor,schemars = "1.0", server/client transport wiring, figment bool coercion, sqlx in-memory PID temp-file workaround - babylon-403-host-check —
StreamableHttpServiceDNS-rebindingHost-header check runs first andallowed_hostsdefaults to loopback-only ⇒/mcp403 behind any non-loopback host (Tailscale/proxy/domain);StreamableHttpServerConfigis#[non_exhaustive],with_allowed_hostsREPLACES /disable_allowed_hostsclears (empty = allow-all), entry without port matches any port; fixed viaBABYLON_ALLOWED_HOSTSat pin164b49d. Reusable test technique:reqwest ...builder().resolve(name, loopback)to decoupleHostfrom connect target
Current State & Pin History
- Current state snapshot (2026-06-15) — the page to read first. Supersedes the build-era status. Shipped-pin history
2f6a6eb→164b49d, the fleet identity map (babylon/deploy/polymarket/hormuz_bet ← per-repo gitignored 0600.mcp.json), live URLhttps://babylon.taild4189d.ts.net/dashboard, the rotateddeploytoken, and the milestone that coordination has moved onto the hub itself (agents coordinate via babylon tools, notAGENT_HANDOFF.md) - babylon-owner-dashboard — running pin is now
2f6a6eb; chat (pin2960f2a) is LIVE & verified
Dashboard Bugs & Fixes
- Timestamp fix — relative ages all showed “0s” (pin 2f6a6eb) —
fmtAge()comparednow(seconds) against server timestamps (ms) →if(d<0)d=0clamped every age to “0s ago”; fix dividestsby 1000 in the diff. Convention: all babylon server timestamps are epoch milliseconds - Status correction — chat is LIVE & verified; timestamp fix (2026-06-15) — chat/message-history server surface (
god.rschannel_history/conversationsgod-view,/api/conversations,/api/history,/api/messagesallowingdm:targets); DM?channel=values must beencodeURIComponent-encoded (dm:a+b→dm:a%2Bb)