Fix: configure postgrest cannot tell "configured" from "was configured once"
IMPLEMENTATION RULES: Before implementing this plan, read and follow:
- WORKFLOW.md - The implementation process
- PLANS.md - Plan structure and best practices
Status: Backlog
Goal: A PostgREST instance whose database no longer carries its grants is detected, rather than reported as configured.
Created: 2026-08-22, while writing
the PostgREST verify playbook
(commit f2bd3a2).
Priority: Medium — the failure is silent and the platform reports success throughout, which is the expensive kind.
Problem
uis configure postgrest --app <name> decides whether work is needed by checking
the Kubernetes secret:
PostgREST already configured for 'testapp' (schemas: api_v1). To proceed:
./uis deploy postgrest --app testapp
PostgREST already configured for 'testapp' with schemas 'api_v1' — nothing to do.
The secret lives in the postgrest namespace. The grants live in PostgreSQL.
Those two can come apart, and when they do nothing notices.
Reproduction (observed 2026-08-22)
configure+deployan instance — works, API serves the schema- Drop and recreate the database (an app team resetting, a restore from backup, a re-bootstrap — all ordinary)
configureagain → "already configured — nothing to do",exit=0deploy→exit=0, pods1/1 Running- Every API call:
permission denied for schema api_v1
Confirmed the grants were absent: information_schema.role_table_grants showed
only postgres, no web_anon. GRANT USAGE ON SCHEMA had never run against the
new database.
Nothing in that sequence reports a problem. configure says configured,
deploy says deployed, uis status says Healthy, the pod is Ready. The only
symptom is that the API refuses every request.
What is NOT wrong
Worth stating, because the first reading of this was harsher and wrong:
--purgeworks. It recovers the instance correctly.--purgerefuses while a Deployment exists, and says exactly what to run first. That ordering is right.- Skipping work when nothing changed is correct behaviour. The defect is not
that
configureis idempotent; it is that its idempotency key is the wrong object.
The gap is detection, not recovery.
Phase 1: Check the thing that matters
Tasks
- 1.1 Before declaring "already configured", verify the grants are actually
present — the role exists, has
USAGEon each configured schema, andSELECTon its tables.configure-postgrest.shalready builds exactly this GRANT block, so the check is the same query in reverse - 1.2 On drift, do not fail: re-apply the grants and say so. The secret is still valid; only the database side is missing. Re-granting is idempotent
- 1.3 If the database or schema is gone entirely, fail with the existing style of message — those already exist and are good
- 1.4 Keep the fast path fast: when everything matches, still say "nothing to do"
Validation
./uis configure postgrest --app x --database x --schemas api_v1
# drop and recreate database x
./uis configure postgrest --app x --database x --schemas api_v1
# -> reports drift, re-applies grants
./uis verify postgrest --app x # passes
Phase 2: Make the running instance say so
Configure-time detection only helps if someone runs configure. An instance can drift while deployed.
Tasks
- 2.1 088-test-postgrest.yml
already catches this — test B fails with the API's own
permission denied for schemain the message. Confirm the failure text names the likely cause rather than only the symptom - 2.2 Consider whether
SCRIPT_CHECK_COMMANDfor postgrest should ask the API rather than the Deployment. A Ready pod that 403s everything currently reports Healthy inuis status
Validation
An instance with missing grants is reported unhealthy by something other than a user's failing request.
Acceptance Criteria
-
configuredetects missing grants and re-applies them - The fast path still short-circuits when nothing has drifted
- A drifted instance is visible without running
verifyby hand -
--purgebehaviour is unchanged
Implementation Notes
The general shape, which is not specific to PostgREST: an idempotency check
that reads a marker rather than the state the marker stands for. The secret
is a receipt, not the thing bought. Two other instances of the same shape are
already filed — INVESTIGATE-secrets-dev-to-production (validation is a
hardcoded allowlist rather than a check against the template) and
PLAN-cli-verify-registration-fix (registration in one list implies registration
in the others).
A related discovery worth keeping: PostgREST caches its schema at startup, so
a table an app adds by migration is invisible until the cache reloads. Same
signature — pod Ready, health checks green, API 404s the table. The verify
playbook handles it with NOTIFY pgrst, 'reload schema'; whether UIS should
signal a reload after an app migrates is an open question, and probably belongs
to whatever owns app migrations rather than here.
Files to Modify
provision-host/uis/lib/configure-postgrest.sh— the already-configured checkprovision-host/uis/services/integration/service-postgrest.sh— possiblySCRIPT_CHECK_COMMAND