# AgentPod Fleet/facilities console for agent runtimes. Three tiers: - `apps/node-agent` — Go daemon on each host; detects harnesses (hermes, openclaw, claude-code, codex, opencode, pi, muse, kilo — `cmd/agentpod-node/registry.go` is the list), dials the hub over WSS, serves station capabilities (health/logs/fs/terminal/lifecycle/cleanup/acp/changeset), and updates itself from GitHub releases **when asked to** — there is no timer on the node. The trigger is `apn update` on the host, or the hub: one node from the console, or the whole fleet via `fleet nodes update` (which calls `POST /api/nodes/update-all`). Nodes do not drift forward on their own, so after a release the fleet stays where it is until someone rolls it (issue #295). - `apps/hub` — Bun + Hono + Drizzle/Postgres backend; node gateway, broker, station registry; sign-in and tokens are the organization plane's (`ORG_PLANE_*`). See `apps/hub/CLAUDE.md`. - `apps/console` — SvelteKit with `adapter-static` (Svelte 5 runes, vite, bits-ui, Tailwind); a client-routed SPA deployed to Cloudflare Pages. It IS SvelteKit — `pnpm check` runs `svelte-kit sync`. - `packages/contract` — zod schemas shared by hub ↔ agent ↔ console. Change here first when a frame/API shape changes. ## Commands ```bash cd packages/contract && bun test cd apps/hub && DATABASE_URL="postgres://agentpod:agentpod-dev-password@localhost:5434/agentpod" bun test cd apps/node-agent && go test -race ./... cd apps/console && pnpm check && pnpm test && pnpm build ``` Hub tests REQUIRE both: a **pgvector** postgres on `:5434` (`docker run -d --name agentpod-test-postgres -e POSTGRES_USER=agentpod -e POSTGRES_PASSWORD=agentpod-dev-password -e POSTGRES_DB=agentpod -p 5434:5432 pgvector/pgvector:pg16`) and the explicit `DATABASE_URL` override — bun auto-loads `apps/hub/.env`, which may pin a dev DB. Details: `TESTING.md`. On an enrolled node, `apn status|stop|start|restart|logs [-f]` and `apn service install|uninstall` manage the node-agent service (systemd on Linux, LaunchAgent on macOS), and `apn telemetry status|enable|disable` (or `fleet nodes telemetry`) sets its OTLP endpoint — see `docs/OPERATING.md`. ## Workflow - TDD: failing test first, including a regression test for every bug fix. - Branches: trunk-based on `main` — short-lived branch → PR → `main`. **`develop` is dead** (419 commits behind, last touched 2026-08-08); do not branch from it. Required checks: `contract`, `hub`, `node-agent`, `console`, `worker`; `strict`, so a branch must be up to date before it merges — and nothing, including a workflow, can push straight to `main`. Full flow: `CONTRIBUTING.md`. - Releases: tag `v*` on `main` → `release-node-agent.yml` publishes binaries + `install.sh` + `SHA256SUMS` (self-update verifies against SHA256SUMS — an incomplete release bricks fleet updates). Nothing else to do: the Fly/worker images default to `releases/latest/download`, so there is no version pin to move. `publish-images.yml` still resolves a concrete release and passes `--build-arg AGENTPOD_VERSION`, so a published image stays reproducible. - Console production builds need `PUBLIC_HUB_URL=https://hub.agentpod.dev` baked in at build time (a plain build points the deployed console at localhost). Production gets the console only on a `v*` tag, after the hub (`release-hub.yml`); `main` deploys it to staging. ## Gotchas - Go tests: never leave unreaped children named like pgrep targets (zombies match by comm); macOS SIGKILLs copied system binaries — re-exec the test binary via a `TestMain` env hook for named process stubs. - Console tests: global teardown in `apps/console/src/vitest-setup.ts` flushes bits-ui's scroll-lock timer — don't remove it, and don't add per-file workarounds. - Features touching the live fleet get verified against the real deployment before their issue closes; deploy/ops runbooks are `docs/DEPLOYMENT.md` and `docs/OPERATING.md`.