> For the complete documentation index, see [llms.txt](https://faction-os.gitbook.io/faction-os-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://faction-os.gitbook.io/faction-os-docs/apps/server/readme_server.md).

# @factionos/server

Node 26.2.0+ ESM + Express 5 + `ws`. Implements the current FactionOS REST and WebSocket surface described in [`docs/api/README_api.md`](/faction-os-docs/docs/api/readme_api.md), with the detailed source-backed contract in [`docs/api/event-api-hook-contracts.md`](/faction-os-docs/docs/api/event-api-hook-contracts.md).

## Quick start

```bash
cp ../../.env.local.example ../../.env.local
npm install
npm run dev
```

Defaults bind to `127.0.0.1:2468` (the FactionOS default port is kept so existing hook scripts targeting that port keep working). Health check at <http://127.0.0.1:2468/health>.

The server auto-loads env files in this order: root `.env.local`, root `.env`, `apps/server/.env.local`, then `apps/server/.env`. Already-exported shell variables keep priority over file values, earlier files win over later files, and blank placeholders are ignored. Prefer root `.env.local` for shared local values such as `FACTIONOS_AUTH_TOKEN`; use `apps/server/.env` only for server-specific fallback defaults.

By default, the server accepts real hook events only. Keep `FACTIONOS_MOCK=false` in the root `.env.local` file for full live testing. Synthetic demo events run only when `FACTIONOS_MOCK=true` is set explicitly.

The server refuses non-loopback bind hosts by default. Set `FACTIONOS_BIND_HOST` for a specific loopback host, or set `FACTIONOS_ALLOW_NON_LOOPBACK_BIND=true` only when intentionally exposing the server to another interface. HTTP CORS and WebSocket Origin checks default to local Vite dev origins on ports 5193 and 5194; override them with `CORS_ORIGINS` and `FACTIONOS_WS_ALLOWED_ORIGINS`.

Set `FACTIONOS_AUTH_TOKEN` to require a local bearer token on HTTP routes and WebSocket connections. Hooks, CLI commands, and adapters read the same variable. For browser development against an auth-protected local server, set `VITE_FACTIONOS_AUTH_TOKEN` in `apps/web/.env.local` to the same value. The token gate applies before read routes, write routes, and unsupported catch-all route classification.

Rate limiting is enabled by default with a 240-request, 60-second fixed window per client. Tune it with `FACTIONOS_RATE_LIMIT_MAX`, `FACTIONOS_RATE_LIMIT_WINDOW_MS`, and `FACTIONOS_RATE_LIMIT_ENABLED=false`. Set `FACTIONOS_TRUST_PROXY=true` only when the server runs behind a trusted reverse proxy that sets forwarding headers.

## REST endpoints

### Core

| Method       | Path                                 | Body                                                                                                                                       | Returns                                                                                                                                                                                                          |
| ------------ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`        | `/health`                            | none                                                                                                                                       | `{ ok, version, uptimeMs, mock }`                                                                                                                                                                                |
| `POST`       | `/event`                             | hook payload                                                                                                                               | `{ accepted: true, missionId?, heroId? }`                                                                                                                                                                        |
| `GET`        | `/heroes`                            | none                                                                                                                                       | `{ heroes: Hero[] }`                                                                                                                                                                                             |
| `GET`        | `/heroes/:id`                        | none                                                                                                                                       | `Hero`                                                                                                                                                                                                           |
| `GET`        | `/missions`                          | `?limit=50`, valid `1..1000`                                                                                                               | `{ missions: Mission[] }`                                                                                                                                                                                        |
| `GET`        | `/missions/:id`                      | none                                                                                                                                       | `Mission`                                                                                                                                                                                                        |
| `POST`       | `/permission-response`               | `{ requestId, approved?, reason? }`                                                                                                        | `{ ok: true, resolved }` plus paired command-center permission/attention audit updates                                                                                                                           |
| `POST`       | `/plan/approve`                      | `{ planId, approved?, reason? }`                                                                                                           | `{ ok: true, resolved }` plus paired command-center plan-approval permission/attention audit updates                                                                                                             |
| `GET`        | `/notice-board`                      | `type`, `since`, `forSession`, `limit`, `offset`, `includeResolved`, `roomCode`, optional `debug=1`                                        | `{ messages, total, debug? }`                                                                                                                                                                                    |
| `GET`        | `/notice-board/context`              | `sessionId`, optional `roomCode`, `limit`                                                                                                  | `{ context, messages?, total? }`                                                                                                                                                                                 |
| `POST`       | `/notice-board`                      | `{ type, content, priority?, tags?, authorSessionId?, authorName?, authorType?, targetSessionIds?, relatedFiles?, roomCode?, expiresAt? }` | `{ notice }`                                                                                                                                                                                                     |
| `POST`       | `/notice-board/:id/resolve`          | `{ resolution?, resolvedAt?, authorSessionId? }`, optional `roomCode` query                                                                | `{ success, noticeId, resolvedAt? }`                                                                                                                                                                             |
| `POST`       | `/notice`                            | Compatibility `{ body, severity?, targets? }` plus canonical notice fields                                                                 | `Notice`                                                                                                                                                                                                         |
| `GET`        | `/notices`                           | optional `roomCode`                                                                                                                        | `{ notices: Notice[] }`                                                                                                                                                                                          |
| `GET`        | `/suggestions/summary`               | none                                                                                                                                       | `{ summary }` with manager counts and severity buckets                                                                                                                                                           |
| `GET`        | `/suggestions/scan/status`           | none                                                                                                                                       | `{ status }` with codebase scan progress, timestamps, last error, and last scanned commit                                                                                                                        |
| `POST`       | `/suggestions/scan`                  | `{ scope: "codebase", mode?, forceFull?, root?, questIgnorePatterns? }`, plus optional `?full=true`                                        | Starts a full, diff, or daily Quest Board codebase scan and returns accepted status plus compact counts                                                                                                          |
| `GET`        | `/suggestions/analyze/status`        | none                                                                                                                                       | `{ status }` with on-demand analysis progress, timestamps, and last error                                                                                                                                        |
| `POST`       | `/suggestions/analyze`               | `{ scope?: "analysis", force?, forceRefresh?, root? }`                                                                                     | Runs or returns fresh cached Quest Board analysis results with compact counts and manager-owned state                                                                                                            |
| `GET`        | `/suggestions/project-scan/status`   | none                                                                                                                                       | `{ status }` with project scan progress, timestamps, and last error                                                                                                                                              |
| `POST`       | `/suggestions/project-scan`          | `{ scope?: "project", force?, forceRefresh?, root? }`                                                                                      | Runs or returns fresh cached Quest Board project scan items with compact counts and manager-owned state                                                                                                          |
| `POST`       | `/suggestions/:suggestionId/accept`  | `{ suggestionId?, heroId? }`                                                                                                               | `{ success, suggestionId, action, ... }` using `send_prompt` for existing heroes or `prompt_sent` fallback intent                                                                                                |
| `POST`       | `/suggestions/:suggestionId/dismiss` | `{ suggestionId? }`                                                                                                                        | `{ success, suggestionId, dismissed: true }`                                                                                                                                                                     |
| `POST`       | `/issues/:issueId/dismiss`           | `{ issueId? }`                                                                                                                             | `{ success, issueId, dismissed: true }`                                                                                                                                                                          |
| `GET`        | `/quests`                            | none                                                                                                                                       | `{ quests: Quest[] }`                                                                                                                                                                                            |
| `POST`       | `/scrolls/:id/collect`               | `{ heroId? }`                                                                                                                              | `Scroll`                                                                                                                                                                                                         |
| `GET`        | `/scrolls`                           | none                                                                                                                                       | `{ scrolls: Scroll[] }`                                                                                                                                                                                          |
| `GET`        | `/achievements`                      | none                                                                                                                                       | `{ achievements: Achievement[] }`                                                                                                                                                                                |
| `GET`        | `/warroom`                           | none                                                                                                                                       | Local stub with capability and Worker separation metadata                                                                                                                                                        |
| `GET`/`POST` | `/export/session`                    | \`?format=csv                                                                                                                              | json`or POST`{ format }\`                                                                                                                                                                                        |
| `POST`       | `/erasure/local/preview`             | `{ boundaryIds? }`                                                                                                                         | Dry-run local erasure summary with redacted counts, manual-review states, unsupported boundaries, and no full trusted erasure claim                                                                              |
| `POST`       | `/erasure/local/confirm`             | `{ confirmationPhrase, idempotencyKey, boundaryIds? }`                                                                                     | Confirmed local erasure for eligible local filesystem boundaries with idempotency, partial-failure reporting, and verification                                                                                   |
| `GET`        | `/diagnostics/runtime`               | none                                                                                                                                       | Compact local runtime counts for heroes, missions, tool uses, connected clients, and recent event throughput only                                                                                                |
| `GET`        | `/diagnostics/orchestration`         | none                                                                                                                                       | Compact local orchestration diagnostics snapshot with manager availability and counts only                                                                                                                       |
| `GET`        | `/diagnostics/provider-readiness`    | none                                                                                                                                       | Compact local provider/setup/runtime readiness, command-center diagnostics posture, safe recovery descriptors, and docs paths only                                                                               |
| `POST`       | `/diagnostics/orchestration/recover` | `{ action, idempotencyKey, dryRun? }`                                                                                                      | Performs only stale listener PID or malformed spool JSON cleanup and returns compact action counts                                                                                                               |
| `GET`        | `/diagnostics/isolation`             | none                                                                                                                                       | Compact read-only isolation posture with executor readiness, terminal/container readiness metadata when wired, guarded-action counts, unsupported-route families, redaction categories, and recovery limits only |
| `GET`        | `/diagnostics/hosted-config`         | none                                                                                                                                       | Status-only hosted config posture with categories, exposure labels, counts, and docs paths only                                                                                                                  |
| `GET`        | `/diagnostics/hosted-identity`       | none                                                                                                                                       | Status-only hosted identity posture with planned/unavailable state, requirement labels, unsupported claim labels, non-overclaim labels, counts, and docs paths only                                              |

The Quest Board routes above are mounted at both the root path and the `/api` prefix. They are the only shipped historical suggestion and issue routes in this phase. Codebase issue accept-as-quest, broad task queue executor dispatch outside the bounded terminal/Git/file/container executable subset, suggestion telemetry, and entitlement gates remain excluded or future work and must stay out of the local server route inventory until a later threat model ships them. | `GET` | `/diagnostics/hosted-persistence` | none | Status-only hosted persistence and public replay posture with data eligibility, requirement labels, blocked payload labels, unsupported claim labels, counts, and docs paths only |

Provider diagnostics are local-only. `src/managers/diagnosticsManager.ts` checks local hook/settings posture, env-name presence, setup docs/scripts, listener PID state, spool health, queue/template/graph/guarded-action counts, and command-center provider capability records. It reports labels, booleans, counts, and docs paths only; it does not call cloud providers or return raw env values, provider payloads, logs, command output, prompts, tokens, or local absolute paths. Recovery is limited to stale listener PID files and malformed spool JSON entries.

## Notice Board Runtime

The server owns the canonical Notice Board state. The board is an explicit coordination channel for active agents and humans; it is not a raw hook-event feed. Notice content should be concise status, question, review, announcement, conflict, completion, or human-authored coordination.

`src/managers/noticeBoard.ts` persists state to `notice-board.json` under `FACTIONOS_HOME` or the injected state root. It loads persisted state on startup, debounces saves, flushes on shutdown, caps the board at 500 messages, and prunes expired, resolved-stale, and non-critical stale notices. Critical notices are retained until resolved or explicitly pruned. The manager stores resolved and unresolved notices, invalidates context caches on mutation, and deduplicates automatic posts for the same room, author session, and type inside the short auto-post window.

The board is room-scoped. A connected War Room can provide the active room code; otherwise the server uses the local standalone room. List and context queries filter by room, type, `since`, `forSession`, resolved state, offset, and limit. Targeted notices are visible only to matching session ids. Context queries return bounded unresolved messages for one session, exclude messages authored by that same session, and cache the response for a short interval.

Canonical routes live in `src/routes/noticeBoard.ts`. `POST /notice-board` normalizes canonical fields and compatibility aliases, rejects unsafe related files, stores the notice, emits `notice_board_message`, and forwards the notice through the optional War Room bridge when connected. `POST /notice-board/:id/resolve` resolves within the active room, emits `notice_resolved`, and forwards the resolution through the optional bridge. Duplicate in-flight posts and resolutions return compact conflicts.

Compatibility routes share the same manager. `POST /notice` accepts legacy `body`, `severity`, and `targets` fields plus canonical notice fields, then stores the normalized notice in the active room. `GET /notices` returns the active-room compatibility list as `{ notices }`.

Automatic mission lifecycle posts are implemented in `src/lib/missionLifecycleNotices.ts`. Accepted mission starts create deduped `status` notices with a short expiration. Accepted mission completions create deduped `completion` notices with a longer expiration and safe relative `relatedFiles` when available. Automatic posts use privacy-safe labels and fallbacks; raw hook telemetry, prompts, command bodies, file contents, terminal output, secrets, and absolute paths do not become Notice Board content.

## Quest Board Suggestion Manager

`src/managers/suggestionManager.ts` owns the local Quest Board suggestion state for idle suggestions, codebase issues, session summaries, analysis results, project scans, scan status, and dismissed suggestion or issue IDs. It persists the manager snapshot to `suggestions.json` under `FACTIONOS_HOME` or the injected state root, loads it on startup, debounces saves, writes through an atomic temp-file rename, recovers corrupt or malformed stores to empty local state, and flushes pending writes during server shutdown.

The file is local-only state. It may include relative file paths and code-derived suggestion text, so manual deletion is the current removal path: delete `suggestions.json` from the configured FactionOS state directory (`FACTIONOS_HOME`, or `~/.factionos` by default) while the server is stopped. The manager does not send suggestion state to hosted storage, analytics, entitlement checks, or provider services.

Session 03 ships the first manager-backed local runtime routes under both root and `/api` prefixes. `GET /suggestions/summary` returns manager counts and severity buckets. `POST /suggestions/:suggestionId/accept` removes an active idle suggestion, emits the canonical suggestion snapshot, and either returns a `send_prompt` action for a resolvable internal hero or a `prompt_sent` fallback intent when no internal hero exists. `POST /suggestions/:suggestionId/dismiss` and `POST /issues/:issueId/dismiss` persist dismissed IDs through the manager and emit the same update path.

New WebSocket clients receive one `suggestion_update` hydrate frame after the Notice Board hydrate and before scroll/achievement hydration. Route mutations emit `suggestion_update` plus string `idle_suggestion` compatibility frames derived from active idle suggestions for the current web store.

Automatic idle suggestions are generated server-side when an ingested `hero_idle` event actually transitions a hero to the idle state. The lifecycle coordinator builds bounded context from the completed mission prompt and summary, recent compact activity, and safe relative modified-file hints, then stores the selected typed suggestion through `SuggestionManager`. The same mutation path emits the canonical `suggestion_update` snapshot and current `idle_suggestion` compatibility frame, so automatic and REST-driven Quest Board updates do not drift.

Idle generation is local-first. Without provider transfer, malformed provider output, empty output, or timeout, the engine creates deterministic typed fallback suggestions from safe mission context and file hints. Provider calls still require both `ANTHROPIC_API_KEY` and `FACTIONOS_ALLOW_LLM_PROVIDER_TRANSFER=true`, and prompts are redacted before leaving the process. Raw prompts, command bodies, terminal transcripts, provider payloads, secrets, and absolute paths are not stored as suggestion activity or emitted in warning paths.

Session summaries are also generated server-side when an event-ingested mission completes. `src/lib/sessionSummaryLifecycle.ts` builds the same bounded context shape, suppresses duplicate in-flight work for the same session, aborts generation after about 15 seconds, stores typed `SessionSummary` entries through `SuggestionManager.setSessionSummary`, and emits the canonical `suggestion_update` snapshot. The summary engine returns at most two follow-up tasks categorized as `test`, `documentation`, `refactor`, or `validation`. Provider transfer follows the same two-level opt-in rule as idle generation, and provider failures, malformed output, empty output, or timeout fall back to schema-valid local summaries when safe context is available.

Analysis results are filtered before they are stored and again when persisted suggestion state is loaded. `src/lib/analysisNoiseFilter.ts` removes model self-talk about truncated, unreadable, missing, or partial context before those items reach active Quest Board state. Valid concrete analysis items are preserved, and filtered stores are rewritten through the normal local persistence path.

Session summaries and analysis filtering are server-only in this phase.

`src/lib/codebaseIssueScanners.ts` is the local default-off scanner layer for Quest Board codebase issues. Callers must explicitly enable `todos`, `gitStatus`, `lint`, `build`, or `tests`; with all options disabled, it performs no filesystem walk, command execution, or manager mutation. Enabled scans first pass through `resolveApprovedScanRoot`, then emit protocol-valid `CodebaseIssue` entries with safe relative paths, bounded titles/messages, bounded suggested prompts, and compact cap/failure metadata. TODO-family comments are parsed from approved source files only. Git, lint, build, and test signals use injectable command execution with timeout and max-output bounds so tests and future orchestration can run without invoking local tools.

Scanner results are local-only. The scanner never sends data to providers, hosted storage, analytics, or entitlement services, and it does not persist raw command logs, absolute paths, file contents, prompts, terminal output, or transcripts. Manager ingestion is available only through `SuggestionManager.addCodebaseIssue`, preserving manager-owned validation, dedupe, caps, persistence, dismissal, and WebSocket snapshot behavior.

`src/lib/codebaseScanOrchestrator.ts` is the shipped Quest Board codebase scan orchestration boundary. It runs full, diff, and daily scans over the default off scanner layer, updates `SuggestionManager` scan status before and after the run, replaces manager-owned codebase issues in one transaction, and emits canonical `suggestion_update` snapshots after scan status or issue mutations. Duplicate scan triggers are single-flight guarded and return a compact conflict instead of racing local command execution.

`POST /suggestions/scan` and `GET /suggestions/scan/status` are mounted under both root and `/api`. `POST /suggestions/scan?full=true` forces a full scan; otherwise the request may use `mode: "full"`, `mode: "diff"`, or `mode: "daily"` with `scope: "codebase"`. Diff scans use the last scanned git commit stored in scan status, combine working-tree, staged, and committed file changes, rescan changed files, and reuse cached TODO-family issues for unchanged files. If there is no previous commit or usable TODO cache, diff mode falls back to a full scan. Server startup schedules a non-blocking daily scan when the 24-hour codebase freshness window has lapsed; readiness is not blocked by stale-scan work, and shutdown clears pending timers plus recovers a running scan status to failed.

Quest ignore patterns come from two local sources: request `questIgnorePatterns` and the repo-level `.factionos-questignore` file under the approved scan root. Patterns are line based, relative, ASCII, bounded, and must not contain absolute paths, backslashes, NULs, empty segments, or parent traversal. The merged matcher applies before TODO-family source scanning and after command-output parsing for git status, lint, build/typecheck, and tests, so ignored relative paths do not become stored Quest Board issues.

Quest scans are local-only. They do not send prompts, file contents, command bodies, raw command logs, absolute paths, request roots, scan payloads, or Quest ignore patterns to providers, hosted storage, analytics, entitlement checks, or Worker surfaces. Local command helpers use timeouts and output bounds, and scanner failures are stored as compact status or failure metadata.

On-demand analysis is available at `POST /suggestions/analyze` and `GET /suggestions/analyze/status` under both root and `/api`. The engine selects up to 10 safe relative code files from recent git history, falls back to working-tree changes, then falls back to deterministic local code hints when history is unavailable. Dependency directories, build outputs, and lockfiles are excluded. Results are parsed into protocol `AnalysisResult` items, filtered for model self-talk, stored through `SuggestionManager.setAnalysisResult`, and treated as fresh for 30 minutes. A fresh cached result returns without provider work unless `force: true` is provided. Duplicate triggers while a run is active return a compact conflict.

Project scan is available at `POST /suggestions/project-scan` and `GET /suggestions/project-scan/status` under both root and `/api`. The engine builds a sanitized workspace summary from local package and directory signals, generates 2-4 protocol `ProjectScanItem` entries, and stores them through `SuggestionManager.setProjectScan`. Project scans are fresh for 24 hours. Startup schedules a non-blocking stale project scan when the cached project scan is absent or stale; readiness is not blocked, and shutdown clears pending startup work.

Analysis and project scan provider calls use `llmClient`, inherit the two-level provider opt-in requirement, and have bounded timeouts. Without both `ANTHROPIC_API_KEY` and `FACTIONOS_ALLOW_LLM_PROVIDER_TRANSFER=true`, malformed provider output, empty output, or timeout, the engines write deterministic schema-valid local fallback results. Route errors and status fields use compact messages and do not echo raw prompts, provider payloads, absolute paths, request roots, tokens, terminal output, or file contents.

Unsupported-route reconciliation keeps shipped Quest Board paths out of the planned route families. Historical file, git, terminal, Worker, hosted, container, remote, channel, task-queue, and collaboration paths still return deterministic `501` envelopes, but `/suggestions/*` and `/issues/:issueId/dismiss` are owned by `suggestionRouter`. Phase 19 channel intake routes under `/channels`, `/channel-commands`, and `/webhooks/*` are also implemented local routes and are excluded from broad historical channel/webhook unsupported classification.

## War Room Local Stub

`GET /warroom` is compatibility/status metadata only. The Cloudflare Worker in `apps/warroom` is the room relay for create, join, approve, and participant socket behavior. Local server routes such as `/warroom/create` or `/rooms/*` remain separate-surface unsupported-route responses and must not become Worker proxies without a future threat model.

The local stub must not store room state, forward Worker payloads, reuse `FACTIONOS_AUTH_TOKEN` as Worker authority, or imply hosted collaboration is available. Phase 05 owns shared Worker/client schemas, web lifecycle state, redacted federation events, and deployment diagnostics.

Phase 06 Session 01 keeps that boundary in `.spec_system/archive/phases/phase_06/collaboration_isolation_safety_baseline.md`. Later authority or collaboration diagnostics must not turn the local server into a room relay, Worker proxy, hosted queue, hosted auth surface, remote executor, or trusted erasure service.

### LLM engines

These wire the five maintained system prompts in [`src/llm/prompts/`](https://github.com/AI-with-Apex-VIP/factionos/tree/main/apps/server/src/llm/prompts/README.md) to HTTP. Every engine ships a canned fallback so the server still responds without provider credentials.

| Method | Path                   | Body                                           | Returns                                 | Engine                            |
| ------ | ---------------------- | ---------------------------------------------- | --------------------------------------- | --------------------------------- |
| `POST` | `/llm/plan`            | `{ missionId?, prompt, conventions? }`         | `Plan`                                  | `planDecomposer` (Sonnet)         |
| `POST` | `/llm/analyze`         | `{ files: [{path, content}], antiPatterns? }`  | `{ issues: CodebaseIssue[] }`           | `onDemandAnalysisEngine` (Sonnet) |
| `POST` | `/llm/scan-codebase`   | `{ root, limit?, extensions?, antiPatterns? }` | `{ rootResolved, scanned[], issues[] }` | wraps `onDemandAnalysisEngine`    |
| `POST` | `/llm/idle-suggestion` | `{ heroId, lastUserTask, recentActivity }`     | `{ suggestions: IdleSuggestion[] }`     | `idleSuggestionEngine` (Haiku)    |
| `GET`  | `/memory`              | none                                           | `{ findings: MemoryEntry[] }`           | `transcriptMiner` (read-only)     |
| `GET`  | `/memory/:type`        | none                                           | `{ findings: MemoryEntry[] }`           | `transcriptMiner` (read-only)     |

`POST /llm/scan-codebase` scans recently modified files for the on-demand analysis engine. The server walks `root`, applies the ignore list defined in [`src/lib/codebaseWalker.ts`](https://github.com/AI-with-Apex-VIP/factionos/tree/main/apps/server/src/lib/codebaseWalker.ts) (`node_modules`, `.git`, `dist`, `target`, lockfiles, sourcemaps, minified bundles, and historical artifact directories), keeps files matching the configured extensions, sorts by mtime DESC, and reads the top `limit` (default 12) into memory truncated to 12 000 chars each before passing them to `analyzeFiles`. The endpoint refuses to scan the filesystem root, rejects roots outside the approved scan-root allowlist, and does not follow symlinks. Emits two `mission_event` frames on the WebSocket (`scan_started` then `analysis_complete`) so the web UI can show progress.

Provider transfer is opt-in at two levels. `ANTHROPIC_API_KEY` only marks the Anthropic provider as configured; the server still uses local deterministic fallbacks unless `FACTIONOS_ALLOW_LLM_PROVIDER_TRANSFER=true` is also set. When provider transfer is enabled, prompt, path, command, token, URL, and transcript text passes through the local redaction boundary before SDK calls. Provider calls have a timeout and fall back locally on missing SDKs, failures, or timeouts. LLM and memory responses include `X-FactionOS-LLM-Provider`, `X-FactionOS-LLM-Provider-Transfer`, and `X-FactionOS-LLM-Privacy` headers.

Codebase scans are approved-root bounded. The current server working directory is approved by default; add more roots with `FACTIONOS_SCAN_ROOTS` using the platform path delimiter. Scan responses and progress events redact the absolute root and return only relative file paths, sizes, timestamps, truncation flags, and issues. Empty root, invalid root, non-directory root, filesystem-root, and unapproved-root failures use compact `invalid_request` envelopes and do not echo the raw requested root. Memory route output also passes through the same redaction boundary before returning stored findings.

Example:

```bash
curl -X POST http://localhost:2468/llm/scan-codebase \
  -H "content-type: application/json" \
  -d '{"root": "/abs/path/to/repo", "limit": 8, "extensions": [".ts", ".tsx"]}'
```

High-risk current POST routes, WebSocket client messages, export options, and LLM inputs return compact `invalid_request` validation errors for malformed bodies and fields. The current shipped API surface is documented in [`docs/api/README_api.md`](/faction-os-docs/docs/api/readme_api.md), and the route/event/hook reconciliation lives in [`docs/api/event-api-hook-contracts.md`](/faction-os-docs/docs/api/event-api-hook-contracts.md). Historical route inventories under ignored `EXAMPLES/findings/` remain local traceability evidence; unwired local-server routes return deterministic 501 payloads with route family and status instead of implying shipped parity.

`GET /diagnostics/orchestration` is for local CLI diagnostics. It returns server availability, generated-at metadata, task queue counts, template availability totals, mission graph counts, and guarded-action counts with deterministic status labels. It does not return raw queue entries, template prompt material, proposal rationale, commands, terminal output, file contents, tokens, transcript paths, broad absolute paths, raw state files, or raw manager payloads. The route is mounted behind the same CORS, local bearer auth, rate-limit, and body-size middleware as the rest of the local server.

`GET /diagnostics/isolation` is for local isolation posture. It returns read-only executor posture, guarded-action counts, deterministic unsupported-route family labels, blocked redaction category names, narrow recovery limits, and terminal/container readiness metadata when runtime managers are wired. Terminal readiness includes PTY availability, safe shell and cwd labels, active session count, timeout, output cap, cleanup posture, and fallback reason. Container readiness includes Docker and Apple Containers probe labels, image, browser dependency, auth mount, worktree mount, network, provider runtime, cleanup posture, and fallback reason. The route does not start terminals, run containers, inspect or mutate workspace files, valid spool entries, archives, browser state, Worker state, backups, logs, or local state files, and it does not return prompts, command bodies, terminal output, file contents, broad paths, tokens, request bodies, proposal rationale, raw queue entries, raw probe output, or raw spool payloads.

`GET /diagnostics/hosted-config` is for local hosted configuration posture. It uses the shared protocol vocabulary in `packages/protocol/src/hostedConfig.ts` and returns variable names, categories, exposure labels, status labels, counts, and docs paths only. It does not return raw env values, account ids, zone ids, tunnel tokens, provider keys, webhook URLs, bearer values, local paths, replay buffers, exports, archives, logs, backups, request bodies, or prompts. Blank or absent hosted variables keep core local server startup and diagnostics in local-only mode.

`GET /diagnostics/hosted-identity` is for local hosted identity posture. It uses the shared protocol vocabulary in `packages/protocol/src/hostedIdentity.ts` and returns planned or unavailable state, lifecycle requirement labels, authorization surface labels, role/audit requirement labels, unsupported claim labels, non-overclaim labels, counts, and docs paths only. It inherits the same CORS, local bearer auth, rate-limit, and body-size middleware as the rest of the local server. It does not return raw account tokens, OAuth codes, refresh tokens, ID tokens, account ids, raw Worker authority tokens, request bodies, local paths, prompts, commands, replay buffers, exports, archives, logs, backups, or env values.

`GET /diagnostics/hosted-persistence` is for local hosted persistence and public replay posture. It uses the shared protocol vocabulary in `packages/protocol/src/hostedPersistence.ts` and returns planned or unavailable state, data eligibility labels, storage requirement labels, public replay requirement labels, blocked payload labels, unsupported claim labels, counts, booleans, and docs paths only. It inherits the same CORS, local bearer auth, rate-limit, and body-size middleware as the rest of the local server. It does not return raw env values, tokens, account ids, request bodies, local paths, prompts, commands, terminal output, replay buffers, exports, archives, logs, backups, scan payloads, media drafts, War Room payloads, or file contents.

`apps/server/src/lib/erasureInventory.ts` is a Phase 08 Session 02 read-only helper, not a route and not a deletion runtime. It derives protocol-owned erasure inventory rows for FactionOS home, session archives, memory, settings, lifecycle, project-root state, logs, backups, valid and malformed spool posture, exports, diagnostics, scan runtime memory, and LLM runtime memory. It accepts optional counts or presence labels and returns bounded labels, docs paths, booleans, and counts only. It does not inspect filesystem payloads, delete files, clear state, expose raw paths, or claim trusted unified erasure.

`apps/server/src/lib/localErasure.ts` and `apps/server/src/routes/erasure.ts` are the Phase 08 Session 03 local erasure runtime. `POST /erasure/local/preview` is dry-run only. `POST /erasure/local/confirm` requires `ERASE LOCAL FACTIONOS STATE` and an idempotency key before deleting eligible release-scoped local filesystem targets. Eligible boundaries include local archives, memory, settings, lifecycle state, project-root state, logs, valid spool entries, exports, and diagnostics. Backup archives and malformed spool entries are manual-review boundaries; scan and LLM runtime memory are compact summary boundaries without durable file targets. The route returns boundary ids, labels, statuses, counts, verification states, audit labels, docs paths, and no-overclaim wording only. It never returns raw paths, prompts, commands, exports, backups, logs, diagnostics, spool payloads, tokens, request bodies, or idempotency key values. Worker, hosted, public replay, analytics, push, remote, workspace-file, and full trusted unified erasure claims remain unavailable.

Session exports preserve CSV headers, JSON schema version, and JSON top-level fields while replacing prompt, path, command, token-like, URL, terminal, and transcript-sensitive values with deterministic placeholders. `POST /export/session` is the browser command-palette path and accepts `{ format }`; authenticated `GET /export/session?format=csv|json` is also supported for direct local probes and downloads. Export responses include `X-FactionOS-Export-Schema`, `X-FactionOS-Export-Privacy: redacted-local`, and `Cache-Control: no-store`. Unknown query or body formats return compact validation envelopes that list allowed formats without echoing the raw invalid value.

`GET /diagnostics/runtime` exposes local runtime size and throughput only: hero, active hero, dismissed hero, mission, active mission, tool-use, connected client, total-event, recent-event, and recent-events-per-second counts. It inherits the same local auth/CORS/rate-limit boundary and does not include raw prompts, paths, commands, exports, replay buffers, logs, WebSocket frames, or session payloads.

Local session archives under `~/.factionos/sessions` are still retained local audit artifacts. Archive writes pass through the local redaction boundary and log compact one-time warnings if directory creation, serialization, or writes fail.

## WebSocket

`ws://127.0.0.1:2468/` - fanout of every server event. Clients send a `hello` message on connect; server replies with `connected` + a fresh `roster_update` + `notice_board_hydrate` + `scroll_state` + `achievement_state` + `task_queue_update` + `agent_template_update` + `mission_graph_update` + `guarded_action_update` + the compact `command_center_*_update` hydration frames.

Client messages are validated before dispatch. Malformed JSON, non-object payloads, unknown message types, oversized frames, and invalid fields produce a compact `server_toast` warning without closing healthy sockets or mutating manager state. Duplicate permission, plan, and guarded-action decisions from the same socket lifecycle are ignored after the first accepted response.

Notice Board sockets hydrate from the canonical manager. New clients receive `notice_board_hydrate` for the active room. Posts from REST, `post_notice`, or automatic mission lifecycle paths emit `notice_board_message`. Resolve calls emit `notice_resolved`. The `post_notice` client message is compatibility input; it accepts canonical or legacy notice fields, routes through the canonical manager, and preserves the same validation, duplicate prevention, bridge forwarding, and privacy boundary as the REST route.

Event shapes are typed in [`@factionos/protocol/events`](https://github.com/AI-with-Apex-VIP/factionos/tree/main/packages/protocol/src/events.ts).

## Real CLI integration

The `/event` endpoint accepts the FactionOS hook payload shape summarized in [`docs/api/README_api.md`](/faction-os-docs/docs/api/readme_api.md) and detailed in [`docs/api/event-api-hook-contracts.md`](/faction-os-docs/docs/api/event-api-hook-contracts.md). Current source dispatches specific handling from `eventName` or `hook`; historical `type`-only payloads are not routed to state changes unless adapted. The installed hook map lives in [`apps/hooks/hooks.json`](https://github.com/AI-with-Apex-VIP/factionos/tree/main/apps/hooks/hooks.json), and `apps/cli` provides `factionos init` to wire it into Claude Code settings.

## Phase 03 Orchestration Boundary

Phase 03 ships a local, in-memory, non-executing orchestration subset. The server exposes bounded task queue routes, source-owned template routes, typed subagent lineage and mission graph updates, guarded-action proposal and decision routes, compact WebSocket hydration frames, deterministic unsupported route classification, and a read-only diagnostics snapshot. These routes inherit loopback defaults, optional bearer auth, Origin/CORS controls, rate limits, body-size caps, compact validation errors, and redaction boundaries.

Approved guarded actions are registry-gated. The default production registry keeps unimplemented executor families unavailable, approved-not-executing, observe-only, or proposal-only, creates bounded command-center execution runs, and returns explicit repair guidance when no safe local executor exists. The current executor-ready local subsets are the Session 08 Git workbench path, the Session 12 terminal/container paths, and the Phase 20 file mutation manager path. Git runs only allowlisted local Git operations with timeout and redacted summaries. Terminal runs require the optional PTY adapter to probe ready, keep command text request-scoped, cap output to the terminal-session detail boundary, and record only safe execution attachments. Container isolated spawn requires local runtime gates and records safe runtime metadata only. File runs require structured file mutation requests, workspace-root containment, expected-hash checks for destructive mutations, local backups for rollback-capable operations, and compact metadata-only execution summaries. Outside bounded terminal/Git/file queue dispatch, campaign executable task dispatch, Git workbench execution, guarded file approval, and terminal/container command-center routes, the server still does not execute metadata-only queued tasks, open remote access, proxy Worker commands, run hosted jobs, publish channels, or perform broad recovery unless a later scoped implementation adds consent, authorization, auditing, scrubbing, rollback limits, and tests.

Phase 06 isolation work still preserves deterministic unavailable results and 501 unsupported-route envelopes for route families outside the implemented File/Git and terminal/container command-center subsets. Any additional executor family must cover consent, authorization, audit, rollback, redaction, tests, docs, and visible failure states before becoming executable.

Phase 19 adds the local command-center scaffold under `/command-center/*` and `/api/command-center/*`. Snapshot routes cover capabilities, plans, tasks, attention, executors, heroes, executions, permissions, file intents, workpads, evidence, verification, review gates, handoffs, diagnostics, channels, usage, notification readiness, and collaboration posture. Mutation routes can refresh capability timestamps, probe one or all registered executor capabilities, record scaffold state for draft plans, tasks, attention, permissions, unavailable execution results, review gates, handoffs, channel commands, notification readiness, and collaboration posture, and return bounded execution detail at `/command-center/executions/:id`. Terminal snapshot/detail and container probe routes live under the same local prefix, and terminal/container mutation routes are the only Session 12 endpoints that can start a PTY session or local isolated spawn after their readiness gates pass. These routes emit compact `command_center_*_update` WebSocket frames and update `/diagnostics/orchestration` registry posture and execution counts. Capability probes, diagnostics, and execution detail routes do not run terminal, container, remote, Worker, hosted, or channel commands.

Phase 20 Session 08 adds managed Hero/Lineage lifecycle control for sessions explicitly registered with `ManagedAgentSessionManager`. The manager can stop owned process sessions, stop/restart/message owned PTY sessions through `TerminalSessionManager`, reject duplicate in-flight actions, and dispose registered resources on shutdown. Process restart is available only when a restart descriptor is registered; PTY message input is request-scoped and is not stored in lifecycle rows. Hook-observed sessions and unregistered external agents stay record-only, while `change_model`, `change_permission_mode`, and `change_isolation_mode` return unavailable policy-blocked records before any execution run. Lifecycle rows and `command_center_hero_lifecycle_update` frames expose only managed session id, provider label, readiness, supported actions, execution run id, compact status/summary, and unavailable labels; they never expose raw commands, terminal output, transcripts, provider payloads, broad paths, tokens, or hosted/external-session control claims.

Phase 20 Sessions 04-07 add executable dispatch for plan campaign tasks while preserving the same local executor boundary. Campaign create and refine routes may accept per-task terminal, Git, file, or container executable payloads and `dependencyTaskIds`, but executable payloads are stored privately. Public campaign task records expose only dependency ids, execution readiness, compact result summaries, queue ids, task ids, execution run ids, and compact file rollback metadata or container runtime/cleanup metadata when present. `POST /command-center/campaigns/:id/dispatch` walks the campaign DAG in stable dependency order, creates queue entries only for ready executable tasks, includes `{ planId, taskId }` references, and routes terminal/Git/file/container work through the shared `TaskExecutionCoordinator`. Unknown, cyclic, failed, unavailable, cancelled, or planning-only prerequisites block dependents with safe `blockedReason` copy; tasks waiting on unfinished prerequisites stay pending. Planning-only campaign tasks are returned as planning-only results and are not queued or executed.

File queue dispatch and guarded file approvals use `src/managers/fileMutationManager.ts` through the shared coordinator or executor registry. Structured file requests stay private to the executable payload; broad queue entries, command-center execution rows, and WebSocket updates expose only operation labels, safe path labels, changed-byte counts, backup ids, rollback state, compact result summaries, and failure/unavailable codes. `/task-queue/:id/rollback` accepts a file backup id and restores through the manager when the backup is still available. These routes never return raw file contents, patch bodies, search/replacement text, backup contents, broad absolute paths, tokens, provider payloads, or diagnostics logs.

Container queue dispatch and campaign container tasks use the existing `src/managers/containerRuntimeManager.ts` isolated-spawn boundary through the shared task execution coordinator. Queue and campaign payloads may store only safe hero labels, image labels, runtime labels, command labels, ids, and recovery notes. Runtime unavailable, image unavailable, stale revision, duplicate in-flight, timeout, and failed-run states return compact queue and command-center execution metadata with `container_run` kind, runtime, cleanup posture, container run id when present, unavailable reason, retryability, and recovery guidance. These routes do not accept arbitrary Docker commands, entrypoints, env, mounts, volumes, image-inspect payloads, workspace paths, or raw container output.

Dispatch and retry responses plus WebSocket updates include campaign, task, queue, and execution changes plus executed, planning-only, unavailable, failed, blocked, waiting, and retryable task id buckets. Unavailable executor results map to `executionReadiness.status: "unavailable"` with `unavailableReason`; failed results use `failureCode` only on failed readiness/summary records. `retryFailed` resets only failed or unavailable executable tasks that can be retried, preserves prior queue and execution history, and rejects duplicate in-flight triggers. The next dispatch creates fresh retry queue/execution evidence for the reset executable tasks. Broad campaign responses never include command text, terminal output, Git stdout/stderr, raw diffs, broad paths, private executable payloads, or terminal session output; bounded execution details remain scoped to `/command-center/executions/:id` and terminal output remains scoped to terminal session detail routes.

Phase 20 release claims are owned by `.spec_system/specs/phase20-session11-release-evidence-and-event-privacy/release-evidence.md`. Server release wording may claim local execution only for evidence-backed terminal, Git status/stage/unstage, file, container, campaign, guarded-action, owned managed lifecycle, template queue, and proposal-first channel paths. Git push, generic webhook/chat auto-execution, hosted or Worker command bridges, remote access, arbitrary Docker commands, provider model/permission or isolation mutation, production-hosted Command Center validation, hosted identity/storage, public replay hosting, and trusted unified erasure remain policy-blocked, proposal-only, unavailable, unsupported, or no-claim until a later scoped release record promotes them.

Session 16 adds web command-center ergonomics without adding server routes or new execution claims. Browser shortcuts use the existing local REST contract: selected Attention approve/reject still goes through `/command-center/attention/:id/decision` with expected revisions and idempotency keys, queue cycling is client selection state, and terminal, isolated spawn, handoff, and File/Git entries open existing local surfaces. The adjacent-surface panel is also client routing over existing snapshots and UI entry points. Mission Control, Plan Workpad, Permission Modal, Notice Board, Quest Board, War Room, Settings, Git/Codex, terminal, handoff, export, replay, achievements, mission history, channels, and usage rows do not create new server capabilities. Remote Access remains explicit no-claim copy and still does not open tunnels, hosted diagnostics, Worker commands, or remote executors. App desktop/mobile evidence is `tests/e2e/orchestration-command-center.e2e.ts`; server validation remains the local route, WebSocket, typecheck, lint, and full Vitest suite evidence.

Session 15 adds deterministic command-center metrics and notification posture. `src/managers/usageMetricsManager.ts` aggregates bounded operational signals from plans, tasks, attention, permissions, executions, review gates, channels, queue age, blocked age, latency, executor utilization, provider usage summaries, and mission artifact estimates. `POST /command-center/usage` can record one protocol-shaped usage metric, and `POST /command-center/usage/refresh` rebuilds the derived rollups with duplicate in-flight protection and compact `command_center_usage_update` frames. Provider usage and artifact estimate input is metadata-only and must not include raw prompts, transcripts, terminal output, provider payloads, file contents, diffs, local absolute paths, or secrets.

Session 15 also adds `src/lib/notificationRouting.ts` and `src/routes/commandCenterNotifications.ts` for local notification preferences, readiness, Web Push consent, subscribe, and unsubscribe posture. Routes live at `/command-center/notifications/{preferences,readiness,subscribe,unsubscribe}` and the `/api` aliases. They persist local preferences, safe readiness records, and optional subscription summaries with endpoint hashes and key lengths only. VAPID readiness, browser permission, service-worker availability, explicit consent, duplicate-trigger guards, payload minimization, and compact WebSocket updates are enforced before state mutation. Consent and readiness do not imply active hosted push delivery; `pushDeliveryActive` remains false unless a later scoped server delivery implementation proves that path.

Session 14 adds local channel and webhook intake routes under both root and `/api`: `GET /channels`, `POST /channels/:provider/pair`, `GET /channel-commands`, `POST /webhooks/generic`, and `POST /webhooks/github`. They create bounded `CommandCenterChannelCommand` rows with source labels, delivery ids, validation state, replay metadata, and scope labels. Intake can convert a valid delivery to queue, attention, or guarded-action review state, but conversion remains proposal-first and trusted remote execution is not enabled. Duplicate replay keys update the existing channel command replay metadata rather than duplicating conversions. Route validation rejects raw headers, cookies, auth payloads, raw bodies, raw payloads, signatures, secrets, and unsafe path labels before state mutation.

Command-center snapshot routes now accept safe `projectLabel`, `worktreeLabel`, `sourceLabel`, `rosterLabel`, and `scopeKinds` filters in addition to existing id, state, family, package, cursor, and sort filters. These filters match stored scope labels only; they do not read files, inspect worktrees, infer remote authority, or claim hosted identity.

Session 13 adds the command-center collaboration and handoff helpers. `src/managers/handoffManager.ts` owns handoff scaffolding, readiness validation, resume-source state, re-entry stale-state handling, and compact remote-access summaries. `POST /command-center/context-notices` creates canonical Notice Board messages with `commandCenterLinks` for review requests, blockers, and completions. `POST /command-center/handoffs/scaffold`, `/validate`, `/resume-source`, and `/reentry` mutate handoff state with expected-revision and duplicate-trigger guards. The collaboration posture `GET` and `POST` routes at `/command-center/collaboration-posture` expose visibility, Notice linkbacks, visibility counts, room labels, and remote-access summary labels only.

The collaboration routes use `src/lib/commandCenterCollaboration.ts` and `src/lib/commandCenterValidation.ts` to reject raw payload fields before manager storage, Notice persistence, War Room bridge relay, WebSocket broadcast, diagnostics, or docs examples. They do not run Worker commands, open tunnels, claim remote access, execute local tools, read transcripts, inspect replay buffers, or return raw prompts, command bodies, terminal output, file contents, diffs, tokens, authority values, logs, exports, or broad paths. `src/lib/warRoomNoticeBridge.ts` exposes only compact relay posture and catch-up summaries for room snapshots.

Session 10 keeps mission artifact ownership inside `src/managers/orchestrationCommandCenter.ts`. `POST /command-center/workpads` and `POST /command-center/evidence` store manager-owned metadata records with safe targets, revision checks, idempotency keys, and compact WebSocket updates. `POST /command-center/verification/:id/retry`, `POST /command-center/verification/:id/skip`, and `POST /command-center/review-gates/:id/decision` enforce stale-revision checks, duplicate-trigger guards, retry limits, and bounded attention creation or resolution. Artifact routes and manager helpers reject blocked raw payload fields and store only metadata, safe repo-relative labels, ids, timestamps, revision state, and blocked-category labels; they do not read or return raw evidence contents, diffs, command output, prompts, transcripts, logs, tokens, or broad absolute paths.

Session 08 adds the bounded File/Git command-center subset. `POST /command-center/file-intents/conflicts` checks active write/edit/delete/move intent conflicts by repo-relative path, and `POST /command-center/file-intents/:id/resolve` records clear, override, or reassignment audit metadata without editing workspace files. `POST /command-center/git/preview` builds allowlisted local Git workbench plans without running Git. `POST /command-center/git/execute` runs only allowlisted local Git workbench operations through timeout-capped execution, stores compact `git_` command-center execution runs, and emits `command_center_execution_update`; push remains policy-blocked unless external Git execution is explicitly enabled. Broad responses use bounded summaries and repo-relative labels only, never raw diffs, command output, secret values, or absolute paths.

Session 06 of Phase 20 adds the manager-level file mutation core in `src/managers/fileMutationManager.ts`. The manager can read safe metadata, preview, write, edit, delete, move, rename, and roll back local workspace files when a caller supplies repo-relative paths and current `expectedSha256` preflight data. It rejects traversal, absolute paths, URL-like paths, NUL input, symlink escapes, destination-parent symlink escapes, generated or ignored paths, stale hashes, active file-intent conflicts, destination collisions, backup failures, and duplicate in-flight triggers before workspace mutation. Backups are local recovery artifacts under the injected FactionOS state root at `file-mutations/backups/<backup-id>/`; backup metadata contains ids, operation labels, safe repo-relative path labels, hashes, timestamps, and rollback state only. File contents stay only in the local backup content file and never enter command-center rows, WebSocket events, diagnostics, docs examples, logs, or broad API responses.

The file mutation manager is now wired into guarded-action approval, task queue dispatch, campaign dispatch, and queue rollback surfaces. File requests must be structured and schema-validated; broad route responses and WebSocket events carry only compact manager-owned evidence. The compact `kind: "file"` command-center execution attachment carries operation, safe path labels, before/after hashes, changed byte count, backup id, rollback state, and conflict count.

Session 12 adds the bounded terminal and container command-center subset. `GET /command-center/terminal/snapshot` probes the required native `node-pty` adapter, safe shell/cwd labels, active session count, timeout policy, output cap, and cleanup posture. `POST /command-center/terminal/preview` validates request-scoped command text without starting a PTY. `POST /command-center/terminal/run` starts a PTY-backed session only when the probe is executor-ready, rejects duplicate in-flight runs, records compact execution metadata, and exposes bounded output only through `/command-center/terminal/sessions/:id`. Input, kill, and restart routes use the same idempotency and in-flight guards. `GET` and `POST /command-center/containers/probe` inspect Docker and Apple Containers readiness without starting containers. `POST /command-center/containers/isolated-spawn/preview` and `POST /command-center/containers/isolated-spawn/run` expose local guarded isolated-spawn readiness; run requires ready local gates and records safe container attachment metadata. Task queue and campaign container dispatch are a queue-facing use of the same manager, not new direct Docker APIs. Missing PTY support, Docker, Apple Containers, image state, mounts, browser dependencies, network, or provider runtime returns structured unavailable or preview-only states instead of remote, hosted, Worker, or tunnel execution.

Phase 19 Session 05 promotes hook `permission_request` ingest into paired command-center permission and attention records. `POST /command-center/permissions/:id/decision` and `POST /command-center/attention/:id/decision` record approve or reject decisions with bounded reason, actor, expected revision, idempotency, duplicate, stale, and timeout metadata, then emit paired permission and attention frames when linked. Legacy `/permission-response`, `/plan/approve`, WebSocket `permission_response`, and WebSocket `plan_approval` now route through the same command-center decision manager while preserving their compatibility response or event shape. These decisions are local audit and cockpit state only; they do not run executors, tools, file edits, terminal commands, Worker actions, hosted jobs, or remote operations.

Session 06 adds the executor registry used by guarded-action REST routes and WebSocket `guarded_action_decision`. Approval records intent first, then probes the matching registry entry. Non-ready entries create unavailable or approved-not-executing command-center execution runs with repair guidance; executor-ready entries move through queued, executing, and executed or failed run states with redacted bounded summaries. File audit adapters and Git workbench adapters plus the Session 12 terminal and container adapters are the shipped executor-ready local mutation-adjacent families when their probes pass. Remote, Worker, hosted, webhook, push, channel, and settings route families remain unavailable, planned, or separate-surface unless explicitly registered as executor-ready in a scoped implementation.

## Phase 06 Closeout

Phase 06 closes the server-owned local isolation and separation subset. The local `/warroom` route remains a compatibility/status stub, Worker room APIs remain separate-surface responses, and `/diagnostics/isolation` remains read-only compact posture. Unsupported local route families return deterministic 501 envelopes with family and status labels instead of raw request bodies or private context.

Outside the bounded File/Git, terminal/container, terminal/Git/file/container task queue, and campaign executable dispatch subsets documented above, the server still does not execute metadata-only queued tasks, mutate files, run arbitrary terminal commands, start arbitrary containers, proxy Worker rooms, host collaboration identity, provide analytics, publish replay storage, or perform trusted erasure. Future executor work needs a separate threat model with consent, authorization, audit, rollback, redaction, tests, docs, and visible failure states before any mutation is shipped.

## Phase 07 Hosted-Service Boundary

Session 01 of Phase 07 adds documentation-only guardrails for hosted configuration, identity, storage, public replay, analytics, push, remote access, and diagnostics. The server still owns the loopback runtime, current local auth, local diagnostics, redacted exports, redacted archives, LLM privacy headers, and unsupported-route envelopes.

Future server-owned hosted work must keep server-only secrets out of browser config, health responses, diagnostics, logs, replay fragments, exports, and archives. Any hosted-sensitive route must add protocol contracts where shared, request validation, authorization, rate limits, compact error envelopes, redaction, disabled or unavailable states, focused tests, and docs before it is described as active. Session 01 does not add hosted auth, hosted storage, analytics ingestion, push routing, remote access, public replay hosting, hosted diagnostics, production-hosted validation, or trusted erasure. Session 03 adds planned-state hosted identity diagnostics only. It does not add OAuth, SSO, organization management, account UI, Supabase auth, hosted storage authorization, analytics consent, public replay hosting, push, remote access, production audit proof, or trusted erasure. Room-local Worker authority remains separate from local server auth and is not hosted account identity. Session 04 adds planned-state hosted persistence diagnostics only. It does not add a Supabase client, database, bucket, storage migration, upload endpoint, browser direct storage write, account-backed persistence, public replay route, public replay indexing, takedown runtime, release-grade deletion, production-hosted validation, or trusted erasure. Current replay links and exports remain local-first artifacts, not server-hosted public replay or hosted-safe storage. Session 06 adds planned-state hosted operation diagnostics only. The server exposes `GET /diagnostics/hosted-operations` behind the same CORS, local bearer auth, rate-limit, and body-size middleware as the other local routes. The response is compact posture: status labels, push/remote/tunnel/diagnostics surface labels, requirement labels, blocked payload labels, unsupported claim labels, booleans, counts, and docs paths only.

The route does not add push delivery, push subscription storage, VAPID private key use, Cloudflare Tunnel, remote access, remote executors, hosted diagnostic agents, production-hosted validation, or trusted erasure. It must not return raw env values, subscriptions, VAPID private keys, Cloudflare account ids, zone ids, API tokens, tunnel tokens, request bodies, local paths, prompts, commands, terminal output, room payloads, logs, backups, exports, archives, replay buffers, scan payloads, or media drafts. Unsupported methods continue through the deterministic 501 catch-all without echoing request bodies.

Phase 07 Session 07 closeout validated the four local hosted diagnostics routes (`hosted-config`, `hosted-identity`, `hosted-persistence`, `hosted-operations`) as status-only, auth-inheriting, redacted posture endpoints. Server route tests and the secret scan (886 tracked files) confirm bounded labels and counts only, with no raw credentials, account ids, zone ids, tunnel tokens, or local payloads returned. No hosted route family is promoted to active; production-hosted route validation remains blocked/no-claim unless a later release record proves deployed hosted behavior.

Phase 08 Session 01 records the release baseline in `.spec_system/archive/phases/phase_08/release_requirements_risk_baseline.md` and routes server-adjacent erasure, hosted identity, and production-hosted validation work in `.spec_system/archive/phases/phase_08/phase08_requirement_routing_matrix.md`. Unless a specific release record says otherwise, the server must not describe local diagnostics, local recovery, local exports, local archives, hosted posture routes, or health checks as full trusted erasure, active hosted identity, hosted storage, analytics capture, public replay hosting, push delivery, remote access, hosted diagnostics upload, or production-hosted validation.

Phase 08 Session 02 adds the server erasure inventory helper above and the protocol contract in `packages/protocol/src/erasure.ts`. This closes inventory and vocabulary setup only. Session 03 still owns local/browser deletion, confirmation, audit, idempotency, partial-failure, and verification behavior.

Phase 08 Session 08 release-candidate closeout records passing local server quality gates and focused erasure/hosted guardrail tests, but live hosted smoke still blocks deployed hosted claims. Keep `/diagnostics/hosted-*`, `/health`, `/event`, local erasure routes, and local recovery output scoped to local posture unless a later release record proves deployed hosted behavior.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://faction-os.gitbook.io/faction-os-docs/apps/server/readme_server.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
