# Security boundaries

All upstream web text is untrusted data, never model instructions. Siftpath does not execute HTML/JavaScript, render HTML, run page commands, or implement a prompt-injection detector. It forwards the official reader's output with UNTRUSTED_WEB_CONTENT. The consuming agent must apply its instruction hierarchy.

Read input retains the previous public-URL guard: HTTP(S), no userinfo, no literal IP/localhost/single-label/local or reverse-DNS hosts. This does not inspect DNS answers/redirects; official hosted readers own their SSRF and redirect restrictions. Siftpath never fetches the page directly. Do not configure a local privileged reader unless you trust it with these inputs.

Configuration is trusted local code-equivalent input: stdio commands can execute arbitrary programs, HTTP endpoints receive queries and page URLs, and headers/env can access credentials. Never accept model-provided provider definitions. Config is not loaded from a workspace .env; use a private file or environment. POSIX setup files are 0600; Windows requires user ACLs. Preserve backups securely.

Secret interpolation is allowed only in connection env/headers, not HTTP URLs/stdio arguments. HTTP must be HTTPS, except explicit loopback development endpoints. Userinfo, known secret-like query parameters and URL interpolation are rejected; do not smuggle other credentials in arbitrary URL paths/query parameters. Credential values never enter model schemas, router diagnostics or raw upstream error bodies. Upstream stdio stderr is suppressed because third-party logging cannot be trusted not to echo credentials. Siftpath does not sanitize successful third-party content as secrets: configure only trusted official MCP services.

Stdio upstreams inherit the SDK's minimal safe process environment plus explicitly configured env, not every account secret. HTTP redirects for credential-bearing requests are rejected. There is no interactive OAuth storage/flow. Use already provisioned bearer/header credentials or local official stdio.

Output JSON has a character bound and truncation flags. Stdio MCP frames are bounded (1 MiB inbound, 4 MiB upstream). Streamable HTTP response streams are capped at 4 MiB and the operation signal also cancels the underlying fetch/body reading. This is a safety bound, not a claim that all third-party responses are safe. Long-lived SSE streams can also reach this cumulative bound. Configure only trusted MCP servers. Upstream request cancellation is best effort; a timed-out provider may already have billed/processed the call, and fallback can incur another charge.

CLI setup uses pinned Clack prompts in a separately bundled, lazily loaded module; Pi uses its native UI and official keyboard/terminal-width helpers. Passwords are masked, current Keys are never prefilled/displayed, and cancelling a configuration step before save discards the draft without changing disk. Both edit paths read disk-only drafts: environment Keys are resolved at runtime, never copied into private storage or substituted for already saved Keys. Only explicitly entering a nonempty replacement updates its saved value; environment precedence remains unchanged. Quick setup preserves unselected services and still-usable saved routes, and adds no fallback; it repairs only unusable routes. Disable services or explicitly edit routes in advanced setup.

Storage is plaintext JSON, not encryption or isolation from other tools/processes running as the same user. The input flow does not submit Keys to the model or transcript; this does not prevent a model with file-reading tools from reading the private file. POSIX 0600 and Windows ACLs only restrict access according to OS permissions. Existing copies made by older versions are not deleted automatically; inspect and remove them privately (including backups), and rotate credentials if exposed.

CLI `siftpath test` is explicit consent and does not ask again. Clack setup defaults to not testing; Pi test commands/menu show a confirmation with Cancel first. Running Pi TUI tests can be cancelled with Esc, and session shutdown also aborts them. Cancellation is best effort and cannot undo an upstream request or charge. Model-triggered web tools are normal tool calls, not these manually confirmed diagnostics. Official stdio provider packages may themselves load a working-directory .env; for strictly anonymous operation use a clean directory.

## Dependency and release governance

Runtime dependencies are `@modelcontextprotocol/sdk` and `zod`, nothing else; the Pi TUI is an optional peer supplied by the host. The repository lockfile makes development and CI reproducible, while published consumers resolve the same ranges from the registry. Reproducibility for maintainers is not a promise about what a consumer resolves: both are verified.

A production High/Critical advisory blocks release. `npm audit --omit=dev --audit-level=high` is the gate; the full tree, including build tooling, is audited and reported separately and does not silently become a pass. An audit that cannot run or returns an unparsable report is a failure, not an empty result. Fixes stay inside compatible ranges and come from upstream releases; `npm audit fix --force`, downgrades and suppressed checks are not used. A vulnerability with no upstream fix is recorded as a release blocker with its advisory and dependency path rather than worked around.

Most runtime findings arrive through the MCP SDK, which owns HTTP transports, request parsing and credential handling. Updating it is therefore its own reviewed change, not a grouped automated bump. Siftpath itself only imports SDK client/stdio/StreamableHTTP, server/stdio, types and AJV validation; it does not run the SDK's HTTP server or OAuth paths, which bounds, but does not remove, the relevance of server-side SDK advisories.

The tarball is verified twice: against the repository lockfile, and then by generating a real `npm pack` artifact, installing it with a plain `npm install --omit=dev` in a clean directory with no repository lockfile, and checking the actual resolved tree, CLI, MCP stdio server, SDK export and Pi extension registration. Development tooling and the Pi host must not appear in the consumer tree. The locked production install uses the local npm cache offline; the clean consumer install and audit can contact the npm registry. Runtime smoke checks use local fixtures with external networking blocked and never call a provider or model.
