SpiceDB Check API: Consistency Trade-offs with PostgreSQL
Choose fully consistent Check reads for authorization and minimize-latency only for stale-tolerant UI hints, with PostgreSQL isolated and fail-closed on errors.
10 Oct 2026, 05:48 UTC

The problem
Your service must answer, again and again: can this subject perform this relation on this object? SpiceDB answers through its Check API, reading materialized relationships from a tuple store backed by PostgreSQL. The engineering decision is not whether to use SpiceDB, but how fresh each answer must be. A fully consistent check can fail when the database is slow or partitioned; a minimize-latency check can return a stale answer that grants access after revocation.
Useful takeaway
Default to fully consistent reads for any check that enforces access. Reserve minimize-latency reads for low-risk user-interface hints that the application can tolerate being wrong. Treat every Check API error as a denial, keep PostgreSQL network-isolated and encrypted, and validate schema changes before they reach production.
Requirements
- Low-latency authorization decisions at read time.
- High availability for the Check API path.
- Correctness: a revoked permission must not remain effective after the revocation write commits.
- PostgreSQL as the production datastore; the in-memory engine is for development only.
Smallest suitable design
Run one SpiceDB service connected to one PostgreSQL primary. Define only the object types and relations you actually need; over-modeling large fan-outs can make checks recursive and slow. Clients call CheckPermission and never evaluate permissions locally.
Example schema in SpiceDB definition language:
definition document {
relation viewer: user
relation editor: user
permission view = viewer + editor
}
Terms: an object type is the kind of resource, such as document. A relation is a named link between an object and a subject, such as viewer. A subject is the actor, typically a user. A tuple is one stored relationship, for example document:doc-123#viewer@user:user-456.
Client call with fully consistent consistency:
resp, err := client.CheckPermission(ctx, &permspb.CheckPermissionRequest{
Resource: &v1.ObjectReference{ObjectType: "document", ObjectId: "doc-123"},
Permission: "view",
Subject: &v1.SubjectReference{ObjectType: "user", ObjectId: "user-456"},
Consistency: &v1.Consistency{Requirement: &v1.Consistency_FullyConsistent{}},
})
Replace FullyConsistent with MinimizeLatency only when the caller can accept a cached or replica-stale answer. A Zed token is an opaque token the client sends back to bound staleness; it reduces but does not eliminate staleness in minimize-latency mode.
Trust and data boundaries
SpiceDB is the trusted authorization service. The application must not duplicate relation logic in application code or evaluate tuples on the client. PostgreSQL holds the tuple store, which can reveal who has access to what, so treat it as sensitive data.
Example datastore configuration fragment:
datastore:
engine: postgres
connection_uri: "postgres://spicedb:@:5432/spicedb?sslmode=require"
Run this configuration on the host deploying SpiceDB. The operator needs read access to the config file and network access to PostgreSQL. Replace and with values from a secret manager; do not commit real credentials to version control. Expected check: SpiceDB starts and its readiness probe can reach PostgreSQL. Risk: plaintext credentials in logs or shell history if the URI is passed on the command line.
Enforce TLS on API traffic and authenticate clients with pre-shared keys or OIDC. OIDC is an identity layer that lets SpiceDB verify tokens issued by an external identity provider.
Operational checks
- Check API latency percentiles and error rates.
- PostgreSQL connection pool saturation.
- Replication lag if read replicas are used.
- Cache hit ratios for minimize-latency reads.
- Schema migration outcomes and tuple validity after migration.
A readiness probe should verify that SpiceDB can reach its datastore; do not mark the service ready if PostgreSQL is unreachable.
Diagnostic decision example
if check_latency_p99 > 100ms or check_error_rate > 1%:
inspect PostgreSQL connection pool usage
check replication lag on read replicas
confirm the readiness probe can reach the datastore
Run these checks from your observability system or a local debugging session with access to metrics and the database. Required permissions: read access to metrics and PostgreSQL monitoring views. Risk: querying production PostgreSQL with expensive queries can add load during an incident.
Failure modes
| Mode | Guarantee | Typical use | Failure behavior |
|---|---|---|---|
| Fully consistent | Reflects all committed writes | Access enforcement, revocation | Datastore outage causes Check API errors; application should fail closed |
| Minimize latency | May serve cached or replica-stale results | UI hints, non-critical features | Partition can return stale grants or denials; correctness traded for availability |
Additional failure modes:
- PostgreSQL primary outage with fully consistent reads. Check calls error. The application should deny access rather than guess.
- Network partition with minimize-latency reads. SpiceDB may serve stale cache entries. A user removed from a relation may still appear authorized until the cache or replica catches up.
- Schema migration. Removing or renaming a relation can invalidate existing tuples and cause unexpected denials.
- Deep nesting or high fan-out. Relations that compute over many subjects can trigger recursive check explosions and latency spikes.
Rollback for schema changes
Writing a schema changes state. Keep the previous schema file available. If a migration causes denials or errors, reapply the last known-good schema with spicedb schema write. Run this on a host with API access to SpiceDB and valid client credentials. Do not delete tuples unless you have verified they are unused; restoring deleted tuples requires a backup.
Conditions that would change the design
- Sub-10 ms cross-region latency: consider multi-region SpiceDB deployment.
- Write throughput exceeds a single PostgreSQL primary: consider read replicas or sharding.
- Attribute-based access control is required: consider SpiceDB caveats or an external policy engine.
Practical verification
- Run a local SpiceDB instance with PostgreSQL and issue CheckPermission calls in both consistency modes to observe latency differences.
- Simulate a PostgreSQL outage and verify the application denies access when CheckPermission returns errors.
- Load-test a representative schema with expected tuple counts to validate Check API latency and PostgreSQL connection usage.
- Review the SpiceDB documentation for your deployed version to confirm supported datastores, consistency semantics, and cache behavior.
Verification status: this draft is based on general SpiceDB behavior and requires confirmation against your deployed version before production use. Consistency flags, datastore defaults, and cache semantics vary by release.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.