docs: replace daedalus-service basic auth with per-user DRF tokens
All checks were successful
CVE Scan & Docker Build / security-scan (push) Successful in 56s
CVE Scan & Docker Build / build-and-push (push) Successful in 3m30s

This commit is contained in:
2026-05-22 22:59:59 -04:00
parent 7296b8c42f
commit 409da7d109
17 changed files with 364 additions and 163 deletions

View File

@@ -367,9 +367,12 @@ Mnemosyne validates the JWT against `MCPSigningKey` keyed by `kid`.
## 7. REST API — Mnemosyne team lifecycle
All endpoints live under `/mcp_server/api/teams/` and are protected
by the existing `daedalus-service` HTTP Basic account (same auth as
`/library/api/workspaces/` and `/library/api/ingest/`).
All endpoints live under `/mcp_server/api/teams/` and are authenticated
as the Mnemosyne user the team belongs to via a per-user DRF token
(`Authorization: Token <key>`, surfaced on `/profile/settings/`). Each
team has an `owner` FK; non-owners receive 404 (never 403) so a team's
existence isn't disclosed across users. `/library/api/workspaces/` and
`/library/api/ingest/` use the same per-user auth model.
### 7.1 `POST /mcp_server/api/teams/`
Create a team.
@@ -733,7 +736,8 @@ escape hatch for hard compartmentalization.
* `TeamWorkspaceAssignment` PUT is idempotent and replaces, not
unions.
* `/mcp_server/api/teams/` endpoints: create, delete, rotate,
workspaces PUT, all authenticated as `daedalus-service`.
workspaces PUT, all authenticated with a per-user DRF token and
scoped to the team's `owner` (non-owner requests return 404).
### 14.2 Daedalus test surface
* `on_pallas_registered` populates `team_jwt_encrypted` and transitions

View File

@@ -92,14 +92,6 @@ docker compose -f /srv/mnemosyne/docker-compose.yaml \
docker compose -f /srv/mnemosyne/docker-compose.yaml \
run --rm app setup
# Create the daedalus-service user (HTTP Basic auth for ingest API)
# Pass --password from vault; idempotent if user already exists.
docker compose -f /srv/mnemosyne/docker-compose.yaml \
run --rm app \
python manage.py ensure_service_user \
--username daedalus-service \
--password "{{ vault_mnemosyne_daedalus_service_password }}"
# Seed the MCPSigningKey used to sign long-lived Pallas team JWTs.
# --retire-other deactivates any previously-active key. The hex
# emitted to stdout is persisted in Mnemosyne's database and is
@@ -321,13 +313,16 @@ curl -f http://puck.incus:23181/healthz
curl http://puck.incus:23181/metrics | head -5
```
### Verify the daedalus-service account
### Verify Daedalus auth (per-user API token)
Daedalus now authenticates as a Mnemosyne user via the DRF token shown
on `/profile/settings/`. To smoke-test from a deploy host:
```bash
curl -u daedalus-service:<password> \
https://mnemosyne.ouranos.helu.ca/library/api/workspaces/ \
curl -H "Authorization: Token <user-api-token>" \
https://mnemosyne.ouranos.helu.ca/library/api/workspaces/ws_smoke/ \
-o /dev/null -w "%{http_code}"
# Expect: 200
# Expect: 200 if the workspace exists for that user, 404 otherwise.
```
### Verify MCP connectivity (from a client with a valid MCPToken)
@@ -401,6 +396,5 @@ will report as a failure.
| `vault_daedalus_s3_read_secret` | `DAEDALUS_S3_SECRET_ACCESS_KEY` |
| `vault_rabbitmq_password` | embedded in `CELERY_BROKER_URL` |
| `vault_mnemosyne_llm_encryption_key` | `LLM_API_SECRETS_ENCRYPTION_KEY` |
| `vault_mnemosyne_daedalus_service_password` | passed to `ensure_service_user --password` |
| `vault_mnemosyne_casdoor_client_id` | `CASDOOR_CLIENT_ID` |
| `vault_mnemosyne_casdoor_client_secret` | `CASDOOR_CLIENT_SECRET` |

View File

@@ -8,7 +8,7 @@ This document describes Mnemosyne's role in the Daedalus + Pallas architecture a
Mnemosyne exposes two interfaces for the wider Ouranos ecosystem:
1. **REST API** (`/library/api/*`) — consumed by the Daedalus backend (HTTP Basic auth, service account `daedalus-service`) for workspace lifecycle and asynchronous file ingestion. Phase 1, **implemented**.
1. **REST API** (`/library/api/*`) — consumed by the Daedalus backend authenticated as the owning Mnemosyne user via a per-user DRF token (`Authorization: Token <key>`, surfaced on `/profile/settings/`) for workspace lifecycle and asynchronous file ingestion. Phase 1, **implemented**.
2. **MCP Server** (port 22091 internal, `/mcp/` via nginx on 23090) — exposes search, browse, and retrieval tools. Phase 5 of Mnemosyne's own roadmap, **implemented** with workspace-scoped access control via long-lived team JWTs. Consumed by Pallas FastAgents in production (Daedalus integration Phase 2, **implemented** — see [Phase 3 of this doc](#3-phase-3-long-lived-team-jwt-access-control-for-pallas-instances)).
### Phase status
@@ -105,7 +105,7 @@ Auth is controlled by `MCP_REQUIRE_AUTH` in `.env`. Production sets it to `True`
## 2. REST API for Daedalus
All endpoints require HTTP Basic auth as `daedalus-service`. They are consumed by the Daedalus FastAPI backend only — not by any frontend.
All endpoints require an `Authorization: Token <key>` header carrying the DRF token of the Mnemosyne user the workspace belongs to (surfaced on `/profile/settings/`). Workspaces are scoped to their creating user via the `Library.owner_username` property; cross-user access returns 404. They are consumed by the Daedalus FastAPI backend only — not by any frontend.
### Workspace lifecycle
@@ -354,7 +354,7 @@ mnemosyne_s3_operations_total{operation,status} counter
- [x] `GET /library/api/jobs/{job_id}/`, `POST .../retry/`, `GET /library/api/jobs/`
- [x] `library.tasks.ingest_from_daedalus` Celery task with content-hash-aware supersede logic
- [x] `library.services.daedalus_s3` cross-bucket fetch + copy
- [x] HTTP Basic auth via `daedalus-service` user
- [x] Per-user DRF token auth (`Authorization: Token <key>`); workspaces scoped to the owning user via `Library.owner_username`
### Phase 2 — MCP Server (Mnemosyne roadmap Phase 5) ✅ Implemented
- [x] `mcp_server/` module following the [Django MCP Pattern](Pattern_Django-MCP_V1-00.md)