feat(tenant): add tenant_domains allowlist and /v1/domains management API
Why: - Domain values are denormalized into every Qdrant point payload. Without validation, an unregistered or typo'd domain (e.g. "fier" for "fire") silently creates a new partition that retrieval never queries — the file ends up invisible rather than rejected. Tenants also need independently sized domain sets (one may run 14 insurance lines, another 6), which rules out an enum. Changes: - tenant_domains table (migration 41335d162de8) + repository, unique on (tenant_id, domain). - src/application/domains/: ensure_domain_allowed() is the strict-allowlist check now run inside upload_source_file()'s first transaction, before any MinIO object, job row, or Qdrant point is written. - /v1/domains (list/create/patch/disable/enable) gated on its own domains:read/domains:write scopes, deliberately separate from files:write so an upload key cannot create partitions. domain itself is immutable (denormalized into every point payload); only display_name is editable. Disable blocks new uploads without touching already-indexed points. Impact: - BREAKING: POST /v1/files now rejects any domain without an active tenant_domains row (400, unknown_domain). A domain must be created via POST /v1/domains before the first upload to it.
This commit is contained in:
13
README.md
13
README.md
@@ -16,7 +16,18 @@ uv run python -m src.cli.qdrant_bootstrap # the `chunks` collection
|
||||
uv run fastapi dev src/main.py
|
||||
```
|
||||
|
||||
Both commands are idempotent and safe to re-run. `qdrant_bootstrap` verifies an
|
||||
Before a tenant can upload, its domains must be registered — `POST /v1/files`
|
||||
rejects an unregistered or disabled `domain` with `400`. The calling backend
|
||||
manages them over `/v1/domains` using a key with the `domains:write` scope:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/v1/domains \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"domain": "fire", "display_name": "Fire insurance"}'
|
||||
```
|
||||
|
||||
Both bootstrap commands are idempotent and safe to re-run. `qdrant_bootstrap` verifies an
|
||||
existing collection against the pinned schema and exits non-zero on a mismatch,
|
||||
rather than leaving a silently degraded sparse index in place.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user