Git servers (forge_hosts) become full credential owners:
- an encrypted forge token (Fernet, HANDLER_SECRET_KEY) stored per server and
never returned by the API (has_token flag only); used automatically by every
project on that host and addressable as db:host:<hostname> — the reserved
db: credential scheme is now live
- a per-server ed25519 SSH deploy key: generated server-side, public half
shown in the dashboard to paste into the forge, private half encrypted at
rest and materialized 0600 only in the control container (GIT_SSH_COMMAND /
core.sshCommand)
Project registration gets a git-server mode: pick a registered server, type
owner/name, and the API derives the remote (ssh when the server has a deploy
key, https otherwise), computes root_dir under PROJECTS_ROOT, and enqueues a
new 'sync' command the worker executes (clone, or ff-only pull). Spawn always
pulls first, so runs start from the remote's latest state; POST /projects/:p/sync
and 'handler sync' re-pull on demand.
Schedules: recurring agent spawns (prefix, prompt, interval, role). The worker
fires due schedules as ordinary queued spawn commands with timestamped agent
names, so runs are fresh stateless agents and appear in the Activity audit
trail; missed intervals collapse into one catch-up run.
Dashboard: Git Servers pane shows the SSH public key (copy button) and takes a
write-only token; Repositories gains the server-first add form and a Pull now
button; new Schedules pane. Rebuilt static export. Also restores the missing
frontend/lib (api client + format helpers) the components import.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XY1tEhQZXHZ5wci7dLc7rM
Make credentials/hosts, projects, agents, and approvals manageable from the
dashboard. The API and control layer are separate containers, so the API can't
run control actions directly (no git/tmux/claude, doesn't own the tmux
sessions). Instead the API enqueues a command and a worker in the control
container executes it and writes the result back.
Data model (migration 0003):
- `commands` queue/audit table; `forge_hosts` registry; `approvals` gains a
nullable approver id + `actor` so operator verdicts are first-class.
Control worker:
- `control/worker.py` claims commands and dispatches to the existing control
functions (spawn/kill/resume/record_approval/write_skills/poller.sweep),
plus a periodic CI sweep. New `handler worker` CLI subcommand; it becomes the
control image's default command (subsumes `poll-ci --watch`).
API:
- `require_admin` gate + `ADMIN_TOKEN`; project GET/PATCH/DELETE; agent
spawn/kill/delete; resume now enqueues (fixes a cross-container bug where the
API tried to send tmux keys to a session in the control container); new
approvals/commands/hosts routes; forge-init and poll-ci enqueue endpoints.
Credentials/hosts:
- host->token-env lookup consults the `forge_hosts` registry first (built-in
map is the fallback); `resolve()` refactored to a scheme dispatch reserving
`db:` for a future encrypted store. Web input restricts credential_ref to
env:/file:/db: (cmd: stays CLI-only — it would run arbitrary commands).
Dashboard:
- New tabs for projects, agents (spawn/kill with live command-status polling),
approvals, hosts, and an activity/audit view; shared context is now writable.
Tests: +33 (queue atomicity, worker dispatch, CRUD, hosts, admin gating,
cmd: rejection, host-aware credentials, and an API->queue->worker->spawn
end-to-end). README gains a Web management section.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CrhrBToauu4L2qG6jdnuFP