mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 20:53:06 +00:00
feat(web): demo presentation mode for live product demos (#141)
* feat(web): demo presentation mode for live product demos The Global-Admin view toggle only ever reached the nav and the scope hooks. Every GA extra inside a tenant page was gated on the identity and stayed on screen when the toggle was switched off -- including the /health-check build cards, whose commit hashes hyperlink into the private repo, and the Decision Inspector button on the session detail page. New useGlobalAdminUi() (real GA *and* global view on) replaces the bare identity check at the five visible GA surfaces, so switching the toggle off now yields a genuinely tenant-admin-looking view. On top of that a hidden demo mode, armed by a self-consuming ?demo=1 (persisted to localStorage, then stripped from the address bar), forces the global view off for every consumer without touching the stored value, hides the toggle itself and the platform role badges, and silently replaces /admin/* with /dashboard rather than rendering the Access Denied card -- whose text would itself reveal that a platform area exists. ?demo=0 clears it and restores the previous setting. An unrecognised value leaves the stored flag alone, so a typo cannot drop the operator out mid-presentation. Admin Mode stays untouched: a tenant admin has it too, so it belongs in an honest demo. user.isGlobalAdmin is never faked globally -- that would drag canEditConfig along and leave a GA without an own tenant-admin role unable to demonstrate anything. Also hides the settings gear when neither toggle applies, which previously opened an empty panel for Operators and Viewers as well. Presentation only. Every rule mirrored here stays enforced server-side (TenantConfigValidation, GlobalAdminOnly -> 403); no backend file is touched and demo mode is not a security boundary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(web): refresh generated page-lastmod Regenerated by the build (scripts/generate-lastmod.js). The committed file had drifted: about/privacy/terms carried stale dates and /buy, /help and /plans were missing entirely, so the sitemap under-reported them. Pre-existing drift, unrelated to the demo-presentation-mode change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
52ae6541a7
commit
7323ca72b2
@@ -82,6 +82,7 @@ https://docs.autopilotmonitor.com (separate repository).
|
||||
# Web Portal
|
||||
|
||||
* [Portal Navigation Prefetch & the SWA Runtime Bottleneck](web/portal-navigation-prefetch.md) - Why every navigation Link sets `prefetch={false}`: the App Router's viewport prefetch fans out one RSC request per visible link and the Static Web Apps SSR runtime queues them server-side into a multi-minute tail. Records the follow-up measurement (the Standard plan changed nothing; the prefetch storm was an accidental keep-alive), where browser telemetry actually lives, and why this curl build cannot be trusted for HTTP versions.
|
||||
* [Demo Presentation Mode](web/demo-presentation-mode.md) - Two levels of hiding operator internals for live demos: the Global-Admin view toggle now also collapses the GA extras inside tenant pages (Telegram provider, cert-device binding, retention escape hatch, the `/health-check` build and commit cards), and a hidden demo mode armed by a self-consuming `?demo=1` additionally removes the toggle, the platform role badge and access to `/admin`. Presentation only - every rule stays enforced server-side.
|
||||
|
||||
# Security & Identity
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Log
|
||||
|
||||
## 2026-09-01 (5)
|
||||
|
||||
* **New**: `web/demo-presentation-mode.md` - the portal can now be shown live without leaking operator internals. The Global-Admin view toggle was only ever read by the nav and the scope hooks; every GA extra inside a tenant page was gated on the identity and stayed on screen when it was switched off - including the `/health-check` Backend/Portal Build cards with commit hashes hyperlinked into the private repo. New `useGlobalAdminUi()` (real GA **and** global view on) replaces the bare identity check at the four visible GA surfaces, so switching the toggle off yields a genuinely tenant-admin-looking view. On top of that a hidden demo mode, armed by a self-consuming `?demo=1` (persisted to localStorage, then stripped from the address bar via `history.replaceState`; `?demo=0` clears it), forces the global view off for every consumer without touching the stored value, hides the toggle itself and the platform role badges, and silently replaces `/admin/*` with `/dashboard` instead of rendering the Access Denied card - whose text would itself reveal the platform area. Admin Mode is deliberately untouched: a tenant admin has it too. `user.isGlobalAdmin` is never faked globally, because that would drag `canEditConfig` along and leave a GA without an own tenant-admin role unable to demonstrate anything. Both levels are presentation only - `TenantConfigValidation` and the `GlobalAdminOnly` 403s are unchanged.
|
||||
|
||||
## 2026-09-01 (3)
|
||||
|
||||
* **Update**: `agent/decision-engine.md` - agent_metrics_snapshot gains a signal-pipeline health block: monotonic SignalIngress totals (last ordinal, processed, pending, reentrant, queue peak) + decision_stage / decision_last_applied_ordinal / wg_sealing_level+score from the immutable DecisionState, wired via a read-only decisionStateProbe through IComponentFactory. Motivated by the WG-seal soak (project_wg_seal_postfix_soak_residual): a frozen engine and a deliberate Weak sealing verdict were previously indistinguishable in telemetry.
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
type: Concept
|
||||
title: Demo Presentation Mode — Two Levels of Hiding Operator Internals
|
||||
description: How the portal can be shown live without leaking operator-only detail. The existing Global-Admin view toggle now also collapses the GA extras inside tenant pages, and a hidden demo mode (armed by a self-consuming ?demo=1 URL parameter) additionally removes the toggle itself, the Global-Admin badge and access to the platform area. Presentation only — every real gate stays on the server.
|
||||
resource: src/Web/autopilot-monitor-web/lib/demoMode.ts
|
||||
tags:
|
||||
- web
|
||||
- presentation
|
||||
- global-admin
|
||||
- operator-tooling
|
||||
timestamp: 2026-09-01T20:30:00+02:00
|
||||
---
|
||||
|
||||
# Problem
|
||||
|
||||
The product is demonstrated live, and the operator doing the demonstration is a platform Global Admin. Two things leaked into those sessions.
|
||||
|
||||
**The Global-Admin view toggle only covered navigation.** `globalAdminMode` is a localStorage view switch read by `lib/navVisibility.ts` and the scope hooks (`hooks/useAggregatedAdminScope.ts`), so switching it off collapsed the sidebar and the cross-tenant data scope. Every GA extra *inside* an otherwise tenant-scoped page was gated on the identity `user.isGlobalAdmin` instead and stayed on screen: the platform-bot Telegram provider, the cert-device-binding toggle, the retention escape hatch, and — most exposed — the Backend Build and Portal Build cards on `/health-check`, which carry version strings and commit hashes hyperlinked into the private repository.
|
||||
|
||||
**Nothing existed for a stage demo.** The Global-Admin toggle and the purple "Global Admin" badge were always on screen, one click away from opening the platform view mid-presentation.
|
||||
|
||||
# Model
|
||||
|
||||
Two levels, both purely presentational.
|
||||
|
||||
**Level 1 — the Global-Admin view toggle.** Switching it off now yields a view indistinguishable from a tenant admin's. New hook `useGlobalAdminUi()` = *real GA* **and** *global view on*; it replaces the bare identity check at every GA-exclusive **visible** surface. For quick product demos between other work.
|
||||
|
||||
**Level 2 — demo mode.** A separate, hidden flag that forces the global view off for every consumer, hides the toggle itself and the platform role badges, and bounces the platform routes. For conference demos, where nothing may happen by accident.
|
||||
|
||||
`useCanMutatePlatform()` deliberately does **not** change: it stays bound to the identity and gates the mutating controls in the `/admin` area, where a view toggle must never decide what may be written.
|
||||
|
||||
`user.isGlobalAdmin` is never faked globally. That would be the shorter lever but it would drag `canEditConfig` (`app/settings/TenantConfigContext.tsx`) along, and a Global Admin without an own tenant-admin role would lose every write affordance — unable to demonstrate the product at all.
|
||||
|
||||
# Activation
|
||||
|
||||
`?demo=1` on any portal URL writes `demoMode=true` to localStorage and the parameter is then **consumed**: `history.replaceState` strips it from the address bar immediately, so it survives no screenshot and needs no devtools on stage. `?demo=0` clears it the same way. `1|true|on|` (bare) arm; `0|false|off` clear; anything unrecognised returns `null` and leaves the stored value alone, so a typo cannot drop the operator out mid-presentation.
|
||||
|
||||
The stored `globalAdminMode` value is never overwritten — demo mode masks it at read time (`effectiveGlobalAdminMode`), so the operator's usual setting returns with `?demo=0`.
|
||||
|
||||
# What each level hides
|
||||
|
||||
| Surface | Level 1 (view toggle off) | Level 2 (demo mode) |
|
||||
| --- | --- | --- |
|
||||
| Purple platform section in the sidebar | hidden | hidden |
|
||||
| Cross-tenant data scope, platform notifications | collapsed to the tenant | collapsed to the tenant |
|
||||
| Telegram provider, cert-device-binding toggle, retention escape hatch | hidden | hidden |
|
||||
| `/health-check` build + commit cards | hidden | hidden |
|
||||
| "Global Admin" / "Global Reader" badge in the user menu | shows "Admin" instead | shows "Admin" instead |
|
||||
| The Global-Admin toggle itself | still there | hidden |
|
||||
| `/admin/*` reached by a typed URL or bookmark | Access Denied card | silent replace to `/dashboard` |
|
||||
| Admin Mode (delete sessions) | unchanged | unchanged — a tenant admin has it too, so it belongs in an honest demo |
|
||||
|
||||
An already-configured Telegram channel stays visible and readable at both levels (`components/notifications/ChannelEditor.tsx`): it is the tenant's own channel, a tenant admin sees it too, and hiding its provider would make the next save destroy it.
|
||||
|
||||
# Not a security boundary
|
||||
|
||||
Both levels are presentation. Every rule they mirror is enforced server-side and unchanged: `TenantConfigValidation.ValidateTelegramChannelGate` rejects a non-GA Telegram write on both the PUT and the MCP field-patch path, the retention cap is validated in `ValidateModel`, and the platform endpoints answer `GlobalAdminOnly` with 403. Demo mode must never be described, documented or relied on as an access control.
|
||||
|
||||
# Citations
|
||||
|
||||
- `src/Web/autopilot-monitor-web/lib/demoMode.ts` — pure logic (parameter read/strip, effective view flag), covered by `lib/__tests__/demoMode.test.ts`
|
||||
- `src/Web/autopilot-monitor-web/hooks/useAdminMode.ts` — the three modes, URL consumption, effective `globalAdminMode`
|
||||
- `src/Web/autopilot-monitor-web/hooks/useGlobalAdminUi.ts` — the visible-GA-extras hook
|
||||
- `src/Web/autopilot-monitor-web/components/ProtectedRoute.tsx` — platform-route bounce while presenting
|
||||
- [portal-navigation-prefetch.md](portal-navigation-prefetch.md) — the other portal-wide behaviour switch
|
||||
Reference in New Issue
Block a user