Vogt exposes one operation registry (src/vogt/registry) through three
transports — HTTP, the CLI, and MCP — sitting behind a session engine / front
door. Every capability is generated from that one registry, so the three
transports never drift.
The endpoints themselves are self-describing (FastAPI emits OpenAPI), but which
process, which port, and which token was never written down in one place. This
is that map. Read it before reaching for a token — a request to the wrong
surface, or with the wrong bearer, is a 401/404 that looks like a bug and
is not.
| Process | What it is | Port | Serves |
|---|---|---|---|
Core (vogt serve) |
The tracker: projects, work items, tokens, the store. A FastAPI app. | 8000 (container) |
/api/* operations, /mcp, and the generated docs /openapi.json, /docs, /redoc |
| Engine / front door | The session + PWA host that fronts the core in a deployment. A Rust server. | e.g. 8910 on the estate |
the PWA, /api/status, /api/auth/check, /api/sessions, /healthz, /readyz, the assistant — and it proxies /api/vogt/*, /mcp, /api/auth/login and /api/install/* through to the core |
In a deployed stack the engine is the front door: the core is not exposed
directly, and callers reach it through the engine's /api/vogt and /mcp
proxies (engine/server/src/app.rs, MACHINE_NAMESPACES = ["/api", "/mcp"]).
Everything else on the engine is the engine's own.
-
HTTP — on the core, at
{API_PREFIX=/api}/<operation route>. Because the core is FastAPI, you get for free (FR-A4):/openapi.json— the machine-readable spec./docs— interactive Swagger UI./redoc— ReDoc.
These live at the core root (
http://<core>:8000/docs), not under/api, and the front door proxies/api/vogt, not/docs— so Swagger is normally reachable only from inside the deployment. -
CLI —
vogt <group> <op>operates on the local store directly, with no bearer. Inside the core container that store is the core's store, and loopback access is effectively admin — which is howvogt token issuemints a token when no admin bearer exists. (A pod wired to a remote core still defaults to the local store and will saynot_initialized; setVOGT_CORE_URL+VOGT_HTTP_TOKENand call the HTTP API instead — see the CLI hint added in #729.) -
MCP — the same operations as tools over
/mcp(JSON-RPC, streamable HTTP), proxied by the engine to the core. Thevogt-mcp-remotestdio bridge fronts it for agent clients (src/vogt/adapters/mcp/).
The core is the only identity authority. Every credential a client holds is a
core token bound to an actor, and the engine accepts all of them: it keeps
no token table of its own and resolves a bearer by asking the core
(GET /api/auth/whoami, cached for seconds), so one credential opens both
doors. The one exception is the engine's optional break-glass ENGINE_TOKEN,
a static credential the core has never heard of: it 401s the core, and on
/api/vogt the engine substitutes the stack secret for it.
| Token | tokens.kind |
Authenticates | Typical scopes | Where it comes from |
|---|---|---|---|---|
| Session (a person's login) | session |
the core — /api/*, /mcp — and the engine |
the scopes on the login: read,work.write,project.write by default, admin for the first operator |
POST /api/auth/login with a username and password; expires after session_ttl_days (30) or at auth.logout |
| API token (an agent's credential) | api |
the core and the engine | as issued | vogt token issue (admin) |
Stack secret (deploy/vogt-core-token) |
api |
the core, as the front-door actor; the engine, as vogt-core (sessions, history, agent-clis-write) |
VOGT_BOOTSTRAP_CORE_TOKEN_SCOPES |
the file both halves read, adopted at init (#199); server-side only, never in a browser |
Coding-session token (session.start) |
agent |
the core, per session | agent_session_scopes — default everything except admin |
minted per session (#726) |
| Brokered agent token | — | the core, as a shell/agent session's VOGT_HTTP_TOKEN |
deployment-chosen | ENGINE_AGENT_AUTH_VOGT_SECRET_NAME, brokered from the secrets manager at session launch |
Break-glass token (ENGINE_TOKEN, optional) |
none | the engine only — full capability, no actor | — | deploy/.env; its Vogt calls are made with the stack secret |
Concretely: GET /api/auth/check on :8910 accepts a session or an API
token and answers with identity: {name, scopes, capabilities} — the core
actor's identity_ref, or primary / vogt-core for the two static
credentials — and /api/vogt/* forwards the same bearer to the core, so
POST /api/vogt/projects is audited to the person or agent who sent it. A
401 from the engine means nobody recognises the bearer; a 503 means the
core could not be asked, which is an outage rather than a wrong credential,
and the engine says so instead of collapsing the two.
Every core operation passes two gates (FR-S5): authenticate (who is this)
then authorize (may they do it), and the decision is recorded either way.
Scopes are instance-wide — an agent with work.write can write to every
project (DESIGN.md §4.1).
| Scope | Gates |
|---|---|
read |
every read |
work.write |
work-item writes — create, transition, comment |
project.write |
project register/import and project-level writes |
writeback |
exactly forge.writeback (arming forge write-back) |
admin |
token minting, actor creation, password logins (user.*), and instance ops — init, migrate, backup, restore, serve |
- The default session scope set is
read,work.write,project.write,writeback(agent_session_scopes, #726) — everything exceptadmin, applied to every session however it was launched. Narrow it in config only if this instance truly wants to. - Loopback is admin. The CLI acting on the core's own store bypasses the
bearer gate, so
vogt token issueinside the core container is the way to mint or widen a token when no admin bearer is available. - A
403names the requirement and the holding."project.register requires the 'project.write' scope; this token holds read, work.write, writeback"— the fastest way to read a token's actual grants live.auth.whoami(GET /api/auth/whoami,vogt auth whoami) is the other: it answers with the effective scope set, implications applied. - The engine derives its capabilities from these scopes. A bearer the core
resolves gets, on the engine, what its scopes imply
(
auth::capabilities_for_scopesinengine/server/src/auth.rs):adminholds all eleven capabilities;work.writeorproject.writeholds everything exceptgui-controlandagent-clis-write;readalone holds onlypush-write, so a viewer can subscribe to notifications;writebackadds nothing there. There is no separate engine grant to ask for.
# Core (inside the deployment)
GET http://<core>:8000/api/status # core token
GET http://<core>:8000/docs # Swagger UI (internal)
GET http://<core>:8000/openapi.json # raw spec
# Engine / front door
GET http://<engine>:8910/api/status # any core token (session or API), or ENGINE_TOKEN
GET http://<engine>:8910/readyz # unauthenticated health
POST http://<engine>:8910/api/auth/login # {username, password} -> {actor, token, secret}; unauthenticated
GET http://<engine>:8910/api/vogt/auth/whoami # who this bearer is, with effective scopes
POST http://<engine>:8910/api/vogt/auth/logout # revoke the bearer this call carries
POST http://<engine>:8910/api/vogt/... # proxied to the core, caller's bearer forwarded
POST http://<engine>:8910/mcp # proxied to the core (MCP)
# People (admin, via loopback in the core container); the password is read
# from stdin, a file, or a hidden prompt — never from argv
docker exec -i <core-container> \
vogt user create --username ada --display-name "Ada Lovelace" \
--scopes read,work.write,project.write --password-stdin --reason "<why>" < ada-password
docker exec -i <core-container> vogt user passwd --username ada --password-stdin --reason "<why>" < new-password
docker exec <core-container> vogt user list
docker exec <core-container> vogt user remove --username ada --reason "<why>"
# Agents: mint / widen an API token (admin, via loopback in the core container)
docker exec <core-container> \
vogt token issue --actor <ref> --name <n> --scopes read,work.write,project.write,writeback --reason "<why>"
DESIGN.md§4 — the security model (FR-S*), scopes, and per-project scope deferral.ENGINE.md— the engine, the agent-auth broker, and identity passthrough.CONFIG.md— generated config reference (agent_session_scopes,session_ttl_days,bootstrap_*_token_*,ENGINE_AGENT_AUTH_*).