Hindsight: Deployment Detail
Level 3 (Detail). Parent topic: Hindsight Architecture.
Scope
File-by-file breakdown of every manifest that deploys Hindsight in tazlab-k8s (branch master), the API environment contract, the Vault secret chain, and the four gotchas discovered during the 2026-08-28 deployment. No secret values are recorded here — only Vault paths and Kubernetes secret names.
Manifest Map — apps/base/hindsight/ (10 files)
| File | Purpose |
|---|---|
deployment-api.yaml | hindsight-api Deployment: REST + MCP on 8888, worker disabled, ensure-db init container, fsGroup: 1000 |
deployment-worker.yaml | hindsight-worker Deployment: same API image with command: ["hindsight-worker"], static WORKER_ID, metrics 8889 |
deployment-dashboard.yaml | hindsight-dashboard (control-plane) Deployment: binds 9999, talks to API via in-cluster URL |
service-api.yaml | LoadBalancer 8006→8888; homepage annotations; MetalLB shared-IP annotations |
service-dashboard.yaml | LoadBalancer 8007→9999; same MetalLB shared IP |
service-worker-metrics.yaml | ClusterIP exposing worker metrics port 8889 (Prometheus scrape path) |
networkpolicy.yaml | 1 default-deny + 3 allow policies (see below) |
rbac.yaml | ServiceAccount hindsight-sa + Role hindsight-secret-reader (get/list secrets) + RoleBinding |
servicemonitor.yaml | 2 ServiceMonitors (hindsight-api, hindsight-worker) for kube-prometheus-stack |
kustomization.yaml | Aggregates the above |
Delivery chain: clusters/tazlab-k8s/apps-data-hindsight.yaml (Flux Kustomization apps-data-hindsight, path ./apps/cluster/hindsight, dependsOn: [infrastructure-configs], interval 1h, wait: false, prune: true) → apps/cluster/hindsight/kustomization.yaml (thin overlay) → apps/base/hindsight/. Namespace declared in infrastructure/operators/namespaces/hindsight.yaml.
API Environment Contract (deployment-api.yaml)
| Env var | Value / Source | Notes |
|---|---|---|
HINDSIGHT_API_PORT | "8888" explicit | Override required — gotcha 1 |
HINDSIGHT_API_DATABASE_URL | postgresql://hindsight@tazlab-db-primary.tazlab-db.svc:5432/hindsight?sslmode=verify-full&sslcert=/etc/certs/tls.crt&sslkey=/etc/certs/tls.key&sslrootcert=/etc/certs/ca.crt | mTLS cert-only, no password |
HINDSIGHT_API_DATABASE_SCHEMA | hindsight | Dedicated schema (public not writable on PG16 under PGO) |
HINDSIGHT_API_LLM_PROVIDER | opencode-go | opencode gateway (shared with Hermes) |
HINDSIGHT_API_LLM_MODEL | mimo-v2.5 | |
HINDSIGHT_API_LLM_API_KEY | secret hindsight-opencode-key / key value | |
HINDSIGHT_API_LLM_STRICT_SCHEMA | "true" | |
HINDSIGHT_API_LLM_REASONING_EFFORT | none | |
HINDSIGHT_API_EMBEDDINGS_PROVIDER | google | |
HINDSIGHT_API_EMBEDDINGS_GEMINI_API_KEY | secret hindsight-gemini-key / key value | |
HINDSIGHT_API_EMBEDDINGS_GEMINI_MODEL | gemini-embedding-001 | |
HINDSIGHT_API_EMBEDDINGS_GEMINI_OUTPUT_DIMENSIONALITY | "768" | |
HINDSIGHT_API_RERANKER_PROVIDER | rrf | Fuses the 4 recall strategies |
HINDSIGHT_API_MCP_ENABLED | "true" | |
HINDSIGHT_API_MCP_STATELESS | "true" | Endpoint /mcp/{bank_id}/ |
HINDSIGHT_API_MCP_AUTH_TOKEN | secret hindsight-mcp-token / key value | |
HINDSIGHT_API_METRICS_BACKLOG_ENABLED | "true" | |
HINDSIGHT_API_FILE_STORAGE_TYPE | native | |
HINDSIGHT_API_FILE_DELETE_AFTER_RETAIN | "true" | |
HINDSIGHT_API_WORKER_ENABLED | "false" | Worker runs as its own Deployment |
Worker additions: HINDSIGHT_API_PORT=8889 + HINDSIGHT_API_WORKER_HTTP_PORT=8889 (metrics), HINDSIGHT_API_WORKER_ID=hindsight-worker-1 (static — worker identity must survive pod recreation). Dashboard env: PORT=9999, HINDSIGHT_CP_DATAPLANE_API_URL=http://hindsight-api.hindsight.svc.cluster.local:8888, HINDSIGHT_CP_DATAPLANE_API_KEY/HINDSIGHT_CP_ACCESS_KEY from secret hindsight-cp-key.
Probes (API): liveness /health/live, readiness /health, both on 8888. Resources: API 512Mi–1Gi, worker/dashboard small. Both API and worker mount the db-client-hindsight-tls secret at /etc/certs and run the same ensure-db init container (postgres image, digest-pinned): wait for the DB, then hard-fail if the vector extension is missing.
The 4 Documented Gotchas
- Kubelet env collision —
HINDSIGHT_API_PORT. The kubelet injectsHINDSIGHT_API_PORTinto the pod from the Service namedhindsight-api(service-links env mapping). Without an explicitHINDSIGHT_API_PORT=8888in the container env, the API inherits the Service port (8006) and misbinds. The upstream chart sets the same override. (Worker pins it to 8889 for the same reason.) - Control plane ignores
PORT. Thehindsight-control-planeimage binds its built-in default 9999 and ignores thePORTenv var (verified live 2026-08-28). ThePORTenv is kept for documentation only; Services must target 9999. - pgvector 0.8.0 (Crunchy) is NOT trusted.
vector.controllackstrusted = true, soCREATE EXTENSIONrequires superuser — the cert-onlyhindsightrole cannot create it. Fix: userpostgresis declared in the PostgresClusterspec.users, and a Flux-managed Jobhindsight-db-extensions(infrastructure/instances/tazlab-db/hindsight-db-extensions-job.yaml, usingtazlab-db-pguser-postgrescredentials) runsCREATE EXTENSION IF NOT EXISTS vector;idempotently, waiting up to 10 min for the DB. The API/workerensure-dbinit containers verify the extension and hard-fail if missing. - Non-root image uid/gid 1000. The Hindsight images run as uid 1000; mounted TLS secrets would be unreadable without group access. Fix: pod
securityContext.fsGroup: 1000(kubelet chgrps mounted files) + TLS secretdefaultMode: 0640(decimal416).
Hostname for sslmode=verify-full: the certificate SAN matches the short in-cluster name — use tazlab-db-primary.tazlab-db.svc (not the full .cluster.local FQDN) or the TLS verification fails.
Database Chain (PGO tazlab-db)
- PostgresCluster
tazlab-db: databasehindsight, userhindsightwithdatabases: [hindsight];pg_hbalineshostssl hindsight hindsight 0.0.0.0/0 cert(+ IPv6) — cert-only mTLS, no password auth. - Because PGO-owned databases keep
publicowned bypostgres(non-writable for app roles on PG16), Hindsight uses the dedicated schemahindsightviaHINDSIGHT_API_DATABASE_SCHEMA(Alembic-managed migrations);init-sql.yamladditionally alignspublicownership. - Client certificate: VaultPKISecret
db-client-hindsight-tls(below), renewed every 24h; both API and worker Deployments arerolloutRestartTargetsso pods pick up renewals.
VSO Secret Chain (infrastructure/operators/vso/)
Static secrets (Vault KV → K8s Secret):
| VaultStaticSecret | Vault path | K8s Secret | Consumers |
|---|---|---|---|
vaultstaticsecret-hindsight-gemini-key | tazlab-k8s/static/apps/hindsight/GEMINI_API_KEY | hindsight-gemini-key | api, worker |
vaultstaticsecret-hindsight-mcp-token | tazlab-k8s/static/apps/hindsight/MCP_AUTH_TOKEN | hindsight-mcp-token | api |
vaultstaticsecret-hindsight-cp-key | tazlab-k8s/static/apps/hindsight/CP_ACCESS_KEY | hindsight-cp-key | dashboard |
vaultstaticsecret-hindsight-opencode-key | tazlab-k8s/static/apps/hindsight/OPENCODE_API_KEY | hindsight-opencode-key | api, worker |
PKI recipe (reusable pattern for cert-only DB clients):
sa-vso-auth-sa-hindsight.yaml— VSO auth ServiceAccount in namespacehindsight.vaultauth-pki-db-hindsight.yaml— VaultAuthvso-pki-db-hindsight, Kubernetes auth rolevso-role-db-hindsight(referenced cross-namespace asvso-system/vso-pki-db-hindsight).vaultpkisecret-client-hindsight.yaml— VaultPKISecretdb-client-hindsight-tls:mount: pki_int,role: db-client-hindsight,commonName: hindsight,ttl: "24h",expiryOffset: "4h", destinationkubernetes.io/tlswithtransformation.templatesmappingtls.crt/tls.key/ca.crt(offline root CA pinned in the ca.crt template — the 2026-07-05 VSO template bug fix pattern),rolloutRestartTargets→hindsight-api,hindsight-worker.
Digest Pinning
All four images are digest-pinned (supply-chain doctrine, enforced by manifest-purity checks):
ghcr.io/vectorize-io/hindsight-api:0.9.2-slim@sha256:7635a15739361dbdf221ba796ad25a813f876144fe113022eea8e26cb6ee75e7(API + worker)ghcr.io/vectorize-io/hindsight-control-plane:0.9.2@sha256:d9bf57083f7cb0a53ee5011bdfde6aae2490dbf5c360d2d1bff10473b45eb112(dashboard)postgres@sha256:cf78e76683b9ca8c5733cbbdce6c9262b45b6767934dd0a95e671f9a0fc20685(init containers + extensions Job)
NetworkPolicy
| Policy | Selector | Allowed ingress | Port |
|---|---|---|---|
hindsight-default-deny-ingress | all pods | — (deny) | — |
hindsight-allow-api | app: hindsight-api | LAN 192.168.1.0/24, same-namespace pods, monitoring ns, ai-agents ns | 8888 |
hindsight-allow-dashboard | app: hindsight-dashboard | LAN 192.168.1.0/24 | 9999 |
hindsight-allow-worker-metrics | app: hindsight-worker | monitoring ns only | 8889 |
Exposure: both LoadBalancer Services share MetalLB IP 192.168.1.240 (metallb.universe.tf/allow-shared-ip: tazlab-internal-dashboard) — 8006→8888 (API/MCP, also used by LAN agents), 8007→9999 (dashboard). No Traefik ingress; access is LAN/tailnet-direct.
Operations Quick Reference
- Reconcile: Flux picks up
apps-data-hindsightautomatically; force withflux reconcile kustomization apps-data-hindsight. - Verify pods: all three Deployments ready in namespace
hindsight; theensure-dbinit container failing means thevectorextension is missing (gotcha 3) or the DB/cert chain is broken. - MCP token at runtime: fetch from the cluster without printing it (
kubectl -n hindsight get secret hindsight-mcp-token -o jsonpath='{.data.value}' | base64 -dinto a shell variable); never persist or echo it. - After a cert renewal: VSO rotates the 24h client cert and
rolloutRestartTargetsrestarts api/worker automatically; if a pod loops on TLS errors, check the secret’sca.crtmatches the current offline root CA.
See Also
- Parent topic: Hindsight Architecture
- Parent hub: hindsight
- Sibling details: tazlab-db Detail, External Secrets Detail, Mnemosyne Deployment Detail
- References: ExternalSecret variants