English | 简体中文
A management center for a fleet of Scoot agents.
Scoot is a local-first, single-binary AI agent that runs on your machines and keeps every
action in a JSONL audit trail. Today each Scoot install is an island. scootship is the
management center that the Scoot project's docs/EDGE.md
points to: a single Go binary that ingests append-only fleet telemetry over the scoot-edge
protocol and serves an embedded admin dashboard so you can observe the whole fleet from one place.
Status: Phase 2 center-side dispatch core (pre-1.0). The center ingests
statusheartbeats andaudit_batchlog shipping and renders the fleet. Task dispatch/orchestration (EDGE.md E2) now has a persisted center-side queue, node-bound lease output, lifecycle ingestion, and idempotency/provenance tests. Dashboard operators holding thedispatch:managecapability can create new node-targeted dispatch jobs; the/dispatchlist itself remains a read-only audit view, and job control (cancel/retry) is not yet built. Seedocs/roadmap.mdfor the project shape, boundaries, and direction.
scoot-edge is, by design, an optional companion that dials out to a center; the edge
never opens a listener. That makes the center the only inbound trusted surface in the
fleet, so scootship is built defensively:
- Every node endpoint is authenticated by a per-node bearer token, and the dashboard by a login session (form login + HttpOnly cookie, not the browser's basic-auth popup).
- The dashboard login is rate-limited per source IP: repeated failures lock that IP out for
a cooldown (with a
Retry-After), so the center's sole inbound surface resists brute force. - Every response carries defensive headers (a strict
Content-Security-Policywith no inline scripts, plusX-Frame-Options,X-Content-Type-Options, andReferrer-Policy). - Telemetry is append-only and is never reflected back into a node's local state.
- The center can never raise a node's local policy ceiling — it isn't a permission console.
One honest caveat from EDGE.md carries over: once a node opts in to audit shipping, the
bodies it ships (file contents, command output) live at the center. Treat the center, and its
dashboard login, as sensitive. The dashboard/API keep a bounded recent audit window per node and
mark an explicit audit_gap when that window trims; accepted audit batches are still persisted
append-only to the center JSONL log.
Operators (browser · login session)
|
v
+-------------------------------------------+
| scootship (single Go binary) |
| embedded dashboard (embed.FS) |
| POST /telemetry GET /jobs/lease (E2) |
| per-node token auth · append-only store |
+----------------------^---------------------+
| edge dials OUT (HTTPS + per-node token)
+-----------------+-----------------+
| | |
scoot-edge scoot-edge scoot-edge (optional companion)
| | |
scoot scoot scoot
Requires Go 1.26+. No external dependencies, no Node toolchain, no database to install — the dashboard and storage are built in.
# Terminal 1 — run the center in dev mode (dashboard open, demo token n-dev=dev-token seeded)
SCOOTSHIP_DEV=1 go run ./cmd/scootship serve
# Terminal 2 — drive the heartbeat -> ingest -> dashboard path with the built-in simulator:
go run ./cmd/scootship mock-edge -ship-auditOpen http://localhost:8080. You'll be redirected to a sign-in page — in dev mode log in with
admin / admin. After signing in you get the dashboard shell with a collapsible left
sidebar (Fleet, Dispatch, Tokens, Operators + a Settings menu with Account), plus top-right sign-out:
the n-dev node goes online, with its policy ceiling, derived audit counts, capability
labels, and (because -ship-audit is on) a few ingested audit events on the node detail page.
Or use the Makefile:
make run # center in dev mode
make mock-edge # simulated node
make docs # build bilingual mdBook docs into ./book (requires mdBook)
make docs-serve # preview the English mdBook root locally
make ci # fmt-check + vet + test + build- CI runs on pushes, pull requests, and manual dispatch via
.github/workflows/ci.yml:gofmt,go vet,go test,go build, a CLI version smoke, and release-target cross-compile smoke checks. - The docs workflow in
.github/workflows/docs.ymlbuilds the bilingual mdBook on docs-related pushes, pull requests, and manual dispatch; pushes tomainalso deploy the generatedbook/site to GitHub Pages. - Pushing a
vX.Y.Ztag triggers.github/workflows/release.yml, which injects the tag intointernal/version.Version, cross-compiles single-binary archives for Linux, macOS, and Windows, publishes checksums with the GitHub release, and publishes multi-arch Linux Docker images to GHCR asghcr.io/scootship/scootship:X.Y.Zpluslatesttags. - Project-local agent skills live in
.agents/skills:auto-releasefor controlled release orchestration andproject-auditfor scored whole-repo health reports.
serve is configured from the environment (secrets never come from committed config):
| Variable | Default | Meaning |
|---|---|---|
SCOOTSHIP_ADDR |
:8080 |
Listen address. |
SCOOTSHIP_TLS_CERT / SCOOTSHIP_TLS_KEY |
(unset) | PEM paths for direct HTTPS. EDGE.md mandates production-safe transport. Without direct TLS, startup fails unless SCOOTSHIP_DEV=1 or SCOOTSHIP_BEHIND_TLS_PROXY=1 is explicit. |
SCOOTSHIP_BEHIND_TLS_PROXY |
(unset) | =1 allows the center to listen with plain HTTP only when a trusted reverse proxy terminates TLS in front of it. Ensure the listener is not directly exposed. |
SCOOTSHIP_DATA_DIR |
./data |
Append-only store directory. |
SCOOTSHIP_ADMIN_USER |
admin |
Username used to bootstrap the first dashboard operator when the operator store is empty. |
SCOOTSHIP_ADMIN_PASSWORD |
(unset) | Password used only to bootstrap the first dashboard operator. Required for first startup unless SCOOTSHIP_DEV=1 (which bootstraps admin/admin). After bootstrap, operators are managed from the dashboard and stored in SCOOTSHIP_DATA_DIR/operators.json. |
SCOOTSHIP_NODE_TOKENS_FILE |
(unset) | JSON file: {"n-7a3":"secret", ...}. Must be a regular private file with no executable, group, or world permissions (0600 is the normal setting). |
SCOOTSHIP_NODE_TOKENS |
(unset) | Inline n-7a3=secret,n-8b4=secret2. |
SCOOTSHIP_DEV |
(unset) | =1 seeds the demo node token and a default admin/admin dashboard login (insecure; local use). |
SCOOTSHIP_STALE_SECONDS |
90 |
A node is shown "stale" after this much silence. |
SCOOTSHIP_MAX_TELEMETRY_BYTES |
8388608 |
Maximum size of one /telemetry request body. |
SCOOTSHIP_AUDIT_RETENTION_EVENTS |
1000 |
Recent audit events retained per node for dashboard/API reads. Overflow creates an explicit audit_gap; accepted events remain in the append-only JSONL log. |
SCOOTSHIP_DISPATCH_QUEUE_LIMIT |
200 |
Maximum non-terminal (queued/leased/running) dispatch jobs one node may have outstanding at once. New operator-created dispatch jobs beyond this cap are rejected. |
SCOOTSHIP_LOGIN_MAX_FAILS |
5 |
Failed dashboard logins from one source IP before it is locked out. |
SCOOTSHIP_LOGIN_WINDOW_SECONDS |
900 |
Sliding window over which failures are counted. |
SCOOTSHIP_LOGIN_LOCKOUT_SECONDS |
900 |
How long a tripped IP stays locked out. |
SCOOTSHIP_TRUSTED_PROXIES |
(unset) | Comma-separated CIDRs/IPs of reverse proxies whose X-Forwarded-For may be trusted to attribute the real client IP. Unset means trust only the raw connection (spoofed XFF is ignored). |
mock-edge is a dev/test client configured by flags: -center, -node, -token,
-interval, -ship-audit. It sends the token as Authorization: Bearer <token> and the
envelope node_id must match the node assigned to that token.
The dashboard can also create, rotate, and revoke center-managed node tokens. The center generates
high-entropy bearer token secrets, displays them once after creation or rotation, never lists or
returns them from inventory APIs, and persists them in
SCOOTSHIP_DATA_DIR/managed_node_tokens.json with private 0600 permissions. Revocations are
stored there too, so an operator can revoke a token originally loaded from the env or private token
file without editing that original source. Configure a real scoot-edge with the same node id and
secret; in the current edge client flow that means setting SCOOT_EDGE_TOKEN=<secret> and using
--node-id <node> against the center's /telemetry endpoint.
For production setup, data-directory permissions, TLS proxy boundaries, backup, and restore, use the
operator runbook in docs/deployment.md.
scootship implements the center side of the frozen scoot-edge v1 contract. The wire
shapes live in internal/protocol and mirror EDGE.md exactly:
- Envelope
{"v":1,"type":"status|audit_batch|job|job_event","node_id":"...","sent_ts":...,"body":{}}. - E1 (implemented):
POST /telemetryacceptsstatusandaudit_batch(and forward-compatiblyjob_event). Audit ingest is idempotent on the{file_gen, byte_to}cursor and acks the durably stored cursor so the edge only advances after a durable ack. Telemetry batches are decoded and validated before store mutation; invalid audit cursors, empty audit batches, and unknown audit event kinds are rejected. The recent audit window is bounded bySCOOTSHIP_AUDIT_RETENTION_EVENTS; trimming is visible as a center-sideaudit_gap. Node detail API/pages also group retained audit bysession_id/run_idinto chronological run timelines. - E1 health signals (implemented): fleet and node pages derive read-only signals for stale nodes, version drift, audit body lag, retention gaps, duplicate audit reports, policy denies, system errors, and unrestricted local ceilings. They do not trigger remediation or mutate node state.
- Node token lifecycle (implemented): dashboard operators can create, rotate, and revoke center-managed per-node authentication tokens; secrets are generated by the center, displayed once, and then never listed, returned by APIs, logged, or audited.
- Dashboard action authorization (implemented): authenticated operators carry direct built-in
capabilities (
fleet:view,tokens:manage,operators:manage) instead of role groups. All authenticated state-changing forms require a session-bound CSRF token. - E2 center-side core (implemented):
GET /jobs/leaseauthenticates the node, requires a matchingnodequery param, boundscapacity, and returns only jobs already persisted for that node. Dispatch records are append-only JSONL snapshots withidem_keyde-duplication, node binding, capability/label miss rejection (no_matching_capability), only-lower policy clamping against the node's reported ceiling, and lifecycle updates from validatedjob_eventtelemetry. Dashboard operators holding thedispatch:managecapability can create a new node-targeted job from/dispatch/new(session + CSRF protected, bounded by a configurable per-node pending-job queue cap); the/dispatchlist and JSON API remain a read-only audit view, and there is still no cancel/retry/edit control surface for an existing job.
scootship talks only this contract; it does not depend on any Scoot internal.
| Path | Responsibility |
|---|---|
cmd/scootship |
CLI entrypoint: serve, mock-edge, version. |
internal/protocol |
The frozen scoot-edge v1 wire contract (envelope, bodies, cursor). |
internal/store |
Append-only JSONL fleet store with idempotent audit ingest, replay, visible audit-retention gaps, retained-window run timelines, and center-side dispatch queue/provenance records. |
internal/tokens |
Per-node bearer-token registry, private managed lifecycle state, and dashboard-safe token inventory metadata (the center's node auth surface). |
internal/operators |
Dashboard operator accounts, direct capabilities, profile/password management, and password hashing. |
internal/loginguard |
Per-source-IP brute-force throttle for dashboard logins (failure window + lockout). |
internal/config |
Environment-driven configuration. |
internal/center |
HTTP server, bearer + login-session auth, capability gates, CSRF checks, telemetry ingest, node-bound lease dispatch, read-only health/dispatch audit signals, dashboard + JSON API. |
internal/web |
Embedded dashboard templates and static assets (embed.FS). |
internal/mockedge |
Simulated scoot-edge node for dev/test (a faithful client of the public contract, never a second implementation of Scoot). |
internal/version |
Build version string; release builds override it from the tag. |
.github/workflows |
CI, mdBook docs, and tag-driven release automation. |
.agents/skills |
Project-local release and audit skills. |
docs/roadmap.md |
Project shape, non-goals, and direction. |
Read AGENT.md (engineering handbook) and docs/roadmap.md
(intent and hard boundaries) before making changes. For production operation, read
docs/deployment.md. Run make ci before pushing.
MIT — matching the Scoot ecosystem.