Skip to content

docs(authz): Wave 0 permission storage DDL spike - #666

Merged
adlerhurst merged 3 commits into
mainfrom
cursor/permission-storage-wave0-6e44
Aug 7, 2026
Merged

docs(authz): Wave 0 permission storage DDL spike#666
adlerhurst merged 3 commits into
mainfrom
cursor/permission-storage-wave0-6e44

Conversation

@adlerhurst

@adlerhurst adlerhurst commented Jul 30, 2026

Copy link
Copy Markdown
Member

Summary

Adds the Wave 0 design spike for portable FGA relational storage (ADR 032–033 / epic #419): locked decisions D1–D14, Postgres/Spanner-oriented DDL strawman, dual-write membership edges, check/list SQL sketches, end-to-end narrative, and a #333 cross-project grant depiction (foreign user/team principal on the protected project_id).

Links the doc from the API design index and points ADR 032/033 follow-ups at it so #422 can implement migrations without re-litigating the schema shape.

Validation

  • Doc review against plan acceptance criteria (D1–D14 present, dual-write + [ARCH-ADR] Define Architecture for Cross-Project Identity and Collaboration #333 depiction, deferrals for Leopard / partitioning / app grants)
  • Addressed thermo-nuclear review P1–P8: one check SQL shape with $principal_home_project_id, relations+closure only (D14), tighter DDL CHECKs, authz.md / ADR follow-up alignment
  • Docs-only change; no runtime or migration code

Release notes / changeset

No changeset required — no shipped behavior changed (docs only).

Notes

Open in Web Open in Cursor 

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nextgen Ready Ready Preview Aug 7, 2026 2:40pm
nextgen-docs Ready Ready Preview Aug 7, 2026 2:40pm
nextgen-mock-zitadel Ready Ready Preview Aug 7, 2026 2:40pm

Request Review

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

⚠️ No Changeset found

Latest commit: e4e7adc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

cursor Bot pushed a commit that referenced this pull request Aug 5, 2026
Co-authored-by: Silvan <adlerhurst@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 5, 2026
Co-authored-by: Silvan <adlerhurst@users.noreply.github.com>
@adlerhurst adlerhurst moved this from Inbox to In review in Engineering Kanban Aug 6, 2026
@adlerhurst adlerhurst self-assigned this Aug 6, 2026
@adlerhurst
adlerhurst marked this pull request as ready for review August 6, 2026 06:10
@adlerhurst
adlerhurst requested review from Copilot and livio-a August 6, 2026 06:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new API design doc that captures the Wave 0 “permission storage” DDL spike for the portable relational FGA core, then threads that reference into the API design index and ADR 032/033 follow-ups to anchor future implementation work (notably #422 migrations).

Changes:

  • Introduces docs/design/api/permission-storage.md documenting locked DDL/storage decisions (D1–D13), strawman schema, and query sketches.
  • Links the new design doc from docs/design/api/README.md to include it in the suggested reading order.
  • Updates ADR 032 and ADR 033 follow-ups to point implementers at the Wave 0 storage doc and #422.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
docs/design/api/README.md Adds the new permission-storage doc to the API design index/reading order.
docs/design/api/permission-storage.md New Wave 0 storage/DDL spike doc with locked decisions, schema strawman, and illustrative SQL.
docs/adrs/033-internal-permission-management.md Follow-up section now references the Wave 0 storage doc and links to #422.
docs/adrs/032-permission-catalogs.md Follow-up section now references the Wave 0 storage doc and links to #422/#333 context.

Comment thread docs/design/api/permission-storage.md

@livio-a livio-a left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review: Wave 0 permission storage DDL spike — reviewed alongside the rest of the stack (#677, #758).

Well-reasoned spike at the right altitude — freezing tables/PKs/indexes/dual-write rules as locked decisions D1–D13 is exactly the right output for Wave 0, and the implementation in #677 tracks it closely. Two notes, both doc-level:

  • This doc is partially superseded by #677 and reads that way in isolation. #677 reverses D5 (it does create authz_expression_edges + authz_relation_references, which D5 here says to defer until #421) and changes relation identity to (catalog_id, object_type, relation). #677 updates permission-storage.md to match, so head stays consistent — but a reader landing on this PR's version sees the pre-revision D5/identity. Worth a one-line "superseded by #677 for D5 / relation identity," or squashing the doc evolution so the frozen decisions and their revisions land together.

  • [NIT] Illustrative check SQL contradicts the D13 home-project note. The single-resource check example hardcodes e.project_id = a.project_id for the team-membership EXISTS, while the D13 discussion a few lines down states membership edges for a foreign team principal live in the principal's home project, not a.project_id. It's labeled illustrative, but the two passages read as directly contradictory — a half-sentence caveat on the example would remove the trap for whoever writes the Wave 1 resolver.

@livio-a livio-a left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving — Wave 0 design is solid and the earlier review feedback is addressed.

One small note, non-blocking: the newly-declared authz_membership_edges (project_id, set_id) → teams FK is the frozen-schema authority, so #677's migration should match it (flagged there). If it's meant to stay app-enforced for MVP, drop the line here instead so doc and migration agree.

cursoragent and others added 3 commits August 7, 2026 16:39
Capture locked schema decisions (D1–D13), dual-write membership edges,
and a #333 cross-project grant depiction so #422 can implement migrations
without re-litigating the relational shape.

Co-authored-by: Silvan <adlerhurst@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Align check SQL with D13 via principal_home_project_id, single-source
catalog tables (D14), tighter DDL CHECKs, and supersede authz.md /
ADR follow-ups that still pointed at team_memberships checks and
delegation/expression-edge tables.

Co-authored-by: Silvan <adlerhurst@users.noreply.github.com>
@adlerhurst
adlerhurst force-pushed the cursor/permission-storage-wave0-6e44 branch from 517c12a to e4e7adc Compare August 7, 2026 14:39
@adlerhurst
adlerhurst merged commit 8b1d5e3 into main Aug 7, 2026
14 checks passed
@adlerhurst
adlerhurst deleted the cursor/permission-storage-wave0-6e44 branch August 7, 2026 14:58
@github-project-automation github-project-automation Bot moved this from In review to Done in Engineering Kanban Aug 7, 2026
adlerhurst added a commit that referenced this pull request Aug 7, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary

Wave 1 authz MVP storage on top of Wave 0 (#666): Goose migrations for
the six tables, dual-write resource-scope identifiers + membership edges
from existing project/team/user/membership statements, and
`PersistCatalogVersion` for compiler `CatalogMutations` (#720). SQLite
gets the same schema and dual-write path for local parity.

Review / maintainability follow-ups:
- Membership-edge deletes use one `DeleteAuthzMembershipEdges(filter)`
API (kept `ForTeamDeactivate` for the subquery).
- Shared `dialect/authz` holds multi-write user helpers,
`MembershipEdgeSchema`, and catalog row builders (engines keep SQL).
Removed the three identical dual-write adapter files and identity
wrappers.
- Typed authz discriminators (including DDL-aligned principal types),
assignment scope constructors, and `AuthzMembershipEdgeKey`.
- Spanner `activeUniqueKey` lives in Spanner storage (not domain).
- Spanner membership-edge upsert uses a `created_at` no-op update (PK
updates rejected).
- Authz enum SQL parameters bind via `.String()` (matching
`MembershipStatus`), including sibling ResourceKind/CatalogKind binds.
- Spanner nullable authz params bind via `spannerNullString` /
`spannerNullTime` (not Go pointers).
- `CreateAuthzAssignment` mints empty IDs via `ensureManagedID`
(`PrefixAuthzAssignment` / `asgn_`).
- `GetAuthzCatalog` loads a persisted catalog + projected rows; deep
Persist round-trip asserts live in `stmttest` (all dialects). Seeded
`viewer→admin` closure depth is 2; Spanner relation-reference columns
have DEFAULTs.
- `authz_membership_edges` has Wave 0 set FK `(project_id, set_id) →
teams` + MVP `CHECK (user∈team)` with `ON DELETE CASCADE` on **all**
dialects (Spanner matched to Postgres/SQLite). `stmttest`
`DeleteTeamCascadesEdges` asserts hard-deleting a team removes edges on
every engine.

## Validation

```sh
go generate ./internal/service/
go test ./internal/domain/ ./internal/service/ ./internal/storage/v2/dialect/authz/ ./internal/storage/v2/dialect/spanner/ -run ActiveUniqueKey
go test -tags sqlite_integration ./internal/storage/v2/stmttest/ -run 'Authz|PersistCatalog|DualWrite'
go test -tags postgres_integration ./internal/storage/v2/stmttest/ ./internal/storage/v2/dialect/postgres/ -run 'Authz|PersistCatalog|CatalogSeed|DualWrite'
```

Postgres + SQLite green (including `DeleteTeamCascadesEdges`). Spanner
compile-checked + ActiveUniqueKey unit tests; full emulator coverage via
CI `server:test-spanner`.

## Release notes / changeset

Changeset already present: `.changeset/authz-mvp-storage.md`
(`@zitadel/server` minor). Review/maintainability follow-ups are Go
storage/API-shape refactors — no additional changeset.

## Notes

- Stacked on Wave 0 (`cursor/permission-storage-wave0-6e44` / #666).
- Re-synced with rewritten Wave 0 tip after main (`7f696931`): kept Wave
1 shipped schema; folded Wave 0 RSI MATCH SIMPLE + delete notes and
`system-permission-catalog` see-also link. Skipped Wave 0 `grantor_type`
null CHECK (not in Wave 1 migrations).
- Out of scope: resolver (#423), Leopard, filling bundles, #420
auto-compile, Filter support for team-deactivate subqueries, splitting a
second `dialect/authz*` package.

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-9a29c97f-a069-4f09-9bec-244a599e3354?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-9a29c97f-a069-4f09-9bec-244a599e3354&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Silvan <adlerhurst@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants