Global/contextual storage, common audit, seed handoff and existing-binding API admission are implemented. Derived from jointly Accepted ADR-0072 and ADR-0073, 2026-10-25. The global User/authority model, internal binding/adoption consumers and central creation owner exist. Contextual tables/commands/manifests below are implemented; the Phase 03 Status block owns current qualification and review state. Jointly Accepted ADR-0076 / ADR-0077 define P03-2 live authorization and one administrative directory GET. Its confined reader, canonical policy/evaluator, central denial evidence and HTTP directory are wired in the API composition. The additional ADR-0078 and ADR-0079 contracts are Accepted. Self-entry storage/HTTP, deliberate native account creation and context selection, bounded administration routes and native forms are implemented. Selected protected views consume immutable snapshots prepared by the native response owner; RSC rendering holds no credential or API-operation capability. The phase Status block alone owns current packet, qualification and review state. The bounded contract separates these obligations without treating acceptance as runtime proof. The ordered admission timing/observation contract additionally derives from ADR-0074, Accepted 2026-10-26. The phase Status block owns qualification and review state. The fixed-seed prerequisite source derives from ADR-0080; its acceptance does not qualify the new owner transition or seed race proofs. The separate fixed-origin completion contract derives from Accepted ADR-0081; its implementation and proofs remain governed by the phase Status block.
Identity owns global application bindings, contextual Membership/Profile/ assignments and tenant-wide Role templates. Keycloak owns credentials/provider authentication; Tenancy owns Tenant/Organization lifecycle; Audit owns audit rows. Invitations add the scoped offer and its verified acceptance contract under ADR-0093; its first writers remain subject to the phase Status block. Cross-module facts use application contracts, without an Organization FK, navigation or database join. Branch-specific Role configuration is not selected.
P03-1 Step 3 implements one global User/authority model and migration chain, the common prepared creation owner and eight central audit seams. API composition denies seed adoption; the ordinary seed/demo handoff is implemented in Step 4. Existing-binding API admission uses the same read-only contract. The separately classified global-self bootstrap route uses the bounded entry contract below; ordinary API admission never creates a User.
Set both Identity__AuthorityId and Authentication__Tenant__Issuer in the
selected private deployment environment before make migrate installs/verifies
the authority. Supply the migration connection and complete pair together, either
as all three process variables or in the selected private environment file.
The migration owner refuses partial input and never combines the two sources;
a complete process selection takes precedence over file defaults. Schema-only
process input also refuses a file that supplies an authority pair. The ID is
deployment-pinned and never derived from an issuer URL.
The dedicated deployment child receives the migration credential only in its
environment; restore/build and API processes receive no migration credential.
An exact existing registration is a no-write verification. A conflicting pair
refuses without repair/rebind. With both keys absent, migrations create the
schema only; missing authority cannot admit a binding. A partially configured
pair refuses before migration writes. See the environment example
and the canonical authority contract.
Schema-only migration does not satisfy API startup. Every API host, including one
serving only anonymous reads, requires both runtime configuration keys and the
matching installed authority. Startup qualification rejects missing, unavailable
or mismatched registration before accepting HTTP requests. Direct dotnet run
reads process/.NET configuration, not the root .env. Restore the intended
configuration when it differs from the selected database; an issuer rebind remains
P03-9/G13 and is not an automatic repair.
The internal binding service uses explicit startup qualification and fresh, physically read-only existing-binding transactions; it never creates a missing binding. Fixed seed adoption separately requires its confined admission port. Real provider/app-role, schema/body/ACL/caller and physical outcome controls are implemented. The directory contract appears below; closure, production first-admin and issuer rebind retain their named later owners.
User's authority is ADR-0072; the canonical accepted database annex owns its exact global SQL, grants and guards.
| Table | Class / owner | Keys, scope and lifecycle |
|---|---|---|
memberships |
Tenant-owned, org-scoped; Membership root | UUIDv7 MembershipId; referenceable (tenant_id,id); current unique (tenant_id,user_id,organization_id) NULLS NOT DISTINCT WHERE deleted_at IS NULL |
membership_profiles |
Tenant-owned, org-scoped; contained Membership child | PK (tenant_id,membership_id); one profile created with Membership; exact parent organization mirror/lifecycle |
membership_role_assignments |
Tenant-owned, org-scoped; contained Membership child | PK (tenant_id,membership_id,role_id); exact Membership organization mirror; Role capability compatibility guard |
roles |
Tenant-owned, tenant-wide; Role root | UUIDv7 RoleId; referenceable (tenant_id,id); current unique (tenant_id,permission_scope,built_in_key) WHERE deleted_at IS NULL; no organization_id |
role_permission_grants |
Tenant-owned, tenant-wide; contained Role child | PK (tenant_id,role_id,permission_scope,permission_key); exact Role capability-scope mirror; no organization_id |
Use each Standard 05 table class,
its EF filter and ENABLE/FORCE RLS. Org-scoped tables receive canonical
restrictive write guards and immutable organization triggers. Parent references
use (tenant_id,parent_id) with ON DELETE RESTRICT; index every FK. Independent
INSERT/UPDATE mirror triggers compare nullable organizations with
IS DISTINCT FROM. Never rely on a nullable organization column in a FK to
check tenant integrity under MATCH SIMPLE.
Assignments reference Membership and Role through two tenant-composite FKs.
Membership's user_id references same-module users.id with ON DELETE RESTRICT
and its own supporting UserId index. The permanent User tombstone retains the
key; audit actor columns still have no User FK. The BEFORE admission guard maps
unknown and nonadmitting targets to the same refusal before FK diagnostics can
be exposed, and its fixed diagnostic translation discloses no constraint values.
Their trigger checks the exact Membership organization mirror, then requires
Tenant capability iff that organization is NULL and Organization capability iff
it is non-NULL. Role has no organization to match. Grant children reference Role
through its tenant-composite FK and mirror capability scope. Tenant, User/parent
references, Role built-in key and capability scope are immutable.
Role built-in/scope CHECKs admit only the finite manifest pairs; no free-form Role
key or Platform variant enters these tables. Soft-deleted Roles/Revoked or
Suspended Memberships do not contribute live grants. Tenant natural-key indexes
remain partial while referenceable primary-key-containing indexes remain full.
learnstack_app receives SELECT/INSERT/UPDATE on Membership/Role/Profile and
SELECT/INSERT/DELETE on assignment/grant children, subject to all guards. Root
revocation is soft; no app root/Profile DELETE exists. Child changes go through
the owning root and tracked audit capture. No PUBLIC/Platform/dispatcher Identity
privileges or new login are introduced. Global grants/functions follow ADR-0072.
Each migration declares exact grants: a privilege never identifies a code path.
Permission definitions remain compiled code, without a new global Permission
table. Persisted grants select (scope,key) from the registry; unknown/inactive
entries never authorize. Standard 19's accepted foundation contract
owns the exact registry, version-1 grants and generated kernel artifact.
ADR-0093 owns the decision. The following defines the P03-4 boundary. The phase Status block owns delivery.
| Subject | Contract |
|---|---|
| Invitation root | Tenant-owned, org-scoped typed InvitationId; immutable normalized recipient email, inviter UserId, nullable OrganizationId, one compatible RoleId and offered Role version; no invitee UserId selector |
| Pending uniqueness | One Pending offer per tenant/org/normalized email, null-safe, including an expired Pending offer awaiting explicit reissue |
| Lifecycle | Pending → Accepted or Revoked; expiry is evaluated from database UTC after waits; Accepted/Revoked are terminal |
| Accepted disposition | Immutable accepted actor, original MembershipId and created/already_member; retained consumed verifier permits only own-result recovery |
| Generation | Create stages generation 1; explicit reissue advances generation, token and job, using the current TTL; old codes are invalid immediately |
| Email and verifier | Email is personal data; current/consumed domain-separated SHA-256 token verifiers are sensitive credential material; all raw values are excluded from audit/diagnostic sinks |
CreateInvitationCommand automatically uses IInvitationEmailStager to enroll
its encrypted envelope and identifier-only typed external job on the Invitation's
ordinary unit of work. Invitation, envelope, job and MUST audit commit together.
Reissue creates a new generation/envelope/job; it does not repin changed Role
content. A lost response is resolved through the ordinary bounded receipt,
without a recoverable link in replay storage. No prepare/handoff or raw-link-read
API is part of this contract. The private SQL-only delivery tables are common
Infrastructure custody, not Identity aggregates or a new notification module.
Administrative create/reissue use existing live Membership-admin admission, exact resource scope and current whole-Role delegation. Revocation is withdrawal: current exact-scope membership-admin, Pending target and strong root version are required; a changed/deleted Role or uncovered offered grant does not prevent withdrawal. No Role assignment or reissue authority follows. All admin readers, mutations and original receipts additionally compare tenant and exact nullable organization; an Organization context must not disclose a tenant-wide offer merely because the base EF/RLS filters admit that row. Invitable-Role summaries disclose only delegable ID/key/scope/version, without a general Role/registry reader. Own inspection and own accepted-result reading are Off; other-person offer lists/detail, invitable Roles and protected receipt reads are MUST sensitive reads. Mutations remain MUST. The matrix owns each consumer binding; the phase Status owns source-bound qualification.
ICurrentInvitationRecipientReader performs a new configured-issuer UserInfo
observation for each Pending inspection/acceptance: exact sub, one valid email,
Boolean verified=true, 16 KiB/five-second bound and no business locks during I/O.
Email is matched under ADR-0093's explicit bounded mailbox policy; it does not
identify or merge Users. A provider edit can race database commit. Keycloak owns
registration and verification through the fixed same-subject VERIFY_EMAIL
continuation; actual provider verification is required. No global email mirror
is added to User.
The confined fn_identity_invitation_entry accepts the application-derived
32-byte verifier digest, independent host tenant and verified authority/subject
key. It returns only exact offer/actor facts and the stored inviter's global
admission Boolean; it does not prove delegation or expose arbitrary User state.
Its disposed read owner precedes InvitationEntryScope, the eleventh logical
tenant setter, whose single-use witness admits only exact inspection/acceptance/
own-result consumers. This ordinary owner is operation-bound ReadOnly or
ReadWrite according to the admitted consumer, not a general scope hatch. New User
bootstrap commits and disposes first. Neither owner joins or retargets the other.
The database contract owns the exact closed
function/setter/source ledger; Security owns
host/source and native authority.
AcceptInvitationCommandHandler is the sole added two-root exception alongside
provisioning: one Invitation and at most one Membership with contained Profile
and assignment, one ordinary transaction, central plural MUST intents. It writes
no Role, User or Tenancy root. The complete sorted invitee/stored-inviter User
plan precedes the Tenant fence. Preserve the canonical strongest mode derived
from actual effects: shared for nonadmin effects, exclusive for actual Tenant
Admin eligibility/assignment effects, without upgrades or caller exemptions.
Fresh reads after waits recheck actor, generation, database expiry, lifecycle,
pinned Role and the stored inviter's complete current delegation. Standard 05
alone owns lock ordering and retained-reader details.
| Exact target Membership | Outcome |
|---|---|
| None | Create one Active Membership with Profile and the offered assignment |
| Active | Consume as already_member; retain that exact Membership and all Roles/profile |
| Suspended | Conflict without consumption/reactivation |
| Only Revoked history | Create a fresh successor ID and children; never resurrect history |
The Invitation's accepted disposition is centrally captured. The unchanged Membership outcome emits its separately declared subject-only security event, without fabricated before/after or arbitrary handler metadata. A required audit failure rolls back both roots, envelope/job staging and their state changes.
The dispatcher routes an already Accepted offer to GetOwnInvitationResultQuery
before declaring mutation intents. That Off read requires the same fresh Active
actor, consumed verifier and original current Active Membership. It precedes
Pending-only expiry/version/Role/inviter checks and does not require current
email. A Pending mutation that discovers a raced acceptance returns 409 with
truthful failed-attempt evidence; it cannot report a duplicate successful grant.
Unknown COMMIT stays indeterminate until authoritative result recovery.
Native capture and provider sending follow the
architecture journey.
Revoke/reissue/accept invalidates old codes. Prepare refuses an obsolete
generation before its plan commits; already-prepared mail may still arrive.
Business withdrawal adds no ordinary provider-table cancellation writer.
250 is accepted submission, not inbox delivery. Queued, submission accepted,
failed, uncertain, expired and revoked remain distinct product states.
The Invitation aggregate and its Identity migration map invitations with the
canonical tenant/org filters, FORCE RLS, same-module reference keys, explicit
credential redaction and guarded finite transitions. Email is normalized through
InvitationMailbox; invitation authoring and recipient matching reuse that
parser. SMTP receives the validated staged address and independently enforces
transport/header safety. The local
dot-atom is 1–64 ASCII bytes, the full mailbox at most 254, and each ASCII DNS
label at most 63. No provider identity is selected by email.
The exact preliminary SQL function returns bounded offer/actor facts without email or grants. Its operation-bound HTTP/pipeline activation belongs with the three actual recipient operations; schema availability grants no public entry. The phase Status owns implementation and qualification.
The database requires Active/Suspended Memberships to have NULL deletion fields and Revoked to carry both deletion timestamp/actor. Its INSERT/UPDATE lifecycle guard rejects direct resurrection, inconsistent status/deletion markers and transitions outside the diagram. Active/Suspended share current uniqueness; Revoked is terminal. Rejoin creates a new ID and fresh assignments after retiring the predecessor. Profile has no independent delete/status lifecycle; historical child facts cannot restore a revoked Membership's authority.
Profile carries optional contextual DisplayName (128 characters) and
PreferredLocale (35-character well-formed locale tag), both marked sensitive.
No unused contact field is collected. Missing values use explicit safe UI fallback, without copying
global fields on reads. NULL-org profiles contain no branch-private notes;
organization profiles are independent. Self/admin changes allow those fields
only, never canonical email/verification, global identity, Role bindings,
arbitrary settings or consent evidence. The implemented Membership contracts
below carry these exact fields; P03-5/G9 owns dynamic fields/revisions. No dormant
custom-fields column lands in the foundation.
Creation, Suspended→Active and all assignment additions recheck the target User fence, including additions of currently empty/inactive templates. Standard 05's accepted lock contract alone owns the algorithm, ordering, fresh reads, deadlines and database guards. Profile/nonadmin/organization mutations use its shared tenant invariant mode; first-admin and tenant-admin eligibility/assignment/kernel transitions use its exclusive mode. Actual affected rows bind the mode before acquisition; no shared-to-exclusive upgrade or caller-declared nonadmin exemption is admitted. Only the admission operations above, including invitation acceptance, automatically require the User fence; profile-only changes and assignment removals retain their existing tenant-lock requirements. Standard 05 also owns backend-heldness limits, fixed internal plan fault versus target refusal, and the identical outward refusal. No algorithm or new User reader is defined here.
Every Membership mutation, in either lock mode, leaves an Active, globally eligible, tenant-wide Tenant Admin with both kernel pairs. The first mutation therefore establishes that administrator. Role roots may exist first; completed foundation is actual eligible administration plus semantically valid required templates, without a bootstrap Boolean, receipt or table. Trial, Active and Suspended retain their admin. No Archived/termination exemption exists until separately admitted by G13/G16. Final-admin removal/suspension/revocation or kernel stripping refuses. Future global closure cannot make the final admin ineligible before its separate recovery/termination authority and guards are Accepted and implemented.
P03-1 supplies the bounded Tenancy Application.Contracts port
IIdentityScopeLifecycleReader.ReadAsync(TenantId, OrganizationId?, CancellationToken).
Its immutable IdentityScopeLifecycle result has four Booleans:
TenantExistsAndNotDeleted, TenantAllowsOrdinaryWork,
OrganizationBelongsToTenant and OrganizationAllowsOrdinaryWork. Organization
facts are false when no organization is requested; it returns no row or list.
Trial/Active admit ordinary work; Suspended retains an
admin without ordinary-work eligibility. Organization work requires Active and
nondeleted, not just belonging. Missing targets return closed false facts.
The target must match an already trusted, announced owner scope. The Tenancy implementation reads its own tables in one fresh READ COMMITTED statement on that app-role transaction; no cached lifecycle result, additional setter, cross-module SQL join or Organization FK is admitted. P03-1 Step 4 supplies the port/implementation and seed controls; live admission executes it at the declared per-act snapshot. Implemented Membership transitions and Organization lifecycle commands enforce last-admin/default-organization invariants under fresh ordered locks and are exposed through protected HTTP routes. The Phase 03 Status block owns qualification; the foundation port alone does not establish those concurrency proofs.
P03-1 admits only the fixed development-seed capability.
ISeedIdentityFoundationAdmission is DenyAllSeedIdentityFoundationAdmission
in API composition. The external seed/demo owner supplies bounded canonical
manifest facts and actual provider-adoption witnesses to the confined Seeder
implementation. Email, a realm role or SystemActor alone supplies no admission.
There is no HTTP entry, operator population or general bootstrap bypass.
Production first-admin/provisioning admission is P03-9/G13, before a production provisioner uses it. Its authority/recovery cannot inherit the seed capability. Tenancy's current ProvisionTenant remains the ADR-0042 Tenant/default Organization pair; no cross-module transaction or further exception is admitted.
- The external seed owner completes Tenancy acts and ADR-0072 trusted binding adoption, preserving all four actor/provider IDs, credentials and MFA.
InitializeSeedRolewrites one Role and grants per transaction from version 1. Eight templates per tenant include three Organization-capability variants; there are no per-organization Role rows or organization-initialization step.CreateFirstSeedAdminMembershipatomically writes the declared admin, contained Profile/Assignment and central MUST audit, referencing an existing Tenant Admin template. Its complete root/child plan precedes mutation; User/tenant/last-admin guards apply.CreateSeedMembershipwrites each learner and contained state separately.SetSeedMembershipProfilechanges bounded fields through Membership. No command writes another root merely because its transaction is available.- Fresh acts use deterministic version-1 declarations and exact postconditions. Warm completion verifies named immutable roots and a valid installed graph; matching completion performs no writes. Valid suspension, historical revocation, profile/assignment changes and adopted grants are preserved without reset, reactivation or successor creation. Only a recognized pristine incomplete prefix can resume; a missing contained grant or assignment is not evidence that it was never installed. Recheck each possible write's current evidence under its fence/version. Edited partial, ID-conflicting or invalid/foreign state refuses before remaining writes, preserving earlier commits.
The expected fingerprint is not a new receipt table. Existing Role/Membership facts and canonical pristine-prefix evidence establish completion/resume; keep historical manifests for earlier completed acts. Registry additions do not change grants. Legitimate later adoption or removal of nonkernel profile writing is preserved, without turning seed into an upgrade reconciler. The exact evidence, per-write recheck and held-seed/admin race controls belong to Standard 05. Tenancy's separate seed-origin adoption does not grant Role authority or repair Identity children.
The four Identity seed writers use Standard 05's closed temporary prerequisite operation only after their complete ordered plan and target preparation. A separate Identity projection checks the fixed declaration's retained history across the target Tenant's branches using metadata only; ordinary seed readers keep their current-organization predicates. Tenancy prerequisites run narrow first, and the common owner restores narrow scope before returning any usable result. The Identity metadata reader does not depend on the Tenancy guard; a closed composition service joins owning contracts without a runtime dependency cycle or module request types in the common coordinator. It returns no profile values, raw editor evidence or Membership directory. Standard 05 owns source binding, state/write barriers, cleanup and the admitted-writer threat boundary.
Origin adoption has a distinct completion witness and reader, without joining the eight pristine consumers. Identity's closed owning contract checks declared Role/Membership identities, immutable scope/User associations, retained lifecycle/stamps, required children and a valid current finite grant/ assignment graph on the prepared adoption owner. The temporary hatch reveals branch facts only inside this operation and clears before admission returns. Required contained profiles exist and their owning Membership aggregate version is positive; version zero refuses. This is current retained disposition, not proof that the original profile initializer ran. Lawful empty/edited profiles, nonkernel grant removals and retained terminal nonadmin Memberships are preserved. A revoked former admin requires a qualifying local replacement; the current predicate still requires an Active undeleted NULL-org Membership assigned a live Tenant-scoped Tenant Admin Role with both kernel pairs. It neither reads global User/provider state nor freshly attests the original writers' globally eligible administrator invariant. Those writers retain their existing invariant and guards. Completion adds no login, assignment, reactivation or repair authority, and returns no profile values or raw relationship/editor evidence. Standard 05 owns the exact local predicate, marker-2 historical repeat and confinement rules.
Identity also supplies the separate closed ordinary-scope prerequisite for the twelve content seed writers: absence of all retained Tenant-wide Role history, including tombstones and undeclared IDs. Role DELETE is denied, so any retained Role proves the later v1 Identity stage began even when branch Membership facts are invisible. This contract neither borrows the temporary hatch nor exposes a Role directory.
| Existing actor | Membership scope | Assignment |
|---|---|---|
English admin (a001 suffix) |
English / NULL organization | Tenant Admin, Tenant capability |
English learner (a002) |
English / NULL organization | Learner, Tenant capability |
Yoga admin (b001) |
Yoga / NULL organization | Tenant Admin, Tenant capability |
Yoga learner (b002) |
Yoga / default studio | Learner, Organization capability |
SeedData owns the full
legacy actor/tenant/organization IDs. Exact assignment declarations extend that
manifest; suffixes above identify existing actors, not newly allocated IDs.
The four authentication personas remain fixed. Additional cross-tenant/two-branch
and same-tenant-second-person proof populations are fixture-local, outside
IdentityUsers and its exact four-user realm manifest. Email never merges them.
Yoga admin b001 keeps its existing provider claim for default studio b1.
Its seed Membership declaration independently selects NULL organization, and
the seed transaction derives NULL SQL organization scope from that trusted
target; the authentication manifest does not choose the Membership write scope.
A branch session may consume eligible Tenant grants for branch operations and
explicitly declared tenant-wide reads. Tenant-wide Membership/Profile/template
writes require an explicitly selected orgless context through P03-3's validated
native admission transition. Neither the existing branch claim nor app.scope promotes
writes; no extra branch-admin Membership or provider-claim rewrite is required.
Live policy, central evidence and the administrative directory are implemented.
The policy/evaluator consumes the confined reader/source through the shared
decision port; common Application gains no Identity module dependency. The
directory uses the real EF read adapter and common MUST write path. API and
application checks each read fresh committed authority before dispatch.
CommittedIdentityAuthorizationReader coordinates the module statements from
Application; its Infrastructure session factory retains the exact Identity SQL
and physical child owner. The reader consumes an
exact verified actor/tenant/optional-organization candidate in an isolated child
owner. It returns bounded current Membership/Role/grant facts and consumes
Tenancy's lifecycle contract, without a profile/global directory, synthetic
TenantContext or parent transaction reuse. The grant and lifecycle statements
have separate committed snapshots; revocation committed before the grant query
affects that admission, while already-admitted work can finish. Binding remains
the earlier global User eligibility check.
HTTP and direct application dispatch independently classify and authorize each protected act. Explicit public, AccountSite, protected and finite seed/system policies default deny unknown/missing declarations. Resolver membership confirms scope but does not authorize a later protected act. Public requests remain free of identity/grant lookups; AccountSite keeps its separate real HostScope ceiling and Off/physical ReadOnly mode while gaining live membership/lifecycle admission.
The implemented administrative directory uses existing Tenant
identity.membership.admin, without requiring the new v2 grant delta. It admits
HostAndClaim, confirmed ClaimAndMembership or independently verified
HostAndNativeSessionScope, never HostOnly. Native evidence retains the real-host
ceiling. Its declared
tenant-wide read can use an eligible NULL-org Tenant Admin Membership from a
branch session. It returns current undeleted Active/Suspended Memberships and
only MembershipId, UserId, OrganizationId, status, contextual DisplayName and
PreferredLocale. Provider subject, email, global profile and grant graph are
excluded. The API contract owns exact route,
DTO/cursor/no-store rules. The native BFF's closed operation union selects the
generated directory operation using its held credential and admitted context;
it exposes no arbitrary method, path, headers or browser scope selector.
Its MUST identity.membership.list row commits on the ordinary ReadWrite owner
before disclosure; it is not a physical ReadOnly-catalogue query. The implemented
ChangeRoleGrantsCommand and AdoptRoleGrantManifestCommand each mutate one Role
with contained grants, expected version, current post-lock authority and same-
transaction MUST audit. Permissions owns their exact bindings
and delegation ceiling; unknown manifest, self-escalation or kernel removal
refuses. The accepted warm contract preserves valid differing adopted state.
Protected HTTP grant-editing/adoption routes and their native forms are
implemented. They retain current authorization, expected-version and protected
idempotent-receipt checks; the
Phase 03 Status block owns qualification.
Standard 05 owns the dedicated four/two authorization/evidence lanes, tracked job ownership, retained cleanup and required direct/PgBouncer progress proofs. Its read authority contract owns invocation/transaction lifetime and EF/RLS agreement. Standard 18 owns denial occurrences, immutable provenance and all pre-success barriers. No pool, crash, permission or disclosure proof is claimed by acceptance alone.
Accepted under ADR-0078/0079. The phase Status block records current delivery, qualification and review state for the implemented entry, administration and native presentation mechanisms. Keycloak authenticates the provider subject. Native scope evidence remains a separate immutable source; it never rewrites provider claims. The API binds the User, validates actual host and live Membership/lifecycle, then applies each ordinary operation's current capability and target rules. Global-self lookup, deliberate opt-in bootstrap and own-context discovery have only their three exact early-branch descriptors; no ordinary request allocates a User. Creating identity provides neither Membership nor a tenant session. Discovery exposes only the verified person's own eligible context choices, not another person's data.
Six application requests read existing Memberships and edit their contained
profile, assignments or lifecycle. GetMembershipDetailQuery returns the target's
MembershipId, UserId, OrganizationId, status, DisplayName, PreferredLocale, RoleIds
and version. Its ordinary ReadWrite owner commits MUST sensitive-read audit before
disclosure. Only the matched Tenant membership-admin clause may widen detail
reads; the Organization clause remains in the exact current branch.
GetOwnMembershipProfileQuery is Off and module-declared physically ReadOnly.
It selects the actor's exact Active current Membership, without a target selector
or NULL-org fallback, and returns only DisplayName, PreferredLocale and version.
SetMembershipProfileCommand requires applicable membership-admin and
profile-write; SetOwnMembershipProfileCommand requires applicable profile-write
and the exact actor-owned current Membership. Both replace only nullable
DisplayName and an enabled PreferredLocale on an Active target.
ChangeMembershipStatusCommand suspends, reactivates or revokes;
ReplaceMembershipRoleAssignmentsCommand replaces at most eight distinct,
same-tenant, capability-compatible Roles on an Active or Suspended target.
Empty assignments remain subject to the database's total-admin invariant.
Unchanged assignments retain their child instances; no-op mutations do not
advance the Membership version. Suspension and prepared assignments confer no
live authority; Revoked remains terminal. There is no guessed-UserId create/rejoin
path; P03-4 owns invitations/successors.
All four writers retain exact context scope and expected Membership version. They recheck current authority, lifecycle, target graph and complete lock plan after waiting. Assignment additions check each Role's whole current grant set against the actor's live ceiling under retained Role-root locks; profile-only changes need no Role lock. Actual User/administrator effects select the canonical fences, with no shared-to-exclusive upgrade. The owning transaction validates the graph/last-admin invariant and commits MUST audit with the change. Module mutation results retain the exact MembershipId and version for later transport.
The closed HTTP/OpenAPI surface uses these application consumers and the current-branch directory, Role list/detail/registry, own-context and capability reads. The SDK supplies injected, bounded operation wrappers; native/browser activation and qualification remain governed by the phase Status block. Receipt disclosure uses the six closed Identity/Tenancy application reads described below.
Own-current-Tenant context navigation uses Tenancy's named
IOwnTenantNavigationReader contract on the existing ordinary ReadOnly owner.
Its separate IOwnTenantNavigationCoverage provider reads fresh own Membership
facts before and after label selection, bound to the exact active own-context
invocation. A tenant-wide own Membership permits active branch choices; otherwise
only the current branch participates. This adds no general organization reader,
tenant-wide scope hatch, setter or owner function. Cursors bind the actor, Tenant,
organization and coverage mode. Own-capability preview projects nine finite flags
without running denied operations or caching authorization.
The API wire boundary
owns byte-bounded page selection before continuation; ordinary short records keep
the requested count and large escaped labels retain complete subsequent pages.
The existing Role grant/adoption writers use the eight finite templates. Standard 19 owns the five-plus-three registry, explicit v2 exception, monotonic marker and unchanged kernel. Standard 05 owns exact Role/Membership lock plans, adoption guards, warm witnesses and durable protected receipt metadata. Member-detail Tenant read alternatives may mint the existing read witness; exact-branch alternatives stay scoped, and writes never inherit that hatch. Receipt reads reauthorize the stored original target and commit a fresh MUST read event before retained bytes can be returned. Membership, profile, own-profile and Role reads use exact original IDs, including retained terminal targets; own-profile disclosure also requires the original current actor and scope. They neither rerun the mutation nor substitute a successor Membership. The Tenancy receipt reads cover the original Organization or Tenant default resource with their current disclosure capability. Ordinary readers keep their current-only filters; receipt reads add no general history or directory surface.
Permissions owns capability and advisory-preview bindings; audit owns actual and planned classifications. Exact HTTP/native routes and DTOs derive from the joint decision package. UI hints are not grants; actual API/application/target checks remain authoritative. Acceptance closes no runtime, race, provider, browser or accessibility proof by itself.
erDiagram
User ||--o{ Membership : "stable identity reference"
Membership ||--|| MembershipProfile : contains
Membership ||--o{ MembershipRoleAssignment : contains
Role ||--o{ MembershipRoleAssignment : "tenant-composite reference"
Role ||--o{ RolePermissionGrant : contains
Membership {
uuid tenant_id
uuid organization_id "nullable; immutable"
uuid user_id
text status
}
Role {
uuid tenant_id
text permission_scope "capability; no organization column"
text built_in_key
}
Text fallback: User has contextual Memberships; each contains Profile and assignments to tenant-wide Roles. Roles contain grants. OrganizationId is a Membership value, without a cross-module FK/navigation.
stateDiagram-v2
[*] --> Active: admitted creation
Active --> Suspended
Suspended --> Active: User admission rechecked
Active --> Revoked: deletion markers
Suspended --> Revoked: deletion markers
Revoked --> [*]
Text fallback: Active and Suspended exchange; either revokes. Revoked is terminal, with a distinct successor on rejoin. Global Active/Closing/Closed admission and permanent binding follow ADR-0072, not a Membership state transition.
sequenceDiagram
participant Seed as External seed owner
participant Role as InitializeSeedRole
participant Member as CreateFirstSeedAdminMembership
participant DB as App-role PostgreSQL
participant Audit as Central audit path
Seed->>Role: Exact v1 template and closed seed witness
Role->>DB: One Role plus grants; commit
Seed->>Member: Declared admin and existing Role
Member->>DB: Standard 05 admission/invariant guards
Member->>DB: Membership, Profile and Assignment
Member->>Audit: MUST intent with captured children
Audit->>DB: Same-transaction audit
DB->>DB: Deferred invariant guards
DB-->>Seed: Commit and fresh semantic verification
Text fallback: Roles commit separately; first admin/children commit with audit. Failure leaves no partial Membership; earlier Role acts are verified on resume. There is no published primary integration-event flow in this foundation; see the empty catalogue.
flowchart LR
Seed[External seed/demo owner] --> Contracts[Identity Application.Contracts]
API[API: seed admission denied] --> Contracts
Contracts --> Application[Identity Application]
Application --> Domain[Identity Domain]
Application --> Tenancy[Tenancy lifecycle contract]
Application --> Shared[SharedKernel transaction/audit ports]
Infrastructure[Identity Infrastructure] --> Application
Infrastructure --> PostgreSQL[PostgreSQL: learnstack_app]
Shared --> Audit[Central Audit implementation]
Text fallback: Application uses domain, Tenancy application contracts and shared ports. Infrastructure persists through the app role. API seed admission is denied; global/provider adoption remains ADR-0072's confined owner.
P03-1 publishes no Identity integration event and activates no provider ingress, Invitation or closure consumer. P03-4 defines the bounded invitation writers above without an email-bearing integration event; P03-6/10 own later consent/ closure contracts. Framework domain-event dispatch does not imply a published contract.
Identity permissions binds production seed commands to the exact accepted Standard 19 manifest. The seed commands are implemented in P03-1 Step 4; the separately marked P03-2 bindings are implemented. Capability metadata alone grants neither permission nor a route; canonical admission and exact endpoint declarations enforce both.
Identity audit declares implemented root/child classifications and personal/reference paths and the implemented authorization, Membership read/write and Role grant rows. The central writer owns durability; Identity has no audit table/direct audit SQL. Global mutation audit follows ADR-0072 alone.
The canonical initial performance budgets apply: API p95 below 200 ms reads / 500 ms writes, excluding separately reported provider calls. The Step 4 record owns the small local command-path measurement and its limits; it establishes no production capacity. The distinct Step 5 binding-load observation retains its own control and scope. Standard 05 owns bounded lock waits, cancellation and metrics. Timeout yields a fixed retryable failure without partial grants. Candidate-reader/authorization cost measurements belong to the Phase 03 qualification; a global permission cache/session grant cannot replace a fresh check.
Branch-specific Role configuration reopens ADR-0073. Accepted ADR-0076/0077 own live delegation, default deny/revocation, reader handoff and trusted evidence. Those mechanisms and the bounded scope transitions/DTOs are implemented; Phase 03 Status owns qualification. P03-4 invitations, P03-5 dynamic values, P03-9 production first-admin/recovery and P03-10 closure/locators remain named later writers, not implicit foundation authority.
| Subject/control | Current state | Owner / scope |
|---|---|---|
| Models, commands and registry | Global and contextual foundation implemented | P03-1 Steps 3/4; live authorization P03-2 |
| Canonical marker/filter/RLS/FK/unique/aggregate/audit controls | Identity extensions implemented with populated positive/dirty controls | P03-1 Step 4; catalogue owns the individual obligations |
Permission_Definitions_DeclareScope |
Implemented for the finite foundation and live operation contracts | IdentityFoundationConventionTests, OperationPermissionConventionTests and actual command metadata; catalogue owns live authorization controls |
Mapped_Properties_Have_Explicit_Personal_Data_Classification |
Implemented for all current models, including contextual Identity | DataProtectionConventionTests; complete census and neutral-name omission controls |
Identity_Seed_Uses_Finite_Adopted_Manifests |
Implemented; no-write/interrupted/conflict actual seed controls | IdentityFoundationConventionTests / IdentitySeederTests; Phase 03 Status owns qualification |
Identity_Membership_Invariants_Use_Fresh_Ordered_Locks |
Implemented actual app-role races, live Membership transitions and Organization lifecycle/default invariants | IdentityMembershipTests, IdentityMutationLockTests, IdentityFoundationConventionTests, MembershipAdministrationRaceTests and TenancyAdministrationRaceTests; broader closure/locators remain P03-10 |
| Per-sink PII canaries/child audit/MUST rollback | Implemented actual child snapshots/redacted diffs, MUST rollback and value-bearing diagnostic controls | IdentitySeederTests, IdentityDiagnosticSinkTests, TenantExistingBindingAdmissionTests and canonical data-protection proof ownership; classification alone does not establish sink safety |
Global_Identity_Storage_Is_Subject_Confined / Global_Identity_Creation_Uses_The_Common_Audit_Owner |
Implemented global storage/creation, exact self-entry HTTP and deliberate native activation | GlobalIdentitySchemaTests, IdentityAuditConventionTests, GlobalIdentityBootstrapTests, IdentitySelfEntryHttpTests and native session-store.test.ts; Phase 03 Status owns qualification and reviews |
Registered means an actual catalogue entry exists. Until then a new control is planned/unregistered; later Registered is not Implemented. No empty census, skipped suite, type declaration or diagram supplies implementation evidence.