Repository navigation
ctx v5.30.0
v5.30.0 — telemetry foundation for the OpenAI-compatible API (RC-A): llmlog write path, consumer_id, hourly usage rollup, rollup readers, audit trail, foreign-load detector
Release cut RC-A of the OpenAI-compatible API plan (design/06 block A). No /v1
surface yet; everything here is self-contained and useful on its own.
Migrations 158–160:
- 158 context_llm_log.consumer_id (nullable, no FK), CHECK ck_llm_log_one_identity
(at most one of api_key_id / consumer_id), partial index idx_llm_log_consumer.
Boot cost measured: ≈ 0.75 s at 1 M rows warm, ≈ 1 s/GB cold. - 159 context_llm_usage_hourly: hourly rollup of every llmlog row kept by an
AFTER INSERT trigger that never raises, three log2 histograms, backfill in the
same transaction (≈ 8 s per 1 M rows at boot, ≈ 3 s on the live table). - 160 audit trail for knowledge keys and tenant quota: whitelist state functions,
audit_actor_meta (actor, via, request id, actor tenant), UPDATE trigger only on
named columns (a request's last_used_at write never audits), snapshot backfill
(12 rows on the live install), actor/via stamps in every writer.
Daemon:
- llmlog writer: bounded per-pool queue (≤ 4 concurrent inserts, ≤ 4 096 queued),
loss counters on /api/status ("llmlog": {insert_failures, insert_dropped},
server-admin only), throttled WARN. Shutdown settles the queue before and after
the scheduler drain; docker-compose.yml sets stop_grace_period: 30s on ctx —
operators with their own compose file must add it (docs/operations.md). - Tenant spend (daily_calls, cost budgets) and the /api/status llm_24h rollups read
the hourly rollup plus the edge hour instead of scanning the hypertable; figures
are byte-equal to the raw aggregates; a /v1 consumer's rows never count against
a tenant's internal quota (K17). - GET /api/audit: the history of api keys and tenant quota (and, once their
writers land, gateway limits, model consumers/keys/grants, consumer usage,
instance) for server-admins and tenant-admins. A tenant-admin reads only rows
of its own tenant, decided in SQL; foreign and server-admin actors are masked,
a key's is_admin/principal_id never shown, and an update whose only change
lay in those fields is left out. New setting audit.max_limit (default 200). - Foreign-load detector (design/06 §4.9, board amendment A-E5): a 'global'
backend row with metadata.foreign_load_probe = "sglang" makes the status
collector read the engine's own GET /metrics off the status path and compare,
per round, max(running, in-flight HTTP) against the dispatcher's held leases
(suspected after N consecutive rounds, symmetric hysteresis) and, per hour,
the engine's finished responses against the usage rollup (suspected_hourly).
/api/status "foreign_load", server-admin only; four settings
status.foreign_load{timeout,hysteresis_ticks,hourly_min_abs,hourly_min_ratio}.
Rounds follow the status collector: without a poller or the SSE loop nothing
is measured. Activation on the live install is an operator step after deploy.
Upgrade notes: docs/operations.md "Migration 158", "Migration 159", "Migration 160",
"Shutdown and the LLM-log write path", "Foreign load detector".
Installation
With Go:
go install github.com/GottZ/ctx/cmd/ctx@v5.30.0Binary download:
Download the binary for your platform (and SHA256SUMS), verify both, then install:
gh attestation verify ctx-<os>-<arch> -R GottZ/ctx # signed build provenance
# offline: add --bundle ctx.intoto.jsonl (Sigstore bundle attached to this release)
sha256sum -c --ignore-missing SHA256SUMS # download integrity
chmod +x ctx-*
sudo mv ctx-* /usr/local/bin/ctx