Production¶
What a production Venkai deployment has, and — just as usefully — what it does not.
Readiness checklist¶
[ ] docker build succeeds (prove the artefact, don't assume it)
[ ] VENKAI_JWT_SECRET set, ≥32 bytes, stored in a secret manager
[ ] POSTGRES_PASSWORD set, not reused anywhere else
[ ] VENKAI_ENV=production → rate limiting + Secure cookies
[ ] VENKAI_ALLOWED_ORIGINS set to your dashboard origin only
[ ] VENKAI_EMBEDDING_PROVIDER chosen deliberately
[ ] .env confirmed gitignored (git check-ignore -v .env)
[ ] Alembic migration run explicitly
[ ] Port 8100 NOT published to the host
[ ] TLS verified from outside the host
[ ] /api/health returns {"status":"ok","database":"ok"} externally
[ ] Full write→read round trip verified with a real API key
[ ] Database backup scheduled AND a restore tested
[ ] Alerting on /api/health
Configuration¶
VENKAI_ENV=production
VENKAI_JWT_SECRET=<from your secret manager>
POSTGRES_PASSWORD=<from your secret manager>
VENKAI_ALLOWED_ORIGINS=https://app.your-domain
VENKAI_EMBEDDING_PROVIDER=semantic
VENKAI_ENV=production is load-bearing: it is what turns on rate limiting and
the Secure flag on session cookies. A deployment left at the development
default is publicly reachable with no rate limiting at all.
Secrets¶
| Secret | Rotation |
|---|---|
VENKAI_JWT_SECRET |
Logs every user out. Treat as permanent once a pilot starts. |
POSTGRES_PASSWORD |
Rotate with a coordinated database + app restart |
| API keys | POST /api/auth/api-keys/{id}/rotate — no grace period, deploy the new key first |
Keep them in a secret manager, injected at runtime. .env on the host is
acceptable for a small deployment provided file permissions are tight and it is
never committed.
Persistence¶
| Store | PostgreSQL. SQLite is a development convenience, not a production store. |
| Retention | None. Memories persist until edited or the org is deleted. |
| Growth | Linear in memories written. ~7.5 KB per memory of JSON embedding at 384-d. |
| Backups | Yours to schedule. See Self-hosting. |
Watch project size: retrieval is O(memories in project) and
VENKAI_RETRIEVAL_MAX_CANDIDATES (10 000) truncates oldest first — losing
exactly your foundational constraints.
curl -s https://your-domain/api/projects -H "Authorization: Bearer $KEY" \
| python -c "import json,sys; [print(p['contexts'], p['project_id']) for p in json.load(sys.stdin)['projects']]"
Alert at ~7000.
Health and monitoring¶
GET /api/health is a readiness probe — it opens a database connection.
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8100/api/health"]
interval: 30s
timeout: 5s
retries: 3
| Signal | Source |
|---|---|
| Availability | /api/health from outside the host |
| Request volume by kind | GET /api/usage — middleware-counted, real traffic |
| Store growth | GET /api/projects → contexts |
| Candidate truncation | Application logs — the only place it appears |
| Retrieval latency | Client-side timing; the API does not expose it |
There is no metrics endpoint: no Prometheus scrape target, no
/metrics. Instrument at your reverse proxy or in your client.
Logging¶
Structured events are emitted for writes and retrievals with content hashed, never logged — digests of project and agent ids, type, character counts, estimated tokens. Volume is measurable without being readable.
Log retention, shipping and rotation are yours. PYTHONUNBUFFERED=1 in the
image means logs reach your collector promptly.
Security posture¶
What is in place:
- Organization scoping enforced twice — in the query, then per candidate.
- API keys stored as hashes; the raw value is returned once and never again.
404rather than403for other tenants' resources, so responses cannot confirm that an id exists.- Rate limiting per IP in production.
- CORS restricted to
VENKAI_ALLOWED_ORIGINS. - Port 8100 unpublished, so
X-Forwarded-Forcannot be spoofed past Caddy. - Embeddings computed locally; content never leaves your infrastructure.
What is not:
| Absent | Implication |
|---|---|
| Per-key scopes or permissions | Any key can read and write everything in its org |
vk_test_ isolation |
A test key reads and writes real data |
| Audit log of reads | Writes leave a trail; reads are counted, not logged |
| Encryption at rest | Whatever your database provides |
| IP allow-listing | Do it at the proxy |
| SSO / SCIM | Not implemented |
Scaling¶
The API is stateless — run several behind a load balancer. In-process rate limit counters are per instance, so N instances means roughly N × the limit; enforce at the proxy if the number must be exact.
The real ceiling is memories per project, not requests. When retrieval latency
becomes a problem, the answers in order are: split projects, raise the candidate
cap knowingly, then migrate to pgvector with an ANN index
(Planned).
Not production-ready¶
Do not build a production dependency on these:
| Feature | Status |
|---|---|
/api/security/analyze, venkai_security_gate |
Experimental — unvalidated scoring; do not use as a blocking CI gate |
/api/handoffs |
Experimental — a record, not a mechanism |
venkai_client_accept / _deliver |
Experimental |
| Published benchmarks | Planned — Evaluation |