Skip to content

ogar-rbac: mint the canonical RBAC authority, fed by ogar-auth's user - #301

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/patient-identity-architecture-r8lhi5
Sep 6, 2026
Merged

ogar-rbac: mint the canonical RBAC authority, fed by ogar-auth's user#301
AdaWorldAPI merged 1 commit into
mainfrom
claude/patient-identity-architecture-r8lhi5

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

What

Establishes the ownership seam the ruling fixed:

ogar-auth  = canonical user + authentication bindings
ogar-rbac  = authorization over that canonical user

ogar-rbac depends on ogar-auth deliberately. The edge is the point: there is no way to ask this crate a question without first holding an AuthenticatedUser, which only ogar-auth can produce — so "RBAC does not accept an arbitrary parallel identity normalizer" is a property the compiler checks, not a convention.

ogar-auth::user realizes already-minted vocabulary

It invents no IAM model. The classes exist:

vocabulary classid here
auth_store 0x0B01 UserStore
project_actor 0x0104 User / UserId
project_role 0x0117 User::roles
project_membership 0x0108 User::memberships
auth_zitadel / auth_ory_keto 0x0B02+ AuthBinding::provider

Providers stay data, exactly as auth_store's own is-a children already model them (claim_grammar as an attribute). ProviderId is an opaque label, never an enum — a new IdP is a preminted class with a different row, not a new match arm.

The envelope every path converges on is the contract's existing ActorContextreused, not re-declared, which is what stops a second identity type appearing.

No secret aggregation

UserStore maps identities; it does not release secrets. User carries opaque KeyRefs and there is deliberately no aggregate accessor, so an identity lookup cannot quietly become unrestricted key retrieval.

Kiosk preserved

An unauthenticated kiosk user takes the same authorization path and yields the same decision — roles belong to the identity, never to the login method. a2ui-rs has zero files touched.

What did NOT move

  • The generic authorize / authorize_scoped kernel stays in lance-graph-rbac — consumed, never cloned.
  • lance-graph-contract stays zero-dep; OGAR only ever depends downward on it.
  • CapabilityAuthority / hotplug untouched — orthogonal.

The authority is an object (OgarRbac), not impl ClassRbac for OgarClassView: that is E0117 from any third crate, and sharing a repository does not change Rust coherence.

Tests

ogar-auth 19/19, ogar-rbac 7/7, fmt + clippy -D warnings clean. Falsifiers covered: wide {1,7,92} projection, canonical-identity ownership, orphan legality, kiosk parity, provider ignorance (no provider branching), two bindings → one user, key-reference-not-material.

Depends on AdaWorldAPI/lance-graph#1206 (Axis-4 widening); the git deps point at that branch and move to main when it merges.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PGkLH5cYiiWM4QmNkvBBvf


Generated by Claude Code

Establishes the ownership seam:

    ogar-auth  = canonical user + authentication bindings
    ogar-rbac  = authorization over that canonical user

ogar-rbac depends on ogar-auth deliberately. The edge is the point: there
is no way to ask this crate a question without first holding an
AuthenticatedUser, which only ogar-auth can produce, so "RBAC does not
accept an arbitrary parallel identity normalizer" becomes a property the
compiler checks rather than a convention.

ogar-auth::user realizes vocabulary that was already minted rather than
inventing an IAM model: auth_store (0x0B01) -> UserStore, project_actor
(0x0104) -> User/UserId, project_role (0x0117) -> User::roles,
project_membership (0x0108) -> User::memberships. Providers stay data,
as auth_store's own is-a children already model them: ProviderId is an
opaque label, never an enum, so a new IdP is a preminted class with a
different claim_grammar row and not a new match arm. The envelope every
path converges on is the contract's existing ActorContext -- reused, not
re-declared, which is what keeps a second identity type from appearing.

UserStore maps identities; it does not release secrets. User carries
opaque KeyRefs and there is deliberately no aggregate accessor, so an
identity lookup cannot quietly become unrestricted key retrieval.

Kiosk is preserved as a first-class mode: an unauthenticated user takes
the same authorization path and yields the same decision, because roles
belong to the identity and never to the login method.

What did NOT move: the generic authorize/authorize_scoped kernel stays in
lance-graph-rbac and is consumed, never cloned; the contract stays
zero-dep and is never reached into from OGAR.

The authority is an OBJECT (OgarRbac), not `impl ClassRbac for
OgarClassView` -- that is E0117 from any third crate, and sharing a
repository does not change Rust coherence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGkLH5cYiiWM4QmNkvBBvf
@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review September 6, 2026 13:46
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@AdaWorldAPI
AdaWorldAPI merged commit 2ee63a6 into main Sep 6, 2026
3 checks passed
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