Operations & observability
This page is what an operator reads from a running App: readiness, a support bundle, and local HTTP diagnostics. Nothing is sent anywhere — no collector, no telemetry.
auth: { roles: { member: { read: true, write: true }, operator: { read: true, operations: ['readiness', 'bundle'] }, },},Operator reads
Section titled “Operator reads”operations is a grant of its own; no data or command grant implies it.
'readiness' opens GET /api/operations/readiness and 'bundle' opens
GET /api/operations/support. Replies are Cache-Control: no-store; failures
are a bare invalid_request (400), not_authorized (403) or unavailable
(503). An App that signs people in is required: the standalone database
browser serves neither route.
Readiness
Section titled “Readiness”An aggregate ready flag and named checks, each an allowlisted state and
code, never an exception or driver message:
| Check | Present | Not ready or degraded |
|---|---|---|
listener |
always | — |
sources |
always | not_ready, connection_unavailable |
policy |
with --policy |
not_ready, configuration_invalid |
diagnostics |
with TABLEWALK_DIAGNOSTICS=1 |
degraded, diagnostics_degraded |
pools |
once a PostgreSQL pool opens | degraded, pool_saturated |
jobs, events.delivery |
with --jobs |
degraded: backlog_pressure, work_failed; not_ready: worker_halted |
effects |
when a command declares effects | degraded: work_failed; not_ready: worker halted |
Nothing probes a database; ready is not a capacity claim. Load balancer
health checks use /api/app (or /api/whoami for an App that signs people
in).
The support bundle
Section titled “The support bundle”Readiness, plus HTTP metrics when diagnostics are on, plus a capacity section. It never contains App or connection names, paths, SQL, record values, credentials or the caller’s identity.
The capacity section
Section titled “The capacity section”capacity answers “is it the process, the pool, or the database?”. Its
counters are cumulative since start, so compare two bundles.
eventLoop: cumulativeactiveMsandidleMs.pools: per PostgreSQL pool role (named bypositionin the config, never by name or URL):size,open,inUse,waiting,saturated, and cumulativecheckouts,queued,timedOut,failed,waitBuckets,holdBuckets.durationBoundsMs: the buckets’ bounds.
Reading the pool meters says which pattern means add pods, raise a pool, or look at the database.
HTTP diagnostics
Section titled “HTTP diagnostics”TABLEWALK_DIAGNOSTICS=1 npx tablewalk --app ./myappEach response carries an X-Request-ID, and each request ends as one JSONL
http.request event on stderr: time, id, method, a fixed route category
(api.query, web, never the URL), status, duration and whether it
completed. At shutdown an http.metrics summary adds latency buckets. A 200
can still carry a refused save, so judge outcomes by the write result, not the
status. MCP is not instrumented.
docs/observability.md
has the full contract.
Jobs and deliveries
Section titled “Jobs and deliveries”Failed jobs and webhook deliveries wait for an operator. There is no HTTP
write surface; run these from tablewalk/maintenance:
| Jobs | Event deliveries |
|---|---|
inspectJobs, inspectJobAttempts |
inspectEventDeliveries |
redriveJobs |
redriveEventDeliveries |
pruneJobs |
pruneEventDeliveries |
A redrive puts failed or unknown work back in line and records the operator. A worker that halts stays halted until the process restarts. Stores are provisioned before start; see events and jobs.