English source · 5 min
Authenticated database scope
In enforced signed mode, the ordinary shop connection cannot promote itself by setting rac.system or
changing rac.tenant. Native identity, worker and schema owners use a separate
restricted system connection. Both remain subject to forced PostgreSQL row
security; the migration login is absent from an ordinary serving process.
flowchart LR
Request[HTTP / MCP request] --> Admission[Current identity and permissions]
Admission --> Shop[Shop transaction]
Admission --> Native[Trusted native identity / schema owner]
Shop --> Tenant[Restricted tenant login]
Native --> System[Separate system login]
Tenant --> Guard[Role-bound signed scope + existing row policies]
System --> Guard
Guard --> Database[(PostgreSQL)]
Migration[Separate migration job] --> Database
What is enforced
scoped_pool begins a transaction, binds its admitted tenant/actor and sends an
HMAC scope ticket through a bound parameter. The ticket is absent from SQL query
text in pg_stat_activity. Its signature binds the real database login, tenant,
actor, personal-session classification, key ID, expiry and nonce. Changing a GUC,
using an expired ticket or replaying a system ticket through the tenant login
does not widen access. Scope is transaction-local, including transaction pooling,
cancelled streams and connection reuse.
Migration 099 adds a restrictive authenticated policy alongside existing row
policies. Their conditions combine; existing domain and actor policies remain.
Private Studio workspace rows additionally require the signed personal actor.
An integration credential has a distinct actor classification and cannot assume
its creator's personal workspace. The fixed app models/records/reference/index tables receive the same guard and
scoped grants in migrations 101–102; installation no longer creates tables. The tenant login owns
no app table and cannot create/drop tables or disable row security.
It also cannot create temporary objects that shadow native table names on a reused
backend. Configuration removes the database's default PUBLIC temporary-object
grant and retains it only for the trusted system/migration logins; startup checks
the tenant login's effective database/schema privileges, including inherited grants.
The tenant login cannot read global sessions, MFA, identity-provider bindings or platform AI secrets. Merchant identity joins expose only member ID/name/email columns under the current tenant. The language registry is a shared canonical reference: a narrowly bounded native function registers deterministic language IDs without allowing arbitrary global writes. Native inference resolves platform credentials through its trusted owner; credentials never become tenant API data.
Connector tables retain their independent service role and queue owner. Core's tenant login has no connector-table grants. This change does not authorize apps to issue SQL or claim containment of arbitrary native service code.
PostgreSQL explains the relevant boundaries in its row security guide, restrictive-policy reference and secure function guidance.
Coordinated deployment
- Apply migration 099 in the existing owner migration job. It initially stages the additional guards so old running replicas keep working. It does not create a public endpoint, a password or another authoritative queue.
- Prepare two distinct restricted login roles. The existing runtime login can remain the system login; it must not own core tables, inherit their owner or have superuser, RLS bypass or TRUNCATE privileges. A new tenant login must own no relations, inherit no roles and have no database/schema creation privileges. Passwords belong exclusively in deployment secrets.
- From the migration job run
veyndo --database-authority configure SYSTEM_LOGIN TENANT_LOGIN. This grants the selected tenant login only the scoped tables, safe reference reads and safe identity columns. Fixed app storage stays owned by migration authority; native system scope manages logical definitions without app DDL. On PostgreSQL 17 the migration login needsADMIN OPTIONon the system-role membership it maintains; this does not grant the serving role migration rights. A separate database owner must remove the database's defaultPUBLIC TEMPORARYgrant if the migration login cannot revoke it. PostgreSQL can emit a warning rather than failing that REVOKE: verify the tenant's effectivehas_database_privilege(TENANT_LOGIN,current_database(),'TEMPORARY')is false. Do not bypass the startup audit or grant database ownership to the runtime. - Set
BOOTSTRAP_MODE=serve,DB_RLS_REQUIRED=true,DB_SCOPE_REQUIRED=true,DATABASE_RUNTIME_URLto the restricted system connection andDATABASE_TENANT_URLto the tenant connection. Do not supply the migrationDATABASE_URLto the serving container. UseDB_SCOPE_STAGED=trueonly during this coordinated rollout. The two pools shareDB_POOL_MAX(minimum 4), rather than doubling the process/fleet connection budget. - Verify every replica and worker uses the new code and connections. Upgrade
the connector to its gateway/FK-based admission with no Core-table reads first;
Storyfront and Experience keep separate service roles and call commerce APIs.
Review actual database consumers, not only HTTP health. Then run
veyndo --database-authority activatethrough the migration job and removeDB_SCOPE_STAGED. Startup rejects missing enforcement, role escalation, privileged identity grants or tenant tables lacking their restrictive guard. A staged deployment is not yet the enforced security boundary.
The CLI's status command returns enforcement state and role names, never keys.
New security-sensitive production access uses the normal explicit provisioning
approval; local fixture success is not evidence that production was switched.
The hosted cutover on 15 October is now complete: native status reports
enforced=true, the staged exception is disabled, Core passed its strict restart,
and the connector's obsolete Core-table read grant is absent. A new simulated
purchase, Studio session reload/order read and a saved read-only agent task passed.
See the dated evidence and explicit test limits.
Rotation and verification
veyndo --database-authority rotate creates a fresh database-only signing key.
Running servers refresh every minute. Tickets live for five minutes; a retiring
key has a ten-minute verification overlap. A failed refresh cannot remove row
security: signatures eventually expire and access fails closed. Stored signing bytes are
zeroized on replacement/drop and excluded from debug output. This key is specific
to database scope; provider encryption and browser authentication have separate
owners and are not implied repaired by it.
signed_runtime repeats the existing two-replica production-foundation flow with
actual separate logins, restart, order/inventory, native documents, current rights,
outbox and app/schema/staging paths. Its SQL adversary connects as the tenant
login, rather than using SET ROLE on a migration session. It checks forged tenant
and system values, role-bound/expired signatures, private actor substitution,
identity/key access, DDL denial and key rotation after retiring-key expiry.
signed_pooler repeats the same focused flows through two actual PgBouncer
transaction pools, one backend per login, while notification listeners retain
the direct system connection. No pool is configured to force a privileged user.
The canonical HTTP suite registry is also repeated against the signed runtime. Fixtures owning another fresh database (offline setup and document providers) retain their isolated bootstrap; translations run as their separate provider fixture. Existing pure Lean policies remain unchanged; PostgreSQL, transport, signature integration and deployment remain tested implementation, not a whole-system formal proof.