Context-Aware Authorization & Navigation Model
This document defines the 4-axis context model that governs modern navigation and access control across the platform.
1. The Core Principle
Access and navigation are no longer static "Role" based. They are Relationship-Backed, Forensically Verified, and Context-Oriented.
- The Graph: The Directory is the system of record.
- The Lifecycle: Capabilities are only provisioned after a Forensic Verification event (Standard 78).
- The Snapshot: A user's capabilities are calculated at login as an AuthzSnapshot.
- The Context: The user selects a specific "Hat" (Persona) and "Scope" (Target) to act within.
2. The 4-Axis Context Model (Implementation)
The platform's global state is governed by four primary axes as defined in ContextState:
| Axis | Field | Definition |
|---|---|---|
| Hat | activeHat |
The "acting role" for this session (Persona). |
| Scope | activeScope |
The visibility boundary (My Units vs Building). |
| Asset | activeAssetId |
The specific physical target (Unit 104, Plot B). |
| Principal | actingForPrincipalId |
The entity being represented (Acting On-Behalf-Of). |
2.1 Acting-For (Delegation)
The actingForPrincipalId allows for Proxy Interaction.
- If null, the user acts as themselves ("Me").
- If set, the user acts as the target Principal using the capabilities granted by the Delegation Bundle (e.g., proxy_voting).
Standard: All representations must follow the Standard of Delegation (Time-bound, Scope-bound, and Traceable).
2.2 Persona Facets & Bundles
- Facets (Sub-Roles):
- Governance:
President,Treasurer,Secretary,VigilanceMember. - Staff Dept:
Maintenance,BuildingEngineer,FrontDesk,Security,Cleaning,Accounting,Concession. - Legal:
Counsel,Notary. - Occupancy:
TenantLong,TenantShort(STR). - Agent:
PropertyManager,SalesAgent.
- Governance:
- Governance Constraints:
- Owner-Only Voting: Voting rights for Board roles (President/Treasurer/Secretary) are strictly predicated on the
OWNERHat. - Non-Voting Officers: A
STAFFmember serving as Secretary does not transition to a voting Board context.
- Owner-Only Voting: Voting rights for Board roles (President/Treasurer/Secretary) are strictly predicated on the
- Bundles: Pre-defined sets of capabilities defined in the Functional Capability Registry (Standard 78).
- Registry-Driven Infusion: The system no longer hardcodes role-to-capability maps. It pulls the bundle from
registry.tsand infuses it into theAuthzSnapshotduring graph traversal.
3. AuthzSnapshot & Dirty Detection
To ensure security without repeated expensive graph traversals, the platform uses a Snapshot mechanism.
- Storage: The snapshot is hydrated on the client at login.
- Validation: Every server action verifies the user's
AuthzSnapshotagainst the required context of the action. - Dirty Detection (
authzVersion):- The
DirectoryProfiletracks anauthzVersion(timestamp/counter). - If a relationship changes (e.g., a Deed is transferred or a Board term ends), the
authzVersionis bumped in the DB. - The platform detects the mismatch between the "Session Snapshot Version" and the "Latest Directory Version" and triggers a re-fetch of permissions (the "Dirty Snapshot" pattern).
- The
4. Relationship-Based Role Derivation
Permissions are derived through graph traversal:
- Email -> DirectoryProfile.
- Profile -> Deeds -> Units (Produces OWNER role + MY_UNITS scope).
- Profile -> BoardAssignment (Produces BOARD role + BUILDING scope).
- Profile -> VendorAssignment (Produces VENDOR role + ASSIGNED_CONTRACT scope).
5. Implementation Status (Jan 11, 2026)
Status: Waves 1-3 - COMPLETED Status: Wave 4 (Server Enforcement) - COMPLETED Status: Wave 5 (Forensic Enrollment) - COMMENCED
The context-aware engine is now active across the platform.
The following foundational interfaces have been codified in apps/platform/src/lib/context/types.ts:
5.1 ContextState (Active State)
Defines the current "View" of the user.
export interface ContextState {
activeHat: UserRole; // Why I am here (Persona)
activeScope: 'my_units' | 'building'; // Action radius
activeAssetId?: string; // Specific target (if applicable)
actingForPrincipalId?: string; // Representation target
isInitialized: boolean;
}
5.2 AuthzSnapshot (Performance Cache)
Computed on login/switch to avoid N+1 DB calls during navigation.
export interface AuthzSnapshot {
authzVersion: number; // For dirty invalidation
availableHats: UserRole[];
availablePrincipals: PrincipalOption[];
availableAssets: Record<string, AssetOption[]>;
capabilities: Record<string, boolean>; // Pre-computed boolean map
}
5.3 The Graph Resolver (Server)
Located in lib/auth/graph-resolver.ts.
Responsible for mapping a basic User Identity + Roles into a multi-hat AuthzSnapshot.
- Relationship Traversal: Queries Deeds/Leases to find owned/managed Units.
- Registry Mapping: Injects the Capability Bundle from the CAPABILITY_REGISTRY corresponding to the resolved personas.
5.4 The Context Provider (Client)
Located in lib/context/ContextStateProvider.tsx.
- Refreshes the AuthzSnapshot on mount via Server Action.
- Maintains activeHat, activeScope, and activeAssetId.
- Provides the usePlatformContext hook for workbenches.
- Wired: Now wraps the Entire Authenticated Application in app/(app)/layout.tsx.
5.5 The Context Switcher (UI)
Located in components/navigation/ContextSwitcher.tsx.
A Top-Header HUD that allows users with multiple capacities (e.g. Board Member + Owner) to swap their acting perspective instantly, which automatically filters the Registry-Driven Sidebar.
6. Server-Side Guard & Context Verification
As of Jan 06, 2026, the transition from "Hiding" (UI-only) to "Enforcement" (Server-side) is underway.
6.1 The verifyContext Guard (lib/auth/guard.ts)
This primitive serves as the final checkpoint for every write operation. It requires a clientContext (the claim) and a requirements object.
- Non-Breaking Stabilization (Wave 1.5): To support progressive migration, the
clientContextparameter is optional. - Fallback Verification: If context is missing, the guard performs a Direct Snapshot Verification using a provided
fallback.assertAssetIdfrom the action payload. - Identity Re-Verification: Re-resolves the
AuthzSnapshoton the server to ensure theactiveHat(or fallback identity) is valid. - Asset Boundary Enforcement: Validates that
activeAssetId(or the fallbackassertAssetId) is actually owned by or delegated to the user. - Hard-Deny (Wave 1.6): The guard explicitly throws an error if
assertAssetAccessis required but no asset ID is provided (either via context or fallback payload). No "defaults" or "wildcards" are permitted. - Capability Checks: Verifies specific binary permissions (e.g.,
can_vote).
6.2 Mandatory Governance Rule
For governed actions (Voting, Proxies), a target assetId MUST be identifiable either via the context OR the payload regardless of migration status. If no asset can be verified as owned/authorized, the action must hard-deny.
6.2 Implementation (Phase 3 Wave 1)
- Governance:
submitVoteandcastVoteActionnow require a verified context matching the target unit.castVoteActionstrictly rejects ambiguous calls that lack a specific asset context. - Finance: Secured
postJournalEntryActionagainst unauthorized administrative access.
6.3 Mock Snapshot Authority
Since the platform is in a transitional state from user.role (string) to full relationship traversal, the Graph Resolver (lib/auth/graph-resolver.ts) enforces conservative mock authority:
- Finite Unit Set: Only returns unit-304 and unit-102 as available assets.
- Strict Mapping: Assets are only granted if the base user role supports them (e.g., Guest has 0 assets).
- No Wildcards: The resolver contains no logic for "All Assets" or "Default to First Available" in the authoritative path, preventing accidental privilege escalation during development.
6.4 Privileged Guard Relaxation (Wave 1.7 - Jan 06, 2026)
To resolve Hazard 446 (Stale Context Scope Mismatch), the verifyContext guard has been enhanced with localized relaxation for administrative personas.
- Objective: Prevent administrative interactions (e.g., Work Order escalation) from being blocked by stale resident-mode context (e.g.,
activeScope: 'my_units') if the user's current hat inherently provides the required access. - Logic: If the
requirements.scopeis set tobuilding, and the user is acting under a privileged hat (ADMIN,STAFF,BOARD_MEMBER), the guard auto-corrects the scope claim instead of throwing aScope Mismatcherror. - Diagnostics: Auto-corrections are logged as warnings in the server console during development/testing to identify components that are failing to update their client-side context state correctly.
7. Hybrid Identity Model: "The Two Keys"
The platform operates a hybrid model to manage infrastructure ownership (Firebase) vs. human persona representation (GCP).
7.1 Key 1: The "Robot" Key (Firebase)
- Purpose: System-level infrastructure and data management.
- Used For: Real-time DB CRUD, Storage security, and low-level auth.
7.2 Key 2: The "Delegate" Key (OAuth/GCP)
- Purpose: Brand/Human Persona actions. Representation of the Community to external entities.
- Implementation: Domain-Wide Delegation allows the service account to impersonate
platform-admin@singulardream.orgfor tasks like mailing fromservice@singulardream.orgor managing Google Groups.
8. Branching Authorization Pattern (Standard 74.2)
To balance privacy with administrative efficiency, server actions implement a branching pattern:
- Branch A: Administrative (Building Scope) If the user is a known Admin/Staff, they bypass specific asset ownership checks for building-wide data.
- Branch B: Identity-Backed (Unit Scope)
If the user is a Resident/Owner, the guard strictly verifies their relationship to the specific
activeAssetIdor the payload'sassertAssetId.
9. AuthZ Migration & "Hostage Rescue" Strategy
The platform is transitioning from a legacy Firebase-claim based model to the 4-axis Relationship model. During this period, "Hostage" accounts (those using deprecated role strings) are handled via the Transition Bridge:
9.1 The Bootstrap Hat
Legacy roles are treated as high-level "Hats" without underlying relationship data.
- role: 'admin' -> Auto-maps to activeHat: STAFF + activeScope: building.
- role: 'resident' -> Auto-maps to activeHat: OWNER + activeScope: my_units.
9.2 The Mapping Guard
The verifyContext guard (Section 6.1) implements a "Soft-Fail" for legacy roles:
1. Attempt Snapshot: Try to resolve a full AuthzSnapshot from the Directory.
2. Legacy Fallback: If no Directory profile exists, build a temporary snapshot based solely on the legacy Firebase role.
3. Encouraged Migration: UI prompts users with "Partial Context" to complete their Directory onboarding.
9.3 Hard Deprecation Goal
By Wave 5, all server actions will require a DirectoryProfile reference. Custom claims will be used only for Initial AuthN, while the AuthZ Snapshot becomes the sole authority for capabilities.
10. The Delegation Standard (Protocol 74.3)
To support Proxy Voting and Property Management, the platform implements a first-class "Acting-As" protocol.
10.1 The Masquerade Contract
A user ($Actor) may act on behalf of another ($Principal) if and only if:
1. Grant Exists: A valid DelegationGrant record exists in the Directory.
2. Time-Bound: The current server timestamp is within [grant.validFrom, grant.validUntil].
3. Scope-Bound: The $Actor inherits only the intersection of the Principal's rights and the Grant's scope.
10.2 Delegation Scopes
| Scope ID | Description | Typical Use Case |
|---|---|---|
DELEGATE_VOTE |
Cast votes in Governance. | Proxy for Annual Meeting. |
DELEGATE_MANAGE |
Full Unit Operations (Work/Payments). | Property Manager. |
DELEGATE_READ |
Read-Only visibility. | Auditor / Lawyer. |
10.3 Traceability
Every action performed under delegation must be logged with the dual-identity signature:
{ actorId: "user-123", principalId: "user-456", context: "DELEGATE_VOTE" }
Created Jan 06, 2026. Phase 3.1: Consolidated Hybrid Identity and Branching Auth patterns. Updated Jan 08, 2026 with 6-Layer Persona support and Hostage Rescue strategy.