Skip to content

Security model

Maintainer HQ keeps secret-bearing operator state local while exposing sanitized projections to its CLI, dashboard, and MCP server. The workspace document is trusted local state, not a safe interchange format.

Workspace document

The default document lives at ${XDG_DATA_HOME:-$HOME/.local/share}/maintainer-hq/workspace.json. Maintainer HQ creates the parent directory with mode 0700 and the file with mode 0600. It rejects symbolic links, non-regular files, broad permissions, oversized content, and files that change identity or metadata during a read.

A document outside Git uses private filesystem permissions as its storage boundary. A document inside a Git worktree must also be covered by the git-crypt filter with plaintext Git diffs disabled through -diff. Explicit --require-git-crypt validation is available when this policy must apply regardless of location.

The document can contain provider recovery values, HMACs, private route identifiers, private fallback targets, and application secret values. Do not pass its contents through command arguments, logs, issue reports, browser tools, MCP messages, fixtures, screenshots, CI artifacts, or packages.

Activity ledger

The activity sidecar is private operator state, not a publishable event export. It is bound to one workspace ID and can contain non-public repository names, commit and release metadata, operator notes, decisions, blockers, next actions, generated summaries, and cost receipts. Its default directory and file modes, no-follow reads, size bounds, Git-encryption check, adjacent lock, identity validation, and atomic replacement match the workspace boundary.

CLI, dashboard, and MCP consumers receive validated activity records but never the sidecar path or raw file. Provider readers accept only repositories already attached to explicit projects and return allowlisted normalized fields. Source failures become bounded categories without provider bodies. Refresh is idempotent and does not overwrite acknowledgement, pin, archive, or manual audit state.

Safe reads

Maintainer HQ opens private files with no-follow and nonblocking semantics, reads within fixed bounds, and verifies the same file descriptor and path identity after reading. Browser and MCP projections expose repository policy, capability state, secret names, consumer metadata, monitoring target IDs, and environment variable names, but not stored values, HMACs, raw routes, monitoring URLs, or probe configuration.

Environment inventory recognizes live and template dotenv-style files beneath configured roots. It does not follow candidate-file links. The browser receives path, mode, classification, and variable names only. Permission repair changes only eligible discovered live files and never reads their values into a plan.

Atomic writes and recovery

Every workspace writer acquires a private adjacent lock, rereads the planned revision, writes a private temporary file, syncs content and metadata, replaces atomically, and retains the lock through completion. Competing or stale writers fail closed. If a process stops while holding a lock, confirm that no writer remains before removing the adjacent hidden lock file.

Plans bind the complete mutation to a document revision with a process-local HMAC. Secret-bearing plans remain in a bounded expiring memory store, and only their digest and redacted summary leave the service. Apply rereads both workspace and relevant local evidence before confirmation and inside the write lock.

Managed Hookrelay retirement retains recovery until provider route, KV, receiver HMAC, declared sender secrets, and final synchronization absence have all been verified. A safe private recovery manifest makes failed stages resumable. Linked, non-regular, or broadly readable recovery output is removed instead of retained.

Dashboard boundary

The broker binds only to 127.0.0.1 on a random port. Every page and API request uses a random session route and session header, checks the exact loopback Host and same-origin request, and serves an allowlist of bundled assets through bounded no-follow descriptors. The page loads no remote scripts and receives a restrictive Content Security Policy.

Write sessions use exact revision-bound plans and reject changed mutations. Scheduled re-review evidence exposes repository identity, due date, and commit ID but not checkout contents or diffs. Endpoint Monitor inventory is reduced to validated target IDs before it reaches a snapshot; adapter failures become fixed unavailable observations rather than leaking output.

AI activity summaries are disabled unless the server inherits an AI Gateway credential, and no feed operation calls a model automatically. The operator selects exact activity records and field classes before planning. Safe defaults omit bodies and repository names. Planning uses current catalog metadata to admit only providers marked for zero retention and no prompt training; apply fixes that provider allowlist in the Gateway request and sends the prompt only after a second confirmation.

The ledger stores a SHA-256 prompt hash rather than the prompt itself. It retains the selected entry IDs and field classes, generated output, model, eligible-provider and pricing snapshot, estimate, usage, actual Gateway cost, and bounded status so disclosure and spending remain auditable. It discards provider error bodies and any output whose receipt exceeds the reviewed cost limit. Because the limit is checked against the returned receipt, it is an approval and output-acceptance boundary rather than a provider-side billing cap.

MCP boundary

The MCP server is a local stdio process and opens no network listener. Protocol frames use stdout and diagnostics use stderr. A read-only server leaves planning and verification available while rejecting workspace, filesystem, migration, projection, recovery, and provider writes.

Tool callers cannot request the raw workspace document, a secret-bearing projection, an environment value, an arbitrary executable, a provider method, or an API URL. Secret changes identify an inherited environment variable; the process resolves its value only into short-lived memory. Startup and provider diagnostics redact inherited secret-shaped values before returning output.

Confirmation-gated tools mint authenticated method-bound request state, show the complete exact plan, require explicit approval, and reject mismatched or replayed input. Provider writes rerun the adapter plan before and after confirmation, reject failure, suppression, truncation, or changed output, and retain the adapter's own confirmation rules.

Publication boundary

npm run check:publication reads every publishable project file through bounded no-follow descriptors and rejects unexpected links, environment files, machine-private paths, secret-shaped material, remote browser assets, inline scripts, non-ASCII content, and unsafe entrypoints. npm run check:package independently inspects the exact npm file manifest. The release package excludes tests, CI, development scripts, local state, and build output.

The documentation build adds a second bounded inspection over the generated artifact. It requires the complete page and routing surface, rejects linked or oversized output, remote browser assets, source maps, secret-shaped material, and machine-private paths, and publishes only synthetic product data. CI stamps the verified artifact with its exact source revision, passes that artifact unchanged to the protected deployment job, and verifies the canonical origin after publication.

Vulnerability reporting

Use GitHub private vulnerability reporting when available. Include the affected version, impact, minimal reproduction, and proposed mitigation, but never attach a real workspace document, activity ledger, credential, provider projection, environment value, private repository identity, or browser artifact containing private state.

If private reporting is unavailable, contact the repository owner privately through their GitHub profile. Do not open a public issue for an undisclosed vulnerability.

A Strange Lasers project released under AGPL-3.0-only