W spec-memo Wiki
Home Wiki Index GitHub

Status Monitor

From spec-memo Wiki, the living architecture & domain knowledge base

Feature Overview [edit]

When memo serve --sse runs, a read-only operations companion starts in the same process on http://127.0.0.1:3124/ (config ports.status, override --status-port, disable --no-status). Stdio memo serve starts the companion only when --status/--status-port is explicitly passed. The page is a bookmarkable, self-contained dark-theme HTML app (no runtime CDN dependencies, stable path /, <title> contains spec-memo) with tabbed workspaces:

  • Activity & Status: server health cards, topology badge, vault/project list and filter, live MCP tool/HTTP activity log streamed over SSE, and client registry.
  • Memory, Prompts & Intent Stories, Activity & Invoicing, Derived Rules, Wiki, Vaults: master-detail explorers over vault data.
  • Backups: dedicated snapshot management tab (create/list/inspect/restore/delete/download).

The monitor never mutates vault records except the deliberate operator endpoints: backup create/restore/delete and reset (POST /api/vaults/backups, POST /api/vaults/restore, DELETE /api/vaults/backups/{filename}, POST /api/vaults/reset, POST /api/vaults/export, POST /api/vaults/import, POST /api/wiki/regenerate) and the vault-manager routes. Everything else is read-only.

Business Rules & Logic [edit]

  • Bind default is loopback 127.0.0.1; binding non-loopback without an auth token throws at startup. Auth precedence: options.authToken, SPEC_MEMO_AUTH_TOKEN, SPEC_MEMO_STATUS_TOKEN, SPEC_MEMO_SSE_TOKEN.
  • Authorized requests may present Authorization: Bearer <token>, ?token=/?authToken=, or the spec_memo_status_token cookie. GET /login shows a sign-in form; POST /api/auth/login sets an HttpOnly SameSite=Strict cookie and redirects; /api/auth/logout clears it. ?token= on / is promoted into a cookie and stripped from the URL. When a token is configured, unauthenticated API calls return 401 { error: "Unauthorized" }, and / redirects to /login?next=... (same-origin path only).
  • Activity event contract: { id, seq, ts, type: http|tool|system, kind: read|write|meta, ok, durationMs, summary, tool?, method?, path?, statusCode?, projectId?, clientIp?, clientName?, clientType?, operation? }. The ring buffer holds at most 200 events (FIFO drop; seq keeps increasing). All fields are sanitized (secret redaction + absolute-path redaction) before buffering.
  • Project filter semantics: All vaults shows every event; a specific vault shows events where projectId matches or projectId is absent, so transport/system lines stay visible while drilling into one project. The filter is reflected in the URL ?project= and sessionStorage, and applies client-side plus as a server project= query.
  • GET /api/events returns { events, nextSeq }; GET /api/events/stream sends a named snapshot event then activity events, honoring project= and afterSeq (invalid afterSeq -> 0).
  • Backups tab rules: Create Backup with All vaults selected opens a confirmation stating all vaults are included and calls the persist API with projectId omitted and confirmFullBackup: true; omitting the confirm returns 400 { error: "confirmFullBackup required for full backup" } and writes nothing. A selected project creates a single-project snapshot. Unknown projectId returns 400 { error: "Unknown projectId" }.
  • Inventory rows show filename, created, human-readable size, entry count, scope (full/project), and encryption. Filters: filename substring, scope, project, encryption, created-at range, size bounds, and multi-select kinds present (AND across selected kinds). Invalid encrypted values return 400.
  • Row actions: Restore (overwrite warning + optional password), Download (streams application/zip or application/json with Content-Disposition: attachment), Delete (operator types the exact filename in the UI; API requires { confirm: true }, else 400 and the file stays). Clicking a row opens the details drawer (GET /api/vaults/backups/{filename}/inspect). Inspect rejects passwords in the query string (400); an encrypted archive without a password returns 200 { ok: true, encrypted: true, inspectable: false }; a wrong password returns 401 with no vault mutation; a missing file returns 404; a filename escaping backups/ returns 400.
  • Each completed create/restore/delete/reset emits exactly one type: "system", kind: "write" activity event with ok and a summary containing the filename or restore counts; no secrets or absolute paths.
  • Mutating routes require the configured token and take the vault lock via withVaultLock, so export/import/reset/restore/delete do not overlap. Import caps multipart bodies at 64 MiB (413 when exceeded).
  • Unknown /api/* paths return 404 { error: "Not found" } without stack traces.

Technical Architecture [edit]

  • src/status.ts startStatusServer(options) returns { server, port, host, url, close }; the HTML is generated by generateStatusHtml and the login page by generateLoginHtml. close() invokes closeAllConnections() then server.close().
  • GET /api/status returns status, service, version, host, port, mode, role, topology (TopologyInfo), mcp ({ host, port, activeTransports, available } or { available: false, disconnected: true }), projectsCount, uptimeMs (from bus.startedAt), eventsBuffered, clients, activeClientsCount. Topology role: local-vault, intermediary-proxy (hybrid or remote/proxy), or final-remote (SSE daemon).
  • GET /api/vaults returns the enriched array from getVaultProjectList (id, displayName, aliasOf, recordCount). GET /api/clients returns bus.listClients(). Vault-manager routes: POST/DELETE /api/vaults/alias, POST /api/vaults/merge, /rename, /create, PATCH /api/vaults/{id} or POST /api/vaults/update, POST /api/vaults/delete, POST /api/vaults/sync (direction pull|push|both, syncDual -> 200/502).
  • Activity bus (src/activity.ts): createActivityBus({ capacity }) with capture, list(filter?), subscribe(listener, filter?), client registry (registerClient, updateClientActivity, disconnectClient, listClients, capped at 100 clients), close. HTTP request lifecycle is captured in src/server.ts (captureHttpEvent) and the status server records telemetry for every request.
  • Backup routes are backed by src/backup.ts (persistVaultBackup, listBackups, filterBackupList, inspectBackup, deleteBackup, resolveBackupPath, exportVault, importVault, resetVault) and zip helpers in src/status-backup.ts (packVaultZip, unpackVaultZip, parseMultipartFormData). exportVault walks RECORD_SUBDIRS (traps, decisions, specs, plans, logs, reviews, scratch, prompts, sessions); state records live under plans/. Archives pack vault-backup.json as plaintext spec-memo-vault-v1 or (with password) spec-memo-encrypted-vault-v1.
  • Provenance: 0023-mcp-status-monitor.spec.md (bus, activity log, vault filter; spec default :3001), 0026-status-vault-backup-ui.spec.md (sidebar export/import; later moved), 0030-vault-reset-and-proxy-monitor.spec.md (reset, backups list, restore, 3-mode topology), 0032-status-backup-page.spec.md (dedicated Backups tab, full-backup confirm, inventory filters, restore/delete/download/inspect; supersedes the old All-vaults disabled export). See Backup & Restore, Deployment Modes, and SSE Transport.