# Security

Zaxy handles durable memory, so security defaults matter. The most important
rule is to keep secrets out of Eventloom payloads. Eventloom logs are designed
to persist, replay, and export. Store redacted summaries, references, and
non-sensitive identifiers instead of credentials, API keys, bearer tokens, or
private customer data.

Eventloom appends apply a final safety net before an event is sealed. Payload
keys such as `password`, `api_key`, `authorization`, `token`, `secret`, and
`private_key` are replaced with `[REDACTED]`, and common secret-looking string
values are redacted even when the field name is generic. The event records a
`security` classification with `sensitivity` and `redacted_paths`, so replay
can show where redaction occurred without exposing the original value. Treat
this as defense in depth, not as permission to send raw credentials.

Production configuration should use secret files. Supported file variables are
`NEO4J_PASSWORD_FILE`, `MCP_ADMIN_TOKEN_FILE`,
`MCP_REMOTE_AUTH_TOKEN_FILE`, `MCP_OIDC_CLIENT_SECRET_FILE`,
`OPENAI_API_KEY_FILE`, and `PATHLIGHT_ACCESS_TOKEN_FILE`. The settings loader
reads these paths from process environment or `.env`. Direct env variables take
precedence. Secret files should be mode `600` and stored outside the repository
when possible.

Remote MCP/SSE must be authenticated. Configure `MCP_REMOTE_AUTH_TOKEN` or
`MCP_REMOTE_AUTH_TOKEN_FILE`; clients must send `Authorization: Bearer <token>`.
For public multi-tenant deployments, prefer OIDC by configuring
`MCP_OIDC_ISSUER`, `MCP_OIDC_AUDIENCE`, and `MCP_OIDC_JWKS_URL`. OIDC mode
validates JWT issuer, audience, signature through JWKS, required scope
`MCP_OIDC_REQUIRED_SCOPE`, and the session claim named by
`MCP_OIDC_SESSION_CLAIM` before setting the remote session scope. Static bearer
mode uses `MCP_REMOTE_SESSION_HEADER`, defaulting to `x-zaxy-session-id`, for
session scoping. Remote clients should not be able to replay or query another
session by choosing a different payload field.

Remote MCP/SSE requests are rate-limited by resolved session ID before the MCP
transport handles the request. `MCP_RATE_LIMIT_ENABLED=true`,
`MCP_RATE_LIMIT_REQUESTS=120`, and `MCP_RATE_LIMIT_WINDOW_SECONDS=60` provide a
process-local fixed-window guard. A rejected request returns HTTP `429` with a
`Retry-After` header. This is a single-process control; place a shared limiter
or gateway in front of horizontally scaled deployments.

Audit export can be enabled with `MCP_AUDIT_ENABLED=true`. Zaxy appends compact
JSONL records to `MCP_AUDIT_PATH`, defaulting to
`.eventloom/remote_audit.jsonl`, for allowed, auth-denied, and rate-limited
remote requests. Audit records include route, method, outcome, session ID when
known, reason, timestamp, and client host when available. They do not include
bearer tokens, JWTs, request payloads, or raw query text.

Production also requires `MCP_ADMIN_TOKEN` or `MCP_ADMIN_TOKEN_FILE`.
Replay and invalidation are bulk-read or state-changing operations, so they
must fail closed when no admin token is configured. Remote sessions still remain
session-scoped after admin authorization.

Graph projections are session-scoped. Zaxy stores `session_id` on projected
entities and relationships, includes it in the temporal uniqueness constraint,
and applies it to exact, keyword, vector, traversal, replay, and invalidation
paths. This prevents one remote client from retrieving another client's memory
through the shared projection backend.

The default embedded LadybugDB projection stays local to the Eventloom workspace. An
optional Neo4j sidecar should use TLS in production. Development compose binds
ports to localhost. Production compose enables Bolt TLS and mounts certificates
generated by `scripts/generate-certs.sh` or supplied by your platform. The
deployment validator rejects production plaintext Bolt usage without a CA
certificate when the Neo4j backend is selected.

Input validation and payload sanitization live in `src/zaxy/security.py`. They
bound payload size, query length, traversal depth, result limits, and session ID
shape, then classify and redact payloads before durable writes. These limits
protect the graph and filesystem from accidental or hostile inputs.

Observability also needs care. Pathlight tracing should avoid raw query text
unless `TRACE_RAW_QUERIES=true` is explicitly chosen. Raw prompts and queries may
contain sensitive context. Prefer hashes, summaries, and structured metadata for
routine production traces.

Before promotion run:

```bash
scripts/validate-deployment.sh --root .
scripts/release-check.sh --root .
```

See [configuration.md](configuration.md), [deployment.md](deployment.md),
[operations.md](operations.md), [mcp.md](mcp.md), and [README.md](../README.md)
for the surrounding procedures.
