Skip to content

feat: store principal invalidation records - #10

Draft
pyropy wants to merge 1 commit into
srdjan/feat/iam-store-eventsfrom
srdjan/feat/iam-principal-records
Draft

feat: store principal invalidation records#10
pyropy wants to merge 1 commit into
srdjan/feat/iam-store-eventsfrom
srdjan/feat/iam-principal-records

Conversation

@pyropy

@pyropy pyropy commented Sep 10, 2026

Copy link
Copy Markdown

Description

RevocationStore gains AddPrincipalRevocation, recording that every proof a gateway cached for a principal's keys is void. Postgres reads both tables in one UNION ALL ordered by recorded_at, keeping a single horizon, cursor and dedup map. GET /revocation/:cid is unchanged. Part of the Forge S3 tenant IAM work (RFC).

Change log

  • AddPrincipalRevocation on the interface and both backends
  • Migration 00002: principal_invalidation table and index
  • One query over both tables in the stream poll
  • Tests: interleaved ordering, late arrivals inside the settle window, invalid input

🤖 Generated with Claude Code

RevocationStore gains AddPrincipalRevocation, which records that every
proof cached for a principal's keys is void. Both backends reject an
undefined tenant or an empty principal and otherwise mirror Add.

Postgres stores the records in a new principal_invalidation table
(migration 00002) and reads both tables in one UNION ALL ordered by
recorded_at and id, so the stream keeps one now() horizon, one cursor
and one dedup map across both kinds. The memory backend appends to the
same log Stream already reads. Get is unaffected: it reads only the
revocation table.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@pyropy
pyropy added this pull request to stack #12 September 10, 2026 16:19
@pyropy
pyropy force-pushed the srdjan/feat/iam-principal-records branch from 9507aca to bf74330 Compare September 11, 2026 12:40
@pyropy
pyropy requested a lite review from Copilot September 11, 2026 12:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical PostgreSQL streaming and firehose compatibility issues remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds principal invalidation persistence and unified streaming across memory and PostgreSQL backends.

Changes:

  • Extends the store API with AddPrincipalRevocation.
  • Adds PostgreSQL schema, storage, and unified polling.
  • Adds backend and validation tests.
File summaries
File Summary
pkg/store/store.go Extends the store interface.
pkg/store/postgres/store.go Persists and streams principal invalidations.
pkg/store/postgres/store_test.go Tests PostgreSQL behavior.
pkg/store/postgres/migrations/sql/00002_principal_invalidation.sql Adds the invalidation table and index.
pkg/store/memory/store.go Adds in-memory support.
pkg/store/memory/store_test.go Tests in-memory behavior.
pkg/fx/app_test.go Updates the test store implementation.
Review details

Suppressed comments (1)

pkg/store/postgres/store.go:208

  • The UNION now permits the same invocation CID to exist once in each table: the primary keys are table-local and both Add methods accept the same invocation. However, the shared seen map below is keyed only by rec.Cause().Link(), so recording both effects with one invocation silently drops the second event even though Stream promises all records. Key deduplication by record kind plus cause, and prune the same composite key.
			`SELECT kind, cause, revoked_delegation, path_witness, tenant, principal, recorded_at
  • Files reviewed: 7/7 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/store/memory/store.go
s.mu.Lock()
defer s.mu.Unlock()
record.RecordedAt = time.Now() // under the lock, as in Add
s.appendLocked(store.PrincipalRevocationEvent(record))
FROM principal_invalidation
WHERE recorded_at >= $1
) AS record
ORDER BY recorded_at, id`,
yield(store.Event{}, fmt.Errorf("decoding principal invalidation: %w", err))
return
}
event = store.PrincipalRevocationEvent(record)
Comment thread pkg/store/store.go
Comment on lines +105 to +108
// AddPrincipalRevocation adds a principal invalidation record to the store.
// The invalidation is the invocation that voids every proof cached for the
// principal's keys; tenant must be defined and principal must not be empty.
AddPrincipalRevocation(ctx context.Context, invalidation ucan.Invocation, tenant did.DID, principal string) error
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.

2 participants