89 lines
4.8 KiB
Markdown
89 lines
4.8 KiB
Markdown
# Task 3 report — optional local pgvector profile
|
|
|
|
## Status
|
|
|
|
Implemented and verified the `local-vector` Compose profile.
|
|
|
|
- `vector-db` uses pgvector 0.8.5 on PostgreSQL 16, pinned to the official multi-arch
|
|
manifest digest.
|
|
- `vector_data` is a project-scoped named volume and is not shared with application data.
|
|
- database readiness gates the packaged one-shot `vector-migrate` job; core declares the
|
|
migration completion dependency while remaining usable in the pre-existing external profile.
|
|
- bootstrap, migrator, reader, and writer identities are distinct. Bootstrap and migration
|
|
credentials are supplied as Compose secrets; the application receives only reader/writer
|
|
credentials.
|
|
- `deploy/workspaces/local-vector.yaml` selects `pgvector_direct` with separate reader and
|
|
writer connections.
|
|
- the base loopback port binding, `AUTH_MODE=none`, and `THOTH_PUBLIC_EXPOSURE=false` defaults
|
|
are unchanged.
|
|
|
|
## Red/green evidence
|
|
|
|
The initial Compose contract did not list `vector-db`, as required by the brief. The first real
|
|
smoke then failed migration 002 because bootstrap installed the vector extension in `public`.
|
|
The bootstrap was corrected to create the `vectors` schema under the migration owner and install
|
|
the extension there. A clean-volume rerun passed.
|
|
|
|
## Verification
|
|
|
|
- `./scripts/local-vector-smoke.sh`: PASS
|
|
- isolated generated Compose project and credentials
|
|
- clean migration plus idempotent status rerun
|
|
- reader/writer privilege health
|
|
- one-record upsert and similarity search
|
|
- restart of both `core` and `vector-db`
|
|
- persisted search result after restart
|
|
- project-only volume cleanup
|
|
- `./scripts/test-container-deployment.sh`: PASS
|
|
- `./scripts/test-backend-url-policy.sh`: PASS
|
|
- `docker compose --profile local-vector config --quiet`: PASS
|
|
- harness: 477 passed, 5 deselected
|
|
- backend: 84 passed; TypeScript typecheck PASS
|
|
- frontend: 226 passed; TypeScript typecheck PASS
|
|
- `git diff --check`: PASS
|
|
|
|
## Self-review / concerns
|
|
|
|
- Compose cannot make a dependency required only under one profile. The core dependency uses
|
|
`required: false` so the established `external` profile does not activate local infrastructure;
|
|
under `local-vector`, `compose up --wait` still fails if `vector-migrate` exits nonzero, and the
|
|
smoke verifies that successful migration precedes the healthy stack.
|
|
- Reader/writer passwords are injected into core environment variables because Compose service
|
|
attributes cannot be conditional by profile. Bootstrap and migrator credentials remain
|
|
file-backed secrets and are never exposed to core.
|
|
- The smoke intentionally refuses the operator project name `thothii` and removes only its unique
|
|
project namespace and volumes.
|
|
|
|
## Follow-up hardening — credential reconciliation and cleanup ownership
|
|
|
|
Review findings were resolved in a separate follow-up:
|
|
|
|
- Replaced fresh-volume-only initialization with `vector-reconcile`, an idempotent one-shot that
|
|
runs after database health and before `vector-migrate`. It authenticates with only the bootstrap
|
|
admin secret, safely creates missing identities, reconciles role attributes and passwords on
|
|
existing volumes, restores memberships/ownership, and leaves vector data untouched.
|
|
- The migrator is explicitly `NOSUPERUSER NOCREATEDB NOCREATEROLE`. Schema/database ownership is
|
|
sufficient for all packaged migrations because reconciliation creates the two group roles first.
|
|
- The live smoke rotates migrator, reader, and writer secrets on the same populated volume, rejects
|
|
the old reader credential, reruns migrations, recreates core with the new runtime credentials,
|
|
and retrieves the record written before rotation and again after database/core restart.
|
|
- Smoke project names are no longer caller-controlled. Each run creates a unique namespace and
|
|
ownership token. Containers, networks, and volumes carry the ownership label; preflight refuses
|
|
any collision and cleanup verifies every discovered resource before `down --volumes`.
|
|
- Added a dynamic fake-Docker contract suite for caller override, collision, and mismatched cleanup
|
|
labels, plus a real-Docker collision probe using a unique labeled volume.
|
|
|
|
Follow-up verification:
|
|
|
|
- `./scripts/local-vector-smoke.sh`: PASS, including live secret rotation and persisted retrieval
|
|
- `./scripts/test-local-vector-smoke-safety.sh`: PASS
|
|
- `./scripts/test-local-vector-smoke-live-collision.sh`: PASS
|
|
- harness: 477 passed, 5 deselected
|
|
- backend: 84 passed; TypeScript typecheck PASS
|
|
- frontend: 226 passed; TypeScript typecheck PASS
|
|
- Compose security, backend URL, config, shell syntax, and diff checks: PASS
|
|
|
|
Remaining operational constraint: the bootstrap admin secret must continue to match the PostgreSQL
|
|
bootstrap account stored in the volume. Runtime migrator/reader/writer rotation is supported without
|
|
data deletion; bootstrap-account password rotation is a distinct database-administration operation.
|