Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions docs/operations/qdrant-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ Not migrated:
now fails closed when a server URL is used without the key. The repository ships no
Qdrant server manifest for Kubernetes.

### Deployment consumer inventory

The list above covers clients in this repository. Each deployment also needs its own
inventory. Record every client that can reach the deployment, whether it is running,
scheduled, dormant or unreachable to the operator. For each client, record: owner, state
(active/inactive/unknown), the client artifact and interpreter, mode (server, file or
memory), the credential **source name** (never the value), the transport, whether it
needs read or write access, its test receipt and the remaining action. A client you
cannot inspect, such as another user's configuration or a remote host behind a tunnel,
stays `unknown` with a named owner. Finding no running client does not show that none
exists.

## Configuration

- `QDRANT_API_KEY`: the key. Use at least 32 random bytes, for example
Expand Down Expand Up @@ -82,12 +94,19 @@ Plan for downtime.
`QDRANT__SERVICE__API_KEY`. Print only the variable names, never the values.
2. Back up the storage. Take a Qdrant snapshot of each collection with the current
credentials, or stop the container and copy the storage directory. Verify the
backup's size and file count before you continue.
backup's size and file count, and record a SHA-256 manifest of its files, before you
continue.
3. Provision the key in the secret manager. Do not create or rotate it without
owner approval.
4. Update every client environment that sets `QDRANT_URL` with the same
`QDRANT_API_KEY`: MCP server `.mcp.env` and `.mcp.json` entries, CI or scheduled
jobs if any, and operator shells.
jobs if any, and operator shells. Source and editable checkouts also load
`.env.local`, `.env` and `.env.native` from the checkout root
(`mcp_server/config/environment.py` `load_local_env_files`) unless
`PYTHON_DOTENV_DISABLED=1` is set. python-dotenv itself honours that variable from
version 1.2 (the lock pins 1.2.2); an install that does not use `uv.lock` can resolve
an older python-dotenv that ignores it. Values already in the process environment win.
Treat a `QDRANT_API_KEY` or `QDRANT_URL` in those files as a client credential source.
5. Restart Qdrant through the repository Compose file with the key in the environment:
`op run --env-file=<env> -- docker compose -f docker-compose.qdrant.yml [-f <host override>] up -d qdrant`.
In the merged model, `environment` takes precedence over `env_file`, so a host
Expand All @@ -99,7 +118,15 @@ Plan for downtime.
- `GET /collections` with the key returns 200, and the expected collections and point
counts are present.
- `mcp-index setup semantic` preflight reports Qdrant `ready`.
- A semantic search returns results.
- If semantic search is enabled for this deployment, run a semantic search as a
separate check (see below). It is not part of the authentication readback.
- The running container matches the reviewed Compose inputs: its
`com.docker.compose.config-hash` label equals the hash from step 5's exact command
with `config --hash qdrant` in place of `up -d qdrant`, including the same `op run`
wrapper, `--env-file` and `-f` files. It prints `qdrant <hash>`; compare the hash
field. Without the wrapper the command fails, and with a different key value it
reports a different hash, so only a mismatch from the same invocation is drift.
Do not run `docker compose config` without `--hash`, because it renders the key.
7. Restart the MCP clients and confirm that readiness is `ready`.

## Rollback
Expand All @@ -115,7 +142,8 @@ the unsafe state that #106 removes.
- **Qdrant does not start (guard exit 64, or a Compose render error):** the key did not
reach the container. Fix the injection and run the restart again. The data on disk
has not changed.
- **Data problem after the restart:** stop the container, restore the backup from
- **Data problem after the restart:** stop the container, move the failed storage
aside (keep it), check the backup against its SHA-256 manifest, restore the backup from
step 2 into the storage path, and start it again with the API key still required.
- **Code regression:** revert this change's commits. The authenticated server
configuration can stay in place, because the previous client code sends
Expand All @@ -136,5 +164,8 @@ the unsafe state that #106 removes.
It skips when Docker or the pinned image is unavailable:
`uv run --extra dev pytest tests/integration/test_qdrant_auth_container.py -m integration --no-cov`.

Synthetic create, write, query and delete checks prove authentication only. They do not
show that embedding-backed MCP semantic search works. Track that check separately.

A source change does not prove that a running deployment is repaired. Only the
readback in step 6 against the live service shows that.
36 changes: 33 additions & 3 deletions docs/security/dependency-triage-2026-10-31.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,36 @@ advisories. starlette still matches 4 advisories, all listed as not applicable b
- Applicability was judged from the source at this commit. New uses of SDK HTTP
transports, forms, `PyJWK`, chunker repository processing or async Voyage clients
must reopen the matching rows.
- The installed and deployed versions on operator hosts were not inventoried. A host
that installed before this change keeps starlette 1.0.0 and the old certifi until it
runs `uv sync --locked` again or the image is rebuilt.
- Installed and deployed versions on operator hosts were not inventoried here; one
installed lexical STDIO host is assessed in the 2026-11-05 addendum below. Any other
host that installed before this change keeps starlette 1.0.0 and the old certifi until
it runs `uv sync --locked` again or the image is rebuilt.

## Re-scan and runtime applicability (2026-11-05)

- Re-scan: `uv.lock` at `1a61533`, same export and scanner (`pip-audit` 2.10.1), advisory
data as of 2026-11-05. The result is 393 rows in 35 packages and 278 distinct advisories.
Every package's distinct advisory count equals the count in the tables above. The only
difference is that the two advisories fixed by the starlette and certifi upgrades are
gone. This comparison is by per-package count, not by advisory ID; a GitHub advisory
database check found no advisory published since 2026-10-31 against a locked version.
Every disposition above stands.
- certifi: GHSA-6q48-6c7w-9gw2 now recommends 2026.10.19 or later. 2026.10.18 removed every
emSign root, and 2026.10.19 restored the roots that are not affected. The locked
2026.10.18 is outside the vulnerable range. The difference affects only availability for
servers chained to the restored roots, so there is no security action. Pick it up with the
next routine lock refresh.
- Installed lexical STDIO deployments that predate the upgrade keep starlette 1.0.0 and
certifi 2026.2.25. In that mode neither advisory applies:
- `python -m mcp_server.cli stdio` imports Starlette (through the MCP SDK) but builds
no ASGI application. It never imports `mcp_server.gateway` or
`mcp_server.security.security_middleware`, so the repository's
`AuthenticationMiddleware` is never instantiated (Starlette's class of the same name
is imported with the SDK, but no ASGI application is built) and no HTTP listener
reads `request.url`.
- With semantic search and reranking disabled and no summarization provider key in
the process environment, no HTTPS client runs, so the certifi bundle has no
reachable consumer.
- Reopen this assessment, or reinstall from the current lock, before such a host
serves the HTTP gateway (`mcp-index serve` or `uvicorn mcp_server.gateway:app`),
enables semantic search or reranking, or passes a summarization provider key.
Loading