Skip to content

fix(persistence): store full capability token payload as JSONB#173

Open
ExpertVagabond wants to merge 1 commit intosint-ai:mainfrom
ExpertVagabond:fix/pg-token-store-lossless
Open

fix(persistence): store full capability token payload as JSONB#173
ExpertVagabond wants to merge 1 commit intosint-ai:mainfrom
ExpertVagabond:fix/pg-token-store-lossless

Conversation

@ExpertVagabond
Copy link
Copy Markdown
Collaborator

Closes #169.

Problem

PgTokenStore.rowToToken materialized only 11 scalar/JSON columns from sint_tokens, silently dropping every optional field added to SintCapabilityToken after the initial 002 schema:

  • `modelConstraints`
  • `attestationRequirements`
  • `verifiableComputeRequirements`
  • `executionEnvelope`
  • `behavioralConstraints`
  • `passportId`
  • `delegationDepth`
  • `revocationEndpoint`

After a `store → get` roundtrip, the reconstructed token no longer matched the canonical signing payload, so Ed25519 verification against the persisted copy would fail — exactly the class of bug #166 / PR #168 fixed at the canonicalization layer, now reappearing at the persistence boundary.

Fix

Persist the full canonical token as a single `payload` JSONB column and keep a small set of denormalized scalars (`token_id`, `subject`, `issuer`, `resource`, `expires_at`) for indexed lookup only. New optional fields on `SintCapabilityToken` now roundtrip without any schema churn.

Changes

  • `migrations/003_tokens_payload.sql` — forward migration: add `payload`, backfill from legacy columns with `jsonb_build_object`, drop legacy columns, add issuer/resource indexes.
  • `src/pg-schema.ts` — `ensurePgSchema` emits the new layout on fresh installs and self-heals 002-era databases in place (same steps as the migration, wrapped in a guard).
  • `src/pg-token-store.ts` — `store`/`get`/`getBySubject` read and write the `payload` column exclusively.
  • `tests/pg-stores.test.ts` — replaces hand-loaded migration SQL with `ensurePgSchema`, plus two regression tests:

Test plan

  • `pnpm --filter @pshkv/persistence build` — clean
  • `pnpm --filter @pshkv/persistence test` — 27 passed, 29 skipped (PG/Redis require `DATABASE_URL`/`REDIS_URL`)
  • Maintainer-side: run PG suite against a live database to exercise migration 003 and the regression tests

…sint-ai#169)

PgTokenStore previously materialized only 11 scalar/JSON columns, silently
dropping every optional field added to SintCapabilityToken after the initial
schema (modelConstraints, attestationRequirements, verifiableComputeRequirements,
executionEnvelope, behavioralConstraints, passportId, delegationDepth,
revocationEndpoint). After a store -> get roundtrip the reconstructed token
no longer matched the canonical signing payload, so Ed25519 verification
against the persisted copy would fail.

Fix: persist the full canonical token as a single JSONB payload column and
keep a small set of denormalized scalars (token_id, subject, issuer, resource,
expires_at) for indexed lookup only.

- migrations/003_tokens_payload.sql: forward migration with backfill
- pg-schema.ts: emits new layout + self-heals legacy installs in place
- pg-token-store.ts: reads/writes payload JSONB exclusively
- pg-stores.test.ts: regression tests covering every optional field and
  nested constraint shapes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PgTokenStore.rowToToken drops 8 optional capability token fields

2 participants