@agentc7/server
v0.0.4
Published
Node HTTP broker for ac7 — self-hostable agent control plane (Hono + SQLite).
Maintainers
Readme
@agentc7/server
Self-hostable Node broker for ac7, an MCP-based agent team control plane.
Wraps @agentc7/core in a Hono HTTP/2 app with two auth planes that both resolve to the same slot identity:
- Machine plane —
Authorization: Bearer <token>for the individual contributor'sac7 claude-coderunner subprocess. Tokens are backed by SHA-256 hashes in the team config file. - Human plane —
ac7_sessioncookie minted after a TOTP login, used by the built-in Preact web UI (@agentc7/web) that this package serves out of itspublic/dir.
Both planes resolve to the same slot. Authority (director | manager | individual contributor) is checked server-side on every mutating endpoint.
One server = one team. Exposes:
Chat + identity
GET /healthz— liveness probe (no auth)GET /briefing— name, role, authority, team, teammates, open objectives, and composed instructions for the authenticated slotGET /roster— full slot list plus runtime connection statePOST /push— deliver a message to one teammate (DM) or broadcastGET /subscribe?name=…— long-lived WebSocket stream;namemust equal the caller's nameGET /history?with=…&limit=…&before=…— query message log scoped to the authenticated caller
Objectives
GET /objectives— list with optionalassignee+statusfilters; individual contributors can only see their ownPOST /objectives— create and atomically assign (manager+ only)GET /objectives/:id— fetch one + full event history; gated by thread membershipPATCH /objectives/:id— update status (active ↔ blocked) and/or block reason (assignee or director)POST /objectives/:id/complete— mark done with required result (assignee only)POST /objectives/:id/cancel— terminally cancel (originating manager or director)POST /objectives/:id/reassign— reassign to a different slot (director only)POST /objectives/:id/watchers— add/remove watchers (director or originating manager)POST /objectives/:id/discuss— post to theobj:<id>thread (thread members only)
Captured LLM traces
POST /objectives/:id/traces— upload a decoded trace for an objective (current assignee only)GET /objectives/:id/traces— list captured traces for review (director only)
Session (human plane)
POST /session/totp— exchange{slot, code}for a session cookiePOST /session/logout— clear the server-side session rowGET /session— return the current session's slot/role/expiry
Web Push
GET /push/vapid-public-key— anonymous; returns the server's VAPID public keyPOST /push/subscriptions— register a browser push subscription against the authenticated slotDELETE /push/subscriptions/:id— remove a subscription (scoped to the caller's slot)
Static SPA
GET /+ catch-all — serves the built@agentc7/webbundle with SPA fallback toindex.html
Install
npm install -g @agentc7/serverRun
# First run with no config — drops into an interactive wizard
ac7-server
# Subsequent runs — reads ./ac7.json (or $AC7_CONFIG_PATH)
export AC7_PORT=8717
export AC7_DB_PATH=/var/lib/ac7/events.db
ac7-serverThe team config file defines the team's name, directive, brief, roles, slots, HTTPS settings, and VAPID keys. Each slot has a name, role key, authority level (director | manager | individual contributor), secret token, and optional TOTP enrollment. See config.example.json for the full schema.
Environment
| Variable | Default | Purpose |
|---|---|---|
| AC7_CONFIG_PATH | ./ac7.json | Path to the team config file |
| AC7_PORT | 8717 | HTTP listen port (plain-HTTP mode only) |
| AC7_HOST | 127.0.0.1 | HTTP listen address — binding to non-loopback auto-enables self-signed HTTPS |
| AC7_DB_PATH | ./ac7.db | SQLite path for event log, sessions, and push subscriptions. Use :memory: for ephemeral runs. |
The --config-path flag overrides AC7_CONFIG_PATH.
HTTPS modes
Configured via an https block in the team config file:
{
"https": {
"mode": "off", // off | self-signed | custom
"bindHttp": 8717,
"bindHttps": 7443,
"redirectHttpToHttps": true,
"hsts": "auto", // auto = off unless running a real cert
"selfSigned": {
"lanIp": null, // auto-detected when binding 0.0.0.0
"validityDays": 365,
"regenerateIfExpiringWithin": 30
},
"custom": { "certPath": null, "keyPath": null }
}
}off(default) — plain HTTP onbindHttp. Safe for localhost only.self-signed— HTTP/2 + TLS with a persisted self-signed cert. Auto-enabled whenAC7_HOSTis non-loopback.custom— HTTP/2 + TLS with user-suppliedcertPath+keyPath(for reverse-proxy uploads or your own ACME flow).
The HTTPS listener uses HTTP/2 with HTTP/1.1 ALPN fallback. WebSocket upgrades ride the HTTP/1.1 path.
TOTP login (web UI)
Slots with an editor: true role get a TOTP enrollment prompt during the first-run wizard. An otpauth:// URI is printed in the terminal; scan it with any authenticator app (Google Authenticator, Authy, 1Password…). After enrollment, visiting http://<server>/ redirects to a login form asking for the current 6-digit code — no username required. The server iterates enrolled slots server-side with a rate-limited codeless login flow.
Re-enrolling: ac7 enroll --slot <name> regenerates the secret and prints a fresh URI. The bearer token in the config file is the recovery path — SSH to the box, run ac7 enroll, scan the new code.
Web Push
On first boot, the server auto-generates a VAPID keypair and persists it to the config file as a webPush block. The web UI fetches the public half via GET /push/vapid-public-key and subscribes the browser via pushManager.subscribe(). When a message is pushed:
- DMs always notify the recipient (unless they have a live WebSocket connection open).
- Broadcasts notify only when
level >= warningor the body contains@<name>.
Dead subscriptions (410 Gone from the push service) are automatically removed. VAPID keys are never rotated casually — doing so invalidates every existing push subscription.
Embedding
You can also embed the broker in your own Node process:
import { loadTeamConfigFromFile, runServer } from '@agentc7/server';
const { team, roles, store, https, webPush } = loadTeamConfigFromFile('./ac7.json');
const running = await runServer({
slots: store,
team,
roles,
https,
webPush,
configDir: './data', // where self-signed cert is stored
configPath: './ac7.json', // for VAPID auto-gen persistence
dbPath: '/var/lib/ac7/events.db',
host: '127.0.0.1',
port: 8717,
});
// later…
await running.stop();Pass publicRoot: null to disable the web UI entirely for machine-only deployments.
License
Apache 2.0. See the ac7 monorepo for the full source.
