Skip to main content

Crate kranz_server

Crate kranz_server 

Source
Expand description

kranz-server — axum REST + WebSocket layer over a repo’s mission data (docs/protocol.md is authoritative for every route and frame shape).

The read/steer routes NEVER write events.jsonl (single-writer rule §4.3): their only write path is the control inbox (POST /api/missions/:id/control → kranz_engine::control::enqueue). Every read handler re-reads from disk on each request — the engine owns truth and requests are localhost-cheap at human timescales, so there is no in-memory cache to invalidate.

Missions created via POST /api/missions are HOSTED (M2.5): for those, this process holds the MissionEngine — and therefore the single-writer lock — in MissionHost, which is the engine writing events.jsonl. See [host].

Structs§

ApiError
EmbeddedFile
A single dashboard file embedded into a caller’s binary.
HostConfig
The host object in the operator’s global ~/.kranz/config.json.
InvalidMutationAuthority
Error returned when a mutation token cannot be represented safely in an HTTP header.
MissionHost
Registry of missions this server process hosts (see module docs).
MultiRepoHost
Static process-lifetime catalog plus one existing single-repo host per healthy root.
MutationAuthority
Validated mutation authority required by every published router and serve constructor. Keeping the unauthenticated state unrepresentable prevents an embedder from accidentally exposing money-spending POST /api/... routes.
RepoActivity
At-a-glance pipeline counts for the repository picker. Counts follow the dashboard’s work-item projection: ticket-backed missions count once under the ticket state; only ticketless missions are folded independently.
RepoConfig
One operator-owned repository catalog row.
RepoContext
A resolved catalog entry. The root is fixed at startup and requests only ever resolve this entry by its validated id.
RepoSlackConfig
Per-repository Slack routing and authorization owned by global config.
RepoSummary
Public GET /api/repos row.
ServerState
Shared handler state: the repo root for the read-only routes (all mission data is re-read from disk per request) plus the hosted-engine registry.
SlackChannelRoute
Exact Slack workspace/channel route.

Enums§

ApiErrorCode
Stable recovery hints for clients. Human-readable messages remain separate.
DashboardStatic
Static dashboard source for the catch-all frontend routes.
PendingApproval
What MissionHost::try_approve_pending_matching did.

Constants§

TOKEN_HEADER
Header carrying the per-serve mutation token (docs/protocol.md “Authority: mutation token”).

Functions§

bind_listener
Bind bind:port and return the listener. Callers that need the REAL bound address before serving — --port 0 picks an ephemeral port, and the CLI prints/opens the URL — bind first and hand the listener to serve_on_listener.
generate_token
A fresh mutation token: uuid v4 as simple hex. Exposed so embedding shells (the CLI, the Tauri app) mint tokens without their own uuid dep.
load_host_config
Load only the operator-owned host block from a global config file. Missing files are equivalent to an empty catalog; malformed files fail startup instead of silently selecting another repository.
router
Build a read-only convenience router with a fresh, undisclosed mutation authority. GETs work normally; every mutation is refused because callers cannot present that authority. Embedders that need mutations must call router_with_token with an explicit MutationAuthority.
router_with_host
The real router constructor: an explicit MissionHost (tests inject a mock agent backend via MissionHost::with_backend) plus mandatory mutation authority.
router_with_multi_repo_host_and_addr
Build one process router around a static catalog of per-repository hosts. Each configured id is mounted at /api/repos/{id}; the historical unscoped routes are mounted only when the catalog has an explicit default or exactly one healthy repository.
router_with_read_authority_and_addr
router_with_multi_repo_host_and_addr plus a distinct READ-ONLY token (docs/protocol.md “Authority: mutation token”). read_authority authenticates GET/HEAD (and the WS upgrade) wherever the read gate is armed, but is never accepted on a mutating route — it is the token safe to hand to dashboards and agents. If absent, empty, or equal to mutation authority, a distinct read token is generated. Clients obtain it from /api/read-token using either valid token in the header; mutation tokens remain header-only.
router_with_shared_host
router_with_host over an already-shared registry — the kranz serve --slack path, where the Slack bridge holds a clone of the same host.
router_with_shared_host_and_addr
The full router constructor: the REAL bound address (when known) scopes CORS / WS origin approval to that exact ip:port plus the dev-server ports, bind_is_loopback selects the strict loopback Host/origin allowlist vs the LAN one, and require_read_token independently arms the read/WS token gate.
router_with_shared_host_and_bind
Router constructor that threads the serve bind port into CORS / WS origin checks and optionally requires the mutation token on GET + WS upgrade (require_read_token, independent of bind_is_loopback — --read-auth can arm it on a loopback bind without relaxing the loopback Host/origin allowlist).
router_with_static
Build the read-only convenience router with either filesystem or embedded dashboard assets. Use router_with_token for authenticated mutations.
router_with_token
Build the full router with mutation authority gating every POST /api/.... CORS allows any localhost/127.0.0.1 port (and Tauri), and GETs stay tokenless.
serve
Bind 127.0.0.1:<port> and serve the router until the process exits. authority gates every POST /api/....
serve_multi_on_listener
Serve a static multi-repository catalog on an already-bound listener. read_auth forces the read-token gate (GETs and the WS upgrade) even on a loopback bind — off-loopback binds always require it regardless. read_authority, when set, is the read-only token accepted on those gated reads (never on mutations); the mutation authority keeps working for reads too.
serve_on_listener
Serve the router on an already-bound listener. The router is built from the listener’s REAL local address, so --port 0 scopes the origin allowlist to the actual ephemeral port and a non-loopback bind gets the read-token gate.
serve_with_shared_host
serve_with_static over an already-shared registry (see router_with_shared_host). bind widens reachability beyond loopback (e.g. for the glasses app on the same LAN / tailnet). Every POST stays mutation-token-gated; when bind is not loopback, GETs and WS upgrades require the token too. The CLI prints a loud warning for non-loopback binds.
serve_with_shutdown
Same as serve_with_shared_host, but takes an explicit shutdown signal instead of always waiting on Ctrl-C — the testable seam that lets callers (and tests) make the serve future return deterministically.
serve_with_static
Bind 127.0.0.1:<port> and serve the router until the process exits.