Apollo Screen Access Integrity & Load Efficiency
| Field | Value |
|---|---|
| Status | Draft |
| Owner | TBD (Product) |
| Contributors | Oleksii Naboichenko, Apollo frontend team |
| Date | 2026-04-22 |
| Companion ADR | Modular Screen Architecture for uncrew-apollo-frontend |
Overview
This PRD defines the product-facing outcomes that must hold in the Apollo frontend (serving both uncrew and talos product surfaces) with respect to screen access — what a given user can see, reach, and load — and screen efficiency — what the user’s browser and our backend do on their behalf when they open the app.
It describes the correctness, performance, and consistency properties the app must satisfy. How those properties are delivered (routing structure, module organization, loading strategy, authorization primitives) is owned by the companion ADR.
Problem
Three related issues in the Apollo frontend affect users and the team shipping into it.
A live correctness gap exists. At least one restricted screen — the Settings panel — is reachable today by any authenticated user who types the URL, because its access check is absent while every other restricted screen hand-rolls its own. That is a direct, observable gap between the permission model we tell users about and what the app actually enforces. The gap is narrow today, but it is the exact class of defect that gets worse as screens are added, because there is no single place where access is declared.
Every user pays for every screen. When the app loads, the browser currently downloads code for all screens regardless of whether the user is authorized to use them. A warehouse operator loads the admin screens. A talos user loads uncrew-only screens. The cost is cold-start latency, bandwidth, and — less visibly — backend subscriptions to services like traffic, airspace, and notifications that get initiated unconditionally even for users who are not on screens that need them.
Sidebar visibility and route access can drift. A screen can be hidden from the sidebar but still reachable by URL, or vice versa, because visibility and authorization are decided in separate places. Every time we add a role, a tenant-specific allowlist, or a new screen, the surface for this drift grows.
These are not future problems; they’re today’s state.
Why Now
The direct-URL gap to Settings is a current defect. Separately, the set of screens, roles, and tenant-specific allowlists is expanding as talos diverges from uncrew — meaning the cost of the drift-prone model grows every quarter. Fixing the correctness gap and the structural cause of it are cheapest to do together.
Users
Primary users: every authenticated Apollo user, across uncrew and talos. The experience differences map to role bundles — operator, operations manager, supervisor, admin — and to tenant-specific allowlists.
Secondary user: the Apollo frontend team. Each new screen added today requires touching multiple files that independently assert access rules. Reducing that friction is a real secondary outcome.
Goals
- A user can reach — via any path, including direct URL, refresh, shared link, or deep link — only screens their roles, permissions, tenant, and feature flags permit.
- A user’s browser only downloads code for screens they’re authorized to use, at the time it needs to render them.
- Backend service subscriptions initiated on behalf of a user only run when the user is on a screen that requires them.
- The sidebar, the router, and every other surface that mentions a screen agree on authorization by construction — not by convention.
- Adding a new screen requires declaring its access rules in one place. No per-page role checks to remember.
Non-Goals
- Changing the set of screens or what any screen does.
- Changing the set of roles, permissions, or feature flags.
- Visual, layout, or UX changes to any screen.
- Splitting Apollo into multiple separately deployed applications.
- Building a generalized capability / plugin framework (see companion ADR — explicitly out of scope until justified by real reuse).
Success Metrics
We will consider this work successful when:
- Correctness: zero screens reachable by any authorized bypass path (direct URL, refresh, share link, deep link) for users who lack access. Enforced by automated regression tests covering every screen × role bundle combination.
- Drift: zero incidents in which a screen is shown in the sidebar but blocked by the router, or blocked by the sidebar but reachable by URL. Enforced by a CI check that both surfaces are generated from the same authorization source.
- Cold-start bundle: for a representative operator role bundle, initial bundle size drops by a target to be set against a baseline measured during the spike phase. Stretch target: measurable drop in median cold-start time for the same bundle.
- Backend subscription scoping: for a representative restricted user, the set of backend services the browser subscribes to matches the set of screens that user is authorized for — no subscriptions from screens the user cannot reach.
- Deploy resilience: stale-asset failures after a deploy recover without an operator-visible error that requires manual page refresh.
Baselines for the bundle and cold-start metrics will be captured before Phase 0 ships.
Requirements
All requirements describe observable behavior. How each is delivered — what primitive enforces access, what loading strategy the browser uses, how sidebar and router are generated — is decided in the companion ADR.
Functional
FR-01For every screen, a user’s ability to render the screen, navigate to it from the sidebar, reach it via direct URL, and refresh on it, all resolve to the same authorization answer.FR-02The sidebar displays only screens the current user is authorized to access.FR-03An attempt to reach an unauthorized screen — by any path — redirects the user to a safe default without exposing screen-specific errors.FR-04Screens that are currently missing an authorization check receive the same authorization treatment as all other restricted screens. No screen may rely on sidebar-invisibility as its only protection.FR-05When a user’s authorization context changes (role update, tenant allowlist change, flag toggle), the set of screens available to them updates without the user needing to take corrective action beyond normal auth re-evaluation.FR-06The browser only downloads the code required for screens the user is authorized to access, at the time it needs to render them.FR-07Backend service subscriptions sourced from a specific screen (e.g., traffic, airspace, operator notifications, mission ETA) only run while the user is on a screen that requires them.FR-08Adding or modifying a screen’s access rules does not require touching the screen’s implementation or the navigation surface independently. The rule set has a single point of declaration.
Non-Functional
NF-01Cold-start latency for representative role bundles decreases relative to baseline. Exact target set after baseline capture.NF-02Post-deploy sessions that encounter stale asset references recover gracefully — the user sees a brief reload or refresh prompt, not a broken screen.NF-03In-flight data associated with a screen (mission state, drone inventory, map state, shift state) follows the same lifecycle as before this work: values present when needed, cleaned up when the screen is left. No new leaks, no new missing cleanups.NF-04Authorization evaluation happens once per auth context change and is consistent across every surface that consumes it.
User Stories
| # | Story | Acceptance Criteria |
|---|---|---|
| 1 | As an admin, I want to trust that non-admins cannot reach admin-only screens through any URL path. | Every restricted screen is blocked by URL for every unauthorized role bundle; regression tests prove it. |
| 2 | As an operator on a modest laptop, I want the app to open quickly so I can start work without waiting for code I won’t use. | Initial bundle for an operator role bundle is smaller than baseline; cold-start time is measurably better. |
| 3 | As a talos user, I don’t want to load uncrew-only functionality, and vice versa. | Users on one product surface do not download code or initiate backend subscriptions for screens exclusive to the other. |
| 4 | As an engineer adding a new screen, I want a single place to declare who can access it, so I don’t have to remember to update the sidebar, the router, and each hand-rolled role check. | A new screen’s access rules are declared in one place; sidebar and router pick them up automatically. |
| 5 | As a user during a deploy window, I don’t want to see a broken page because my tab was holding an old asset reference. | Stale-asset errors recover automatically; user sees at most a reload, never a broken screen. |
| 6 | As a restricted user, I don’t want my session to initiate backend work for screens I can’t use — both for privacy and for backend load. | Backend service subscriptions initiated by the app match the user’s authorized screen set. |
Regulatory & Compliance
No FAA applicability. No DO-178C applicability (Apollo frontend is not safety-critical software). Access control is a general security concern; this work strengthens it. No new PII is collected, displayed, or transmitted. No new data retention obligations.
Risks (Product-Level)
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| The correctness gap persists for longer than necessary because the structural work is sequenced before the fix. | Medium | High | The fix to the missing screen guard must ship in the first phase, independently of the wider restructuring. |
| Authorization changes silently alter a user’s reachable surface mid-session in a way that disrupts their current task. | Low | Medium | Changes resolve on auth context change only; an in-progress task on a now-forbidden screen surfaces a clear message rather than a blank redirect. |
| Performance targets don’t materialize because most users have access to most screens. | Medium | Medium | Capture baselines by role bundle before committing to targets; scope targets to the bundles where real reduction is available. |
| The lifecycle of in-flight state (missions, drones, map, shift) regresses as the loading model changes. | Medium | High | Lifecycle is a stated non-functional requirement; must be tested before the new loading model ships. |
| A correctness test matrix of every screen × every role bundle is unwieldy and decays over time. | Medium | Medium | The test surface is generated from the single authorization declaration — adding a screen extends the matrix automatically. |
Implementation-level risks — stale-asset recovery, state initialization timing, authorization caching — are tracked in the companion ADR.
Phased Rollout
- MVP (Phase 0): Close the correctness gap on Settings and any other screen found to lack enforcement. Establish a single authorization surface that every screen uses. Behavior observable to users is unchanged except that direct-URL bypasses no longer work.
- Consolidation (Phase 1): Sidebar and router are generated from the same authorization source. Drift between them becomes structurally impossible.
- Efficiency (Phase 2): Per-screen loading kicks in; unauthorized screens are not downloaded. Lifecycle of in-flight state is verified against regression tests. Deploy-time stale-asset failures recover gracefully.
- Opportunistic (Phase 3): Backend subscription scoping — traffic, airspace, notifications, mission ETA — is tightened so restricted users do not trigger subscriptions from screens they cannot reach.
Exact sequencing within these phases — including how the authorization consolidation and the loading change are ordered — is owned by the companion ADR.
Open Questions
- What is the baseline for cold-start latency and initial bundle size, by role bundle? (Required before we can commit to specific NF-01 targets.)
- What is the real distribution of role bundles across the user base? (Informs where the performance win is most material.)
- Are there tenants with custom screen allowlists today? (Must be preserved unchanged by this work.)
- Are there any screens beyond Settings currently lacking an authorization check? (A sweep during Phase 0 should answer definitively.)
- Is there an external customer commitment around initial load time or browser bandwidth that we should be sizing targets against?
- For users whose authorization is reduced mid-session (e.g., role revoked), what is the intended experience — immediate redirect, end-of-action redirect, or forced logout?
Estimation Input
prd_sizing_input:
feature: "Apollo Screen Access Integrity & Load Efficiency"
scope_clarity: "B"
key_terms:
- "screen access"
- "authorization"
- "cold-start"
- "bundle size"
- "sidebar/router consistency"
risk_flags:
- "live-correctness-gap"
- "cross-cutting-refactor"
- "in-flight-state-lifecycle"
domains:
- "frontend-react"
regulatory: false
discovery_needed: truescope_clarity is set to B and discovery_needed is set to true because the performance targets (NF-01) depend on baselines we have not yet captured, and the full extent of unguarded screens is to be confirmed by the Phase 0 sweep.