Production checklist#
What to settle before needtoknow serves real users, and what happens when each part of the system fails. Each item links to the page with the details.
Before launch#
Identity
[ ] The
Subjectis built from your authenticated session: the user id and groups your identity provider vouches for, never values from the request body, and never a tool argument a language model fills in. (Security model)[ ] Only groups that grant access to documents are on the subject. A subject in tens of thousands of groups works, but costs on every query. (Operations)
[ ] In a multi-tenant deployment, every subject carries its tenant, and the authorizer is wrapped in
AllOf(TenantAuthorizer(), ...). A subject without a tenant then gets nothing. (Combining authorizers)
Permissions on the chunks
[ ] Permission metadata is copied onto every chunk at ingestion, and re-copied when a document’s sharing changes. How soon that happens is your revocation delay for those chunks. (Security model)
[ ] The permission fields are indexed in your vector store:
create_schema(pgvector),create_collection(Qdrant, Milvus) andcreate_index(Elasticsearch, OpenSearch) create the indexes; Chroma needs no setup. (Integrations)[ ] Your own backend or authorizer, if you wrote one, passes the conformance kit. (Extending needtoknow)
Retrieval settings
[ ]
recheck=True(the default) stays on. It also catches a filter that is broader than the authorizer’s own check; withrecheck=Falseonly a backend that ignores or mistranslates the filter is caught.[ ]
strategy="auto"(the default) or"prefilter"when the authorizer can return a filter. Post-filtering alone loses recall for users who may read little of the index. (Benchmarks)[ ]
max_candidatesis sized for your least-privileged users, andcomplete=Falseis monitored.[ ] A permission change at the source reaches the chunks’ metadata (
set_metadata) within your revocation target, and you know each source document’s chunk ids. (Operations)
The permission service
[ ] HTTP authorizers have a
timeoutandretriesyou have chosen, and a sharedCircuitBreakerso a failing service is not flooded. (Operations)[ ] Connections use TLS and credentials (
headers=, ortoken=for SpiceDB).[ ]
CachingAuthorizersits in front, with attlchosen as a security decision: it is the longest a revoked permission keeps granting access. (Operations)[ ] With several replicas, the shared cache has a
signing_keyand a Redis reachable only by the application. (Security model)
Observability
[ ] Audit events go to durable storage (
JsonLinesAuditor your own observer), with anaudit_keyfrom your secret store if you want query fingerprints. (Operations)[ ]
OpenTelemetryMetricsandOpenTelemetryObserverare installed, and the alerts in What to alert on are set up, starting with any increase inneedtoknow.violations.[ ]
examined,deniedand timings stay in logs and metrics and are never shown to end users; they can hint that forbidden documents exist. (Security model)
Supply chain and capacity
[ ] The version is pinned, and the release’s signatures are verified as SECURITY.md describes.
[ ] Capacity is measured on your own data with the load harness in
benchmarks/(Benchmarks). In ours, over a million chunks, one 4-CPU machine shared by PostgreSQL and the load generator peaked at 126 requests a second.
When something fails#
needtoknow fails closed: whenever it cannot be sure what a subject may read, it returns nothing and says why. The table shows what each failure looks like.
What fails |
What needtoknow does |
What you see |
|---|---|---|
The permission service is down, slow or erroring |
Raises |
|
The permission service gives a malformed answer (wrong count, non-booleans, not a filter) |
Raises |
As above |
The vector store is down |
Raises the store client’s own error; nothing is returned |
|
The vector store ignores or mistranslates the filter |
The re-check drops every chunk outside it |
|
Post-filtering cannot find |
Returns what it found |
|
A subject has no tenant under |
Raises |
As for a failing authorizer |
The shared cache (Redis) is down or slow |
Treats it as a miss and asks the permission service |
A warning on the |
A shared-cache entry is unsigned, tampered with or copied under another key |
Ignores it (with |
Nothing: it is a miss |
|
Raises, because the caller asked for a revocation now |
The exception at the caller |
An observer raises |
Logs it; retrieval is unaffected |
An error on the |
What to do about each, containment first, is in the incident runbook.
What needtoknow does not protect against is listed in the security model: output control, prompt injection inside readable chunks, and the side channels above.