Skip to main content

Authorization with SpiceDB

Relationship-based authorization for Cuitty Code repositories, apps, and Registry integration.

Cuitty Code uses SpiceDB for relationship-based authorization across repositories, organizations, apps, installs, and package integration points. The database remains the source of metadata, while SpiceDB answers permission checks. Standalone Cuitty Registry authorization is documented separately in Registry authorization.

Required services

  • Cuitty Code API.
  • SpiceDB reachable from the API.
  • Database with the authz_outbox migration applied.
  • A worker path that drains relationship writes from the outbox.

Environment variables

Production deployments should resolve the preshared key through Cuitty Safe:

SPICEDB_ENDPOINT=https://spicedb.example.com:50051
SPICEDB_PRESHARED_KEY=cuitty-safe:acme/prod/spicedb-preshared-key
AUTH_ISSUER=https://auth.example.com
AUTH_CLIENT_ID=cuitty-git

For an isolated local development SpiceDB only, use a disposable placeholder value:

SPICEDB_ENDPOINT=http://localhost:50051
SPICEDB_PRESHARED_KEY=<local-dev-only-spicedb-key>
AUTH_ISSUER=http://localhost:7705
AUTH_CLIENT_ID=cuitty-code

Use TLS and Safe-managed secret references for production SpiceDB deployments. Do not promote the local placeholder into shared env files, CI, or self-hosted production configuration.

Schema and object types

Load the schema before API writes:

zed schema write spicedb/schema.zed

Representative object types include:

  • user
  • organization
  • org_invite
  • account_slug
  • repository
  • app_listing
  • app_installation
  • code_app
  • code_app_installation
  • code_app_grant
  • package
  • code_package
  • registry_namespace when Registry integration is enabled
  • registry_package when Registry integration is enabled

Verify representative permissions with known local data:

zed permission check user:alice read repository:acme/demo
zed permission check user:alice view package:acme/npm/acme-widget
zed permission check user:alice install app_listing:acme/review-bot

Permissions model

Security-expanding writes, such as creating public visibility, granting install permission, or publishing package access, should fail closed if SpiceDB writes cannot be committed. Security-reducing writes, such as revoking access or making a package private, should continue to be retried until the graph reflects the database state.

Private apps and packages may return 404 to callers without view permission. Authenticated callers with partial permission may receive 403 for actions such as publish, install, or settings updates.

Outbox statuses

Authorization relationship writes are recorded in authz_outbox with these statuses:

  • pending: queued for dispatch.
  • processing: claimed by a dispatcher.
  • succeeded: written to SpiceDB.
  • failed_retryable: failed but should be retried.
  • failed_terminal: failed in a way that needs operator inspection.

Inspect stuck rows directly when operational tooling is not available:

SELECT id, operation_kind, resource_kind, resource_id, status, attempts, last_error
FROM authz_outbox
WHERE status IN ('pending', 'processing', 'failed_retryable', 'failed_terminal')
ORDER BY created_at ASC
LIMIT 50;

Replay and reconciliation

Replay retryable relationship writes with the deployment’s authz dispatcher or maintenance command. If the deployment exposes only SQL access, mark stale processing rows back to pending after confirming no dispatcher is running, then restart the API or worker that drains the outbox.

UPDATE authz_outbox
SET status = 'pending'
WHERE status = 'processing'
  AND updated_at < datetime('now', '-15 minutes');

After replay, spot-check resource permissions with zed permission check and compare database visibility against SpiceDB relationships.

Failure modes and recovery

  • SpiceDB unavailable: pause security-expanding app, package-target, and transfer writes; restore SpiceDB; drain the outbox.
  • Schema not loaded: re-run zed schema write spicedb/schema.zed and retry failed rows.
  • Retryable backlog: inspect last_error, fix connectivity or schema drift, and replay.
  • Terminal failures: compare payload_json with the current schema and repair the row or resource manually.