# Implementation Plan: Enterprise Access Boundary & Support Access Governance v1 **Branch**: `276-support-access-governance` | **Date**: 2026-05-05 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `specs/276-support-access-governance/spec.md` ## Summary Prepare one bounded support-access governance slice on top of the repo’s current system support and recovery seams. The narrow implementation path is to introduce one workspace-owned support-access grant history with exactly two scopes, reuse the current system workspace detail page as the request and active-state surface, reuse workspace settings as the owner-approval surface, extend existing system and admin audit views for support-access history, and harden the existing owner-repair utility so it requires both active break-glass and an active recovery-scoped support grant. This slice stays explicitly narrow. Filament remains v5 on Livewire v4, panel-provider registration stays in `apps/platform/bootstrap/providers.php`, no new globally searchable resource is introduced, and no asset registration change is expected. The plan does not create impersonation, a delegated admin browsing bridge, SCIM, or a second support console. ## Inherited Baseline / Explicit Delta ### Inherited baseline - `BreakGlassSession`, the system dashboard break-glass actions, and the current system break-glass banner already provide a global emergency recovery control with TTL and audit. - `RepairWorkspaceOwners` already exposes the current owner-repair utility and audit behavior, but today it only requires platform capability plus active break-glass. - `ViewWorkspace` already acts as the system-plane workspace detail surface and already owns bounded workspace-scoped admin actions. - `WorkspaceSettings` already acts as the admin-plane workspace singleton page and already supports workspace-owner-only actions. - `AuditLog` already acts as the admin-plane customer-visible history surface. - `AccessLogs` already acts as the system security history surface for platform auth and break-glass events. ### Explicit delta in this plan - Add one workspace-owned support-access grant history with bounded lifecycle and expiry. - Add one support-scope catalog with exactly `audit_view` and `workspace_recovery`. - Add one shared `SupportAccessGrantResolver` or manager so current detail, settings, recovery, and history surfaces consume the same support-access truth. - Add a request and end flow on `ViewWorkspace`. - Add owner approval or denial for `workspace_recovery` on `WorkspaceSettings`. - Extend `AuditLog` with a support-access filter and export path. - Extend `AccessLogs` with support-access lifecycle events. - Refactor `RepairWorkspaceOwners` so owner repair requires both active break-glass and an active recovery-scoped support grant for the chosen workspace. ## Technical Context **Language/Version**: PHP 8.4, Laravel 12 **Primary Dependencies**: Filament v5, Livewire v4, Pest v4, existing `BreakGlassSession`, existing audit infrastructure, existing workspace settings and system workspace detail surfaces **Storage**: PostgreSQL via a new workspace-owned `support_access_grants` table plus existing `audit_logs` and current session-backed break-glass state **Testing**: Pest v4 `Unit` plus focused `Feature` coverage **Validation Lanes**: fast-feedback, confidence **Target Platform**: Laravel monolith in `apps/platform`, reusing existing admin and system Filament pages **Project Type**: Web application (Laravel monolith with Filament panels) **Performance Goals**: no new queue family, no Graph calls, and DB-only support-access resolution on current detail, settings, and history surfaces **Constraints**: no impersonation, no delegated admin browsing bridge, no SCIM, no new support console, no new global-search resource, and no asset registration changes **Scale/Scope**: 1 new workspace-owned entity, 1 bounded resolver or manager, 4 existing page families, and focused extensions to existing auth or audit tests ## Likely Affected Repo Surfaces - `apps/platform/app/Models/SupportAccessGrant.php` as the new workspace-owned grant model. - `apps/platform/database/migrations/*_create_support_access_grants_table.php` for new persistence. - `apps/platform/database/factories/SupportAccessGrantFactory.php` for bounded test setup. - `apps/platform/app/Services/Auth/SupportAccessGrantResolver.php` and `SupportAccessGrantManager.php` as the shared lifecycle and mutation layer. - `apps/platform/app/Filament/System/Pages/Directory/ViewWorkspace.php` for request, end, and active-state summary. - `apps/platform/app/Filament/System/Pages/RepairWorkspaceOwners.php` for the combined support-access plus break-glass recovery boundary. - `apps/platform/app/Filament/System/Pages/Security/AccessLogs.php` for system-side support-access history. - `apps/platform/app/Filament/Pages/Settings/WorkspaceSettings.php` for owner approval or denial and a current support-access summary. - `apps/platform/app/Filament/Pages/Monitoring/AuditLog.php` for the support-access filter and export path. - `apps/platform/app/Support/Auth/PlatformCapabilities.php` for new bounded platform-side support-access manage capability. - `apps/platform/app/Support/Audit/AuditActionId.php`, `WorkspaceAuditLogger`, and `SystemConsoleAuditLogger` for stable support-access audit action IDs and structured metadata. - `apps/platform/tests/Unit/System/SupportAccessGrantResolverTest.php` plus focused system, auth, settings, monitoring, and access-log feature tests. ## Support Access Fit - Treat support access as a workspace-owned governance truth, not as an extension of `BreakGlassSession`. - Keep exactly two scopes in v1 so the slice stays on current repo-real support work: - `audit_view` - `workspace_recovery` - Keep support-access lifecycle distinct from break-glass lifecycle. - Keep owner repair as the one high-risk mutation that requires both active break-glass and active recovery-scoped support access. - Keep admin history export on the current audit surface rather than adding a second support-history page. ## UI / Filament & Livewire Fit - Existing operator-facing surfaces remain native Filament surfaces under Livewire v4. - No new Filament resource or globally searchable resource is required; current pages are sufficient. - `ViewWorkspace` keeps one dominant support-access action family: request or end support access. - `WorkspaceSettings` keeps approval scoped to pending `workspace_recovery` requests and must not become a second platform-control plane. - `AuditLog` and `AccessLogs` stay read-only history surfaces and only expand their filter or event scope. - Provider registration remains unchanged in `apps/platform/bootstrap/providers.php`, and no new asset strategy is planned. If a later shared asset becomes necessary, deployment remains the standard `cd apps/platform && php artisan filament:assets` path. ## RBAC / Policy Fit - Workspace and tenant membership remain the primary admin-plane isolation boundaries. Wrong-plane or non-member requests stay `404`; in-scope actors missing capability stay `403`. - Platform-side request and end actions should use one new bounded `PlatformCapabilities::SUPPORT_ACCESS_MANAGE` capability instead of reusing broad console visibility alone. - Workspace-owner approval should remain on `WorkspaceSettings` and should reuse existing owner-level manage semantics rather than adding a separate customer support role. - `AuditLog` export remains tied to the current admin-plane audit view capability. - Business-state blocking on the recovery page must remain distinguishable from authorization failure. ## Audit / Logging Fit - Every support-access request, approval, denial, activation, expiry, end, and ownerless waiver must write a stable audit action ID with structured metadata. - `AuditLog` remains the customer-visible history substrate. - `AccessLogs` remains the system-side security history substrate and expands to include support-access events. - No second audit subsystem is needed. ## Data & Query Fit - `support_access_grants.workspace_id` must be NOT NULL and indexed. - Prevent overlapping `requested` or `active` grants for the same workspace, platform actor, and scope. - Keep the v1 table bounded to the fields in `data-model.md`; do not add broad ticketing, provider, or impersonation fields. - `SupportAccessGrantResolver` should expose a single current summary for the active workspace plus current page context. - `RepairWorkspaceOwners` should query only the targeted workspace’s active recovery grant, not scan across workspaces. ## UI / Surface Guardrail Plan - **Guardrail scope**: changed surfaces - **Native vs custom classification summary**: native Filament - **Shared-family relevance**: system detail summaries, workspace settings approvals, audit-history filters, system security history, and recovery state messaging - **State layers in scope**: page, detail - **Audience modes in scope**: operator-MSP, support-platform - **Decision/diagnostic/raw hierarchy plan**: decision-first, diagnostics-second, support-raw-third - **Raw/support gating plan**: raw audit detail remains on existing history surfaces; default-visible content stays on current support posture and approval need - **One-primary-action / duplicate-truth control**: `ViewWorkspace` remains the one place to start or end support access; `WorkspaceSettings` remains the one place to approve or deny risky recovery; history pages explain rather than re-decide - **Handling modes by drift class or surface**: review-mandatory - **Repository-signal treatment**: review-mandatory now; future hard-stop candidate if a second support console or impersonation bridge appears inside this slice - **Special surface test profiles**: standard-native-filament, shared-detail-family, monitoring-state-page, exception-coded-surface - **Required tests or manual smoke**: functional-core, state-contract, exception-fallback - **Exception path and spread control**: the recovery page remains the named separately governed exception and must not spread its combined prerequisite logic to unrelated surfaces - **Active feature PR close-out entry**: Guardrail ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes - **Systems touched**: `ViewWorkspace`, `WorkspaceSettings`, `AuditLog`, `AccessLogs`, `RepairWorkspaceOwners`, `BreakGlassSession`, audit action IDs, and current audit loggers - **Shared abstractions reused**: current system detail pattern, current singleton settings pattern, current admin audit history, current system access logs, and current audit infrastructure - **New abstraction introduced? why?**: one bounded `SupportAccessGrantResolver` or manager, because current repo truth has no support-access lifecycle abstraction and several surfaces need the same grant state and dedupe rules - **Why the existing abstraction was sufficient or insufficient**: current break-glass and audit abstractions are sufficient for emergency recovery and history rendering, but insufficient for one workspace-scoped ordinary support-access lifecycle with approval and expiry - **Bounded deviation / spread control**: no local page-specific support-access helper is allowed outside the shared resolver or manager path ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: no - **Central contract reused**: `N/A` - **Delegated UX behaviors**: `N/A` - **Surface-owned behavior kept local**: request or approval inputs only - **Queued DB-notification policy**: `N/A` - **Terminal notification path**: `N/A` - **Exception path**: none ## Provider Boundary & Portability Fit - **Shared provider/platform boundary touched?**: no - **Provider-owned seams**: none in this slice - **Platform-core seams**: support-access grant truth, approval flow, break-glass separation, and workspace-scoped history - **Neutral platform terms / contracts preserved**: `support access`, `break-glass`, `workspace recovery`, `audit trail review` - **Retained provider-specific semantics and why**: none - **Bounded extraction or follow-up path**: future delegated support or impersonation only if promoted by a separate spec ## Constitution Check *GATE: Must pass before implementation begins and again before merge.* - Inventory-first / snapshot truth: PASS. This slice does not change tenant inventory or snapshot semantics. - Read/write separation: PASS. Every mutation is bounded, confirmation-protected where high risk, and audit-backed. - Graph contract path: PASS. No Graph or provider calls are introduced. - Deterministic capabilities: PASS. Existing canonical capability registries remain authoritative. - Workspace and tenant isolation: PASS. Workspace context and wrong-plane `404` remain unchanged. - RBAC-UX plane separation: PASS. `/system` handles request or end; `/admin` handles approval or history. - Destructive action discipline: PASS. High-risk approval and recovery remain confirmation-aware. - Global search safety: PASS. No new searchable resource is added. - OperationRun / Ops-UX: PASS. No new run family or `OperationRun` start surface is introduced. - Data minimization: PASS. The new entity stores bounded support-access truth only. - Test governance: PASS. Proof remains in one new unit family plus focused feature families. - Proportionality / no premature abstraction: PASS. One new entity and one bounded resolver or manager are the narrowest viable path. - Persisted truth: PASS. The new table represents independent product truth with its own lifecycle and audit need. - Behavioral state: PASS. Grant status and approval mode change recovery behavior, expiry, and audit outcomes. - Shared pattern first / UI semantics / Filament-native UI: PASS. Existing detail, settings, recovery, and history surfaces remain authoritative. - Provider boundary: PASS. No provider-specific vocabulary or dependency is added. - Filament / Laravel planning contract: PASS. Filament stays v5 on Livewire v4, provider registration remains in `apps/platform/bootstrap/providers.php`, no globally searchable resource is added, and no asset registration change is planned. **Gate evaluation**: PASS. **Post-design re-check**: PASS. `research.md`, `data-model.md`, `quickstart.md`, `contracts/workspace-support-access-governance.logical.openapi.yaml`, `checklists/requirements.md`, and `tasks.md` are present and aligned with the package. ## Test Governance Check - **Test purpose / classification by changed surface**: `Unit` for grant lifecycle, expiry, and dedupe rules; `Feature` for system detail request flow, owner approval, recovery boundary, and history surfaces - **Affected validation lanes**: fast-feedback, confidence - **Why this lane mix is the narrowest sufficient proof**: the slice reuses native Filament and current audit surfaces, so focused unit and feature tests can prove the new truth without browser automation - **Narrowest proving command(s)**: - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/System/SupportAccessGrantResolverTest.php` - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/System/SupportAccessRequestFlowTest.php tests/Feature/System/SupportAccessRecoveryBoundaryTest.php tests/Feature/Auth/BreakGlassModeTest.php tests/Feature/Auth/BreakGlassWorkspaceOwnerRecoveryTest.php tests/Feature/System/Spec114/AccessLogsTest.php` - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/Settings/WorkspaceSupportAccessApprovalTest.php tests/Feature/Monitoring/AuditLogSupportAccessHistoryTest.php` - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - **Fixture / helper / factory / seed / context cost risks**: moderate but contained; one new factory is needed, but existing platform user, workspace, membership, and audit fixtures can be reused - **Expensive defaults or shared helper growth introduced?**: no - **Heavy-family additions, promotions, or visibility changes**: none planned - **Surface-class relief / special coverage rule**: standard-native-filament relief for detail, settings, and history surfaces; exception-coded coverage for the recovery boundary - **Closing validation and reviewer handoff**: reviewers should rely on the exact commands above, confirm that no impersonation bridge or second console appears, verify that recovery still needs active break-glass plus active recovery support access, and verify that support history export remains workspace-safe - **Budget / baseline / trend follow-up**: none expected beyond a small feature-local increase - **Review-stop questions**: did the slice add more than two support scopes, did it bypass break-glass on recovery, did it create a second history surface, and did it let admin history leak outside the workspace scope - **Escalation path**: `document-in-feature` for contained naming drift; `reject-or-split` if the slice widens into impersonation, SCIM, or a second support console - **Active feature PR close-out entry**: Guardrail - **Why no dedicated follow-up spec is needed**: routine support-access upkeep should stay inside this feature unless future delegated support or impersonation is explicitly promoted as a separate slice - **Test-governance outcome**: keep ## Review Checklist Status - **Review checklist artifact**: `checklists/requirements.md` - **Review outcome class**: `acceptable-special-case` - **Workflow outcome**: `keep` - **Test-governance outcome**: `keep` - **Escalation rule**: if implementation adds admin-plane browsing, impersonation, SCIM, or a second support console, flip the workflow outcome to `split` before continuing ## Rollout Considerations - Land persistence and shared resolver or manager first. - Add workspace detail request or end flow next. - Add workspace-owner approval next. - Harden recovery enforcement only after support-access truth exists. - Add history filter and export last so the customer-visible trail reflects the final lifecycle semantics. - Keep Filament v5 on Livewire v4, provider registration in `apps/platform/bootstrap/providers.php`, global search unchanged, and assets unchanged. ## Risk Controls - Reject any implementation that adds impersonation or delegated admin browsing. - Reject any implementation that lets `workspace_recovery` proceed without active break-glass. - Reject any implementation that adds more than the two scoped support capabilities in this slice. - Reject any implementation that creates a second support history or approval surface outside the current detail, settings, and audit pages. - Reject browser-heavy proof as the default validation lane. ## Research & Design Outputs - `research.md` resolves the key design choices: workspace-scoped grants, two-scope catalog, recovery plus break-glass coupling, existing-surface reuse, and admin history export. - `data-model.md` records the new grant entity, state and approval semantics, dedupe rule, and derived summary shape. - `quickstart.md` provides the bounded reviewer flow, explicit approval and waiver expectations, and focused validation commands. - `contracts/workspace-support-access-governance.logical.openapi.yaml` captures the logical route and action boundaries for request, approval, end, history, export, and recovery gating. - `checklists/requirements.md` records the prep-time review outcome, workflow outcome, and test-governance outcome. - `tasks.md` keeps implementation bounded to current support and recovery surfaces. ## Project Structure ### Documentation (this feature) ```text specs/276-support-access-governance/ ├── checklists/ │ └── requirements.md ├── contracts/ │ └── workspace-support-access-governance.logical.openapi.yaml ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md └── tasks.md ``` ### Source Code (expected implementation surfaces) ```text apps/platform/ ├── app/ │ ├── Filament/ │ │ ├── Pages/ │ │ │ ├── Monitoring/ │ │ │ │ └── AuditLog.php │ │ │ └── Settings/ │ │ │ └── WorkspaceSettings.php │ │ └── System/Pages/ │ │ ├── Directory/ │ │ │ └── ViewWorkspace.php │ │ ├── RepairWorkspaceOwners.php │ │ └── Security/ │ │ └── AccessLogs.php │ ├── Models/ │ │ └── SupportAccessGrant.php │ ├── Services/ │ │ ├── Audit/ │ │ │ └── WorkspaceAuditLogger.php │ │ ├── Auth/ │ │ │ ├── BreakGlassSession.php │ │ │ ├── SupportAccessGrantManager.php │ │ │ └── SupportAccessGrantResolver.php │ │ └── SystemConsole/ │ │ └── SystemConsoleAuditLogger.php │ └── Support/ │ ├── Audit/ │ │ └── AuditActionId.php │ └── Auth/ │ └── PlatformCapabilities.php ├── database/ │ ├── factories/ │ │ └── SupportAccessGrantFactory.php │ └── migrations/ │ └── *_create_support_access_grants_table.php └── tests/ ├── Feature/ │ ├── Auth/ │ │ ├── BreakGlassModeTest.php │ │ └── BreakGlassWorkspaceOwnerRecoveryTest.php │ ├── Filament/Settings/ │ │ └── WorkspaceSupportAccessApprovalTest.php │ ├── Monitoring/ │ │ └── AuditLogSupportAccessHistoryTest.php │ └── System/ │ ├── Spec114/AccessLogsTest.php │ ├── SupportAccessRecoveryBoundaryTest.php │ └── SupportAccessRequestFlowTest.php └── Unit/ └── System/ └── SupportAccessGrantResolverTest.php ``` ## Complexity Tracking | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | New persisted support-access entity | Support access now needs its own lifecycle, expiry, and approval lineage | Session-only or audit-log-only approaches could not gate recovery and customer-visible approval safely | ## Proportionality Review - **Current operator problem**: current support and recovery seams lack one bounded workspace-scoped support lifecycle that is both customer-visible and strong enough to gate recovery - **Existing structure is insufficient because**: `BreakGlassSession` is global and current pages can only infer support state indirectly through scattered audit events - **Narrowest correct implementation**: one grant history table plus one shared resolver or manager, added to the current system detail, settings, history, and recovery pages only - **Ownership cost**: one new entity, one new resolver or manager, one new factory, one migration, and focused tests - **Alternative intentionally rejected**: break-glass-only governance and broad impersonation bridge - **Release truth**: current-release truth with bounded future preparation only