Skip to content

M6-04 Administration in the console

No Date Ruling Reason
D-M6-04-0 2026-10-21 Base int/m1-wave52 at 15f2309, branch feat/M6-04-console-admin in its own worktree with feat/M4-28-admin-agents at b06cd0e merged first (stacked build, its own diff applied on the base because the base holds M2-20 and M2-21 as squashed commits); not merged locally and no pull request from this build, the integration lane ports the commits after the merge. The base names the chapters in German (01-produkt.md, 07-rechte.md, 08-identitaet.md, 10-backend-richtlinie.md, 15-client-architektur.md), so the sentence of A4 stands in 01-produkt.md 5.2 in English, and the rows stand in a section of the ticket because the base has no section 10. The brief relies on no core rule or contract change that the base lacks. Beside the files of the brief, services/server/Dockerfile and its context list change (the console stage copies packages/admin_ui/lib, the new target builds the test bundle) and infra/compose/test/compose.console-e2e-admin.yaml is new, because the console now depends on admin_ui and the suites need the bundle in the stack. Instructions of the launcher; merged names win (D-W-100).
D-M6-04-1 2026-10-21 Hosting admin_ui: the pages people, groups, devices, agents and audit of M6-02 host AdminBody, the body of the desktop's AdminShell without its navigation column, at full width (maxWidth: null); the console's own navigation stays. A place is ConsoleRoute: a detail adds its ID to the path of the page (/people/<id>, /groups/<id>, /agents/<id>), the grants are a tab of the groups page at /groups/grants, and the key of their target (space.<id>, channel.<id>) never enters the path, so no path of the console names content (ADR 0028, the route test). The repository is ApiAdminRepository over the console's cookie client; the IDs of Idempotency-Key come from Uuid7Generator of domain. admin_ui gets no console code: only AdminBody, the optional view-as hook and the two view-as widgets. Brief Design note 1; R-CL-01.
D-M6-04-2 2026-10-21 Step-up order in the browser: the confirmation dialog of admin_ui comes first and sends the request; 401 step_up_required of a signed-in console starts ConsoleActionStepUp through ConsoleStepUpInterceptor: a fresh createStepUpChallenge, then a dialog with the purpose; with passkey_options the passkey (WebAuthn, userVerification = required), otherwise password with one-time code, otherwise the providers of the new OIDC login. After success the session is read again and the request repeats once with the same options (same Idempotency-Key, body and CSRF header); a cancel, a cancelled passkey or the choice of the OIDC re-login ends the request with StepUpCancelled and sends nothing more (the OIDC re-login leaves the tab, so the change is not repeated and the dialog says so). The login and step-up operations below /api/v1/auth/ and the console's own reads marked consoleNoStepUpExtra are not touched, and a second step_up_required goes to the caller. This replaces the behavior of M6-02 where step_up_required during a session returned to the step-up screen of the login; that test now proves the dialog. Brief Design note 2; 08 6.2.
D-M6-04-3 2026-10-21 View as person without content: "View as person" in "Who has access?" and on a person's detail calls ConsoleAdmin.startViewAs, which opens /people/view-as, ends a stored view first, reads the owner's spaces (targets) before viewAsUser (afterwards every read of the session has the intersected rights) and shows the answer as a structure list and "Not visible for : ..." with text only. The banner (ViewAsBanner) comes from getConsoleSession.view_as and stands above the header of every page until endViewAs or its end time; at the end time the console reads its session with consoleNoStepUpExtra and once more after the one 401 step_up_required the server answers to the first request after the end (07 9.2), so no step-up is asked. After a reload the structure is gone (the answer lives in memory only) and the page says so; the banner stays. 01 5.2 A4 has the sentence for the console. Brief Design note 3; 07 9.2; ADR 0028.
D-M6-04-4 2026-10-21 The patrol web suites run in phase 2 of make console-e2e: patrol_cli 4.8.0 has no build of a web bundle for another server, so apps/console/integration_test/admin/admin_bundle.dart is a hand-written copy of the steps of its generated test_bundle.dart (explorer test, PatrolAppService, window.__patrol__getTests and __patrol__runTest), pinned with patrol 4.10.0. The Dockerfile target console-e2e-web builds it with the flags of the real console; the runner exports it, recreates api with console.assets_dir at the bundle and auth.step_up_max_age = 3s (existing keys) so every changing action needs the step-up of the adapter, and drives the patrol protocol from the pinned Playwright image in Chromium with the virtual authenticator and the CSP collectors of M6-03. The owner logs in and confirms through the API of the console session, the seed (spaces, Sam with a waiting device, a group, agents with personal and organization mode) goes through the API, and the channels of Marketing carry the names of the golden example of 07 9.3. Brief Design note 4; 15 20; SEC-T05.
D-M6-04-5 2026-10-21 The audit export of the console goes to a browser download (FileDownloads: a blob: URL and an anchor with download, revoked after the click); enrollment codes and token secrets stay in the state of their admin_ui widgets only, never in browser storage. Brief fixed requirement 15.
D-M6-04-6 2026-10-21 The token secret of A5 is a SelectableText instead of a Text in a SelectionArea: in the web build a SelectionArea makes the engine insert a style element without the nonce, which the console CSP blocks (style-src-elem, found by the patrol suite A5 and reproduced with a minimal bundle), and the secret was not shown. SelectableText raised no violation; the desktop keeps a selectable secret. Fixes found by the suites in the console itself: the Idempotency-Key generator draws with pow(2, bits) because 1 << 31 and 1 << 32 are wrong in the web build, and parallel requests that need a step-up share one prompt. SEC-T05 for the new screens; brief fixed requirement 16.
D-M6-04-7 2026-10-21 The suite A2 compares the preview text with the golden example of 07 9.3 line by line; only the date differs by design of the earlier tickets: the wizard sends the typed last day as an end at the start of the next day (M4-27, "valid until" includes the day), and the server's text names the day of that end, so typing 2026-12-31 shows "bis 01.01.2027" / "until 2027-01-01". The suite expects the golden with that day. Whether the text should name the last day instead is a question for the preview catalogue and stays open (report). After the code review: a logout clears the view-as structure, the "Not visible" list and the profile of the last person and sends the next login to the first page, and the start of the console reads its session once more after the one 401 step_up_required that ends an expired stored view, so a reload keeps the session. Brief Design note 4; code review of M6-04.
D-M6-04-8 2026-10-22 Port to main: the ticket's own commits from feat/M6-04-console-admin (built on int/m1-wave52) are cherry-picked onto origin/main as feat/M6-04-console-admin-main; M4-28, M4-29 and M6-03 are already on main, so the stacked merge of M4-28 drops out. On main the chapters have English names, so the sentence of A4 stands in docs/spec/01-product.md 5.2 and code comments cite 08-identity.md and 15-client-architecture.md; the rows of this ticket and the changelog entry are fragments (docs/decisions.d/M6-04.md, changelog.d/M6-04.md). In services/server/Dockerfile the smoke stage already exists on main before the console stage, so only the stages console-e2e-build and console-e2e-web are added. Rules of the landing lane (fragments, English chapter names on main).