diff --git a/DESIGN.md b/DESIGN.md index 23a5826..14e84a1 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -10,7 +10,7 @@ A platform administrator works from a support or security request in a bright of ## Hierarchy -- Management-console logo, 256px tinted surface sidebar, and compact Lucide navigation. +- Management-console logo, 256px tinted surface sidebar, and compact Lucide navigation. Overview is the standalone first destination. `Admin Management` contains Workspaces, Users, and Platform Settings; `Governance` contains Admin Audit. Every established route remains unchanged. - Workspaces use a distinct workspace-shell icon with a top bar and side panel; the Kubernetes Layers symbol remains reserved for cluster navigation. The Users workspace filter repeats the same workspace-shell icon. - A persistent shield-labelled `Platform Admin` identity beneath the wordmark, reinforced by the favicon and browser title. - The browser title stays concise as `Admin · AcornOps`; the fuller privileged context remains visible inside the interface. @@ -40,7 +40,7 @@ A platform administrator works from a support or security request in a bright of - Primary controls use the management console's dark neutral treatment and activation controls use Acorn orange. - Directory toolbars follow the management console's inventory pattern: leading-icon search, high-value filters, a `Showing X of Y` loaded-result badge, and cursor-driven `Load more` pagination. Select controls use the management console's shared `Select` values directly: 44px default trigger, 16px horizontal padding, 14px medium UI type, orange interaction border/ring, fixed overlay layer 140, management menu shadow, compact-responsive option heights, and an orange-strong selected checkmark. Dropdown menus also use the management `custom-scrollbar` values: a 5px transparent track, orange 22% rounded thumb, and orange 40% hover thumb, with thin Firefox parity. By explicit platform-console refinement, chevrons remain muted grey when open and communicate state through rotation rather than color. The Users toolbar keeps its free-text workspace field, but workspace-name and ID suggestions use the same menu surface instead of a browser-native datalist. Its native browser clear glyph is replaced by a themed accessible clear control placed before the chevron. Platform clay remains wayfinding context and does not replace the shared orange control accent. - The Workspaces toolbar uses the same inventory rhythm as Users with a leading-icon workspace-name search, a management-aligned All statuses/Active/Suspended dropdown, loaded-result badge, and cursor-driven pagination. A compact Active/Suspended column makes access posture scannable. Because the producer does not expose a lifecycle-status query, an active status filter exhausts the existing search cursor and filters the complete result set locally; the default unfiltered view remains paginated. Plan details remain in the panel. Search maps to the existing `q` producer query and does not claim ID or creator search that the producer does not provide. -- Selecting a workspace opens a deep-linkable right-side panel while retaining directory context. `Workspace Details` presents Kubernetes connections, virtual-machine connections, current plan, creator, and creation date in that order. Kubernetes and VM values use compact neutral connected/plan-limit counters derived from `/admin/v1/system/config`; red is reserved for over-limit exceptions because ordinary capacity is context, not a success state. `Change Plan` sits directly beside the current plan value so the action remains attached to the object it changes. The dialog contains only the plan selector and supplies an accurate system-generated reason without asking for a ticket or free-form reason. There is no quota-usage row, quota override action, or separate Manage Workspace section. The aggregate member count is attached to `Workspace Access`, directly above the read-only member-and-role table. +- Selecting a workspace opens a deep-linkable right-side panel while retaining directory context. The directory and `Workspace Details` render the creator using display name, email, and immutable user ID as fallbacks. `Workspace Details` presents Kubernetes connections, virtual-machine connections, current plan, creator, and creation date in that order. Kubernetes and VM values use compact neutral connected/plan-limit counters derived from `/admin/v1/system/config`; red is reserved for over-limit exceptions because ordinary capacity is context, not a success state. `Change Plan` sits directly beside the current plan value so the action remains attached to the object it changes. The dialog contains only the plan selector and supplies an accurate system-generated reason without asking for a ticket or free-form reason. There is no quota-usage row, quota override action, or separate Manage Workspace section. The aggregate member count is attached to `Workspace Access`, directly above the read-only member-and-role table. - Workspace lifecycle is a distinct access-governance section with a more prominent Active or Suspended badge that matches the directory status vocabulary. Suspension and restoration both require typing the exact workspace name in the confirmation dialog; neither asks for a free-form reason or ticket reference, and each records an accurate system-generated reason with the administrator token. Cancel bypasses field validation and always closes the confirmation. Suspension blocks ordinary workspace-member authorization and discovery while retaining memberships, targets, workload state, references, and audit history. Restoration re-enables access through those retained memberships. Neither operation starts, stops, or modifies workloads. - Workspace access rows are grouped by descending role privilege—Owner, Admin, Member, Auditor, Viewer, then unknown roles—and alphabetized by identity within a role tier. The default workspace view remains read-only. `Manage Access` switches the existing section into a deliberate workspace-scoped editor without navigating away. A compact person-plus action beside `Done` grants an existing user access and exposes its purpose through hover/focus text; rows update roles or revoke individual access. The complete authoritative page set disables the sole Owner while allowing co-owner changes, and the producer remains authoritative during races. Atomic ownership transfer remains contract-blocked. Bulk workspace revocation is intentionally absent from this surface. The Users panel remains the complementary user-scoped editor for managing one identity across workspaces; both entry points use the same fixed membership routes and role catalog. - User filtering is limited to verification posture until the admin user contract exposes another actionable account state; disabled-user language must not be inferred from session or authentication data. @@ -52,7 +52,7 @@ A platform administrator works from a support or security request in a bright of - The select-then-update interaction is the explicit opt-in for a role mutation. It applies immediately without another dialog and sends an accurate system-generated reason required by the producer contract; optional `ticketRef` is omitted. - `Workspace Access` lists workspaces alphabetically, shows the workspace count and a compact `Grant Access` action in its section header, and avoids a duplicate role badge. Membership lifecycle metadata sits in a quiet inline footer beneath the role controls so it remains visibly attached to the access record without competing with the workspace identity. - Membership rows omit the raw `source` value because it has no authorization effect and can be mistaken for the user's authentication method. They show `Added` and, only when the displayed date differs, `Updated`; the footer wraps naturally on compact layouts. -- Admin Audit presents the authenticated human from the immutable OIDC issuer and subject. The ledger Actor column shows one readable administrator value only—recorded display name, then email, then immutable subject—without a role subtitle; the protected details retain the full governance-safe actor and credential evidence. The BFF workload token remains separate audit evidence and never substitutes for the human actor. The ledger translates known allowlisted action codes into concise Event labels and keeps the recorded reason directly beneath. Access grants and revocations display `Modified workspace access`, while role changes retain `Updated member role`; one `Modified workspace access` filter returns all three operations through a fixed producer `actionGroup`. Workspace suspension and restoration share `Modified workspace status`. Exact action codes remain visible as secondary context. Affected identifies `workspaceId` first, followed by `subjectType` and `subjectId` when both exist; workspace-only and subject-only events remain legible without inventing missing context. Pagination mirrors the management console audit log: 50-event cursor pages append in place, an intersection sentinel preloads the next page near the scroll boundary, and a visible secondary `Load more` button remains as the accessible fallback with loading and retry feedback. +- Admin Audit presents the authenticated human from the immutable OIDC issuer and subject. The ledger Actor column shows one readable administrator value only—recorded display name, then email, then immutable subject—without a role subtitle; the protected details retain the full governance-safe actor and credential evidence. The BFF workload token remains separate audit evidence and never substitutes for the human actor. The ledger translates known allowlisted action codes into concise Event labels and keeps the recorded reason directly beneath. Access grants and revocations display `Modified workspace access`, while role changes retain `Updated member role`; one `Modified workspace access` filter returns all three operations through a fixed producer `actionGroup`. Workspace suspension and restoration share `Modified workspace status`. Exact action codes remain visible as secondary context. Affected identifies the recorded workspace name first and falls back to `workspaceId`, followed by `subjectType` and `subjectId` when both exist; the immutable ID remains in projected event details and workspace filtering. Workspace-only and subject-only events remain legible without inventing missing context. Pagination mirrors the management console audit log: 50-event cursor pages append in place, an intersection sentinel preloads the next page near the scroll boundary, and a visible secondary `Load more` button remains as the accessible fallback with loading and retry feedback. - Success toasts follow the management console pattern: centered bottom surface, success icon, dismiss action, brief entrance motion, and an Acorn orange expiry indicator. Messages state the completed outcome without duplicating audit implementation details. - Membership suspension is an access state, not a role. The UI must not add `suspend` to the role catalog or label the existing membership `DELETE` operation as suspension. A future reversible action requires a dedicated `/admin/v1` suspend/restore contract; the current permanent removal is labelled `Revoke workspace access` and retains last-owner protection. - Each access record exposes a compact revoke icon beside `Update Role`, never as a role option. Its hover tooltip and accessible name say `Revoke workspace access`. The individual control uses a persistent light-danger treatment and becomes solid danger on hover. Confirmation names the user and affected workspace scope without mentioning account lifecycle, sends a deterministic contract-required reason, and retains the producer's last-owner safeguard. diff --git a/docs/contracts/README.md b/docs/contracts/README.md index eb83974..a59b7d1 100644 --- a/docs/contracts/README.md +++ b/docs/contracts/README.md @@ -35,6 +35,7 @@ The console has one runtime service dependency: `control-plane`. It does not cal - `lib/admin-contract.mjs` projects successful responses before browser delivery. - Workspace operational summaries and operational admin-audit actions are removed even if the producer returns them. - Admin audit attribution identifies the authenticated human from immutable OIDC issuer and subject fields, with display name and email snapshots for readability. The BFF workload token remains separate credential evidence. +- Workspace creator and admin-audit object labels are optional producer fields. The BFF projects them explicitly; the browser prefers the readable label and falls back to the immutable creator or workspace ID. - The mock store emits producer-shaped DTOs before the same projection is applied. - All accepted deviations are tracked in `DESIGN.md`. diff --git a/docs/contracts/manifest.json b/docs/contracts/manifest.json index 053bb79..111ed33 100644 --- a/docs/contracts/manifest.json +++ b/docs/contracts/manifest.json @@ -65,12 +65,13 @@ "workspacePlanPatchFields": ["planKey", "reason", "ticketRef?"], "workspaceSuspendFields": ["workspaceName", "reason", "ticketRef?"], "workspaceRestoreFields": ["workspaceName?", "reason", "ticketRef?"], - "adminWorkspaceResponseFields": ["clusterCount", "virtualMachineCount", "memberCount", "lifecycleStatus=active|suspended", "suspendedAt?"], + "adminWorkspaceResponseFields": ["createdByDisplayName?", "createdByEmail?", "clusterCount", "virtualMachineCount", "memberCount", "lifecycleStatus=active|suspended", "suspendedAt?"], "memberCreateFields": ["userId", "role", "createUserIfMissing=false", "reason", "ticketRef?"], "memberRolePatchFields": ["role", "reason", "ticketRef?"], "memberDeleteFields": ["reason", "replacementOwnerUserId?", "ticketRef?"], "responseCachePolicy": "All /admin/v1 responses set Cache-Control: no-store", "adminAuditActorFields": ["adminActorIssuer", "adminActorSubject", "adminActorEmail?", "adminActorDisplayName?", "adminActorRole", "authenticationTime"], + "adminAuditObjectFields": ["workspaceId?", "workspaceName?", "subjectType?", "subjectId?"], "privacyProjection": "BFF removes operational workspace fields, filters admin audit to governance action families, and removes target/run identifiers before browser delivery" } } diff --git a/docs/exec-plans/completed/admin-readable-identities.md b/docs/exec-plans/completed/admin-readable-identities.md new file mode 100644 index 0000000..7aea52d --- /dev/null +++ b/docs/exec-plans/completed/admin-readable-identities.md @@ -0,0 +1,50 @@ +# Admin Readable Identities + +Status: completed +Branch: `fix/admin-readable-identities` +Producer: `control-plane` + +## Goal + +Replace raw creator and workspace IDs in routine platform-admin displays with +readable labels while retaining immutable IDs for filtering, details, and +fallback. + +## Constraints + +- Browser traffic remains on same-origin `/admin-console-api/*`. +- The BFF explicitly projects every new field. +- Creator display order is display name, email, then immutable user ID. +- Audit object display order is workspace name, then immutable workspace ID. +- Operational fields, workspace audit events, and unrestricted metadata remain + unavailable. + +## Requirement Classification + +- `REQ-WSP-002`: add a readable creator-display rule. +- `REQ-AUD-001`: replace workspace-ID-first object display with + workspace-name-first display and immutable-ID fallback. +- `REQ-BND-002` and `REQ-BND-003`: preserved. + +## Validation Log + +- `npm run validate`: passed. +- All 95 tests passed, including creator-label precedence, immutable-ID + fallbacks, workspace-name audit objects, and BFF confidentiality projection. +- Lint, contract checks, requirement checks, harness checks, the production + build, and static/API/denial smoke routes passed. +- The rebuilt `platform-admin-console` container is healthy and connected to the + rebuilt control plane. +- Live control-plane data resolves `Development Workspace` to creator + `Dev User`, and all 10 current workspace audit events include + `workspaceName`. +- Browser rendering reached the healthy platform-admin sign-in flow; final + authenticated visual inspection requires the user to sign in because no + reusable authenticated browser session was available. + +## Completion Criteria + +- Workspace directory and details use readable creator labels. +- Admin Audit uses workspace names in the Object column and details. +- Focused projection and rendering regressions pass. +- Requirements, contract, repository, and live browser checks pass. diff --git a/docs/product-specs/current-requirements.md b/docs/product-specs/current-requirements.md index 306d8a7..a750c77 100644 --- a/docs/product-specs/current-requirements.md +++ b/docs/product-specs/current-requirements.md @@ -33,7 +33,7 @@ The executable baseline may use current source, durable docs, contracts, and foc | ID | Requirement | | --- | --- | -| `REQ-UX-001` | Reuse the management console's shell, typography, control structure, and interaction vocabulary. Retain the shield-labelled Platform Admin identity, the concise `Admin · AcornOps` browser title, and a noticeable but restrained cream-clay sidebar tint. The bottom administrator identity is a management-aligned account-menu trigger showing the authenticated human's display name or email and current platform role. Its popover repeats the identity, offers System, Light, and Dark appearance preferences, and provides the explicit logout action. Account settings remain absent because platform-admin identity and credentials are IdP-managed and the console has no local account mutation contract. The former sidebar tenant-boundary card is removed; Overview's `Governance data only` banner carries the lock icon and confidentiality reminder. | +| `REQ-UX-001` | Reuse the management console's shell, typography, control structure, and interaction vocabulary. Retain the shield-labelled Platform Admin identity, the concise `Admin · AcornOps` browser title, and a noticeable but restrained cream-clay sidebar tint. Overview remains the standalone first destination. `Admin Management` groups Workspaces, Users, and Platform Settings; `Governance` contains Admin Audit. The existing `/`, `/workspaces`, `/users`, `/settings`, and `/audit` routes remain unchanged. The bottom administrator identity is a management-aligned account-menu trigger showing the authenticated human's display name or email and current platform role. Its popover repeats the identity, offers System, Light, and Dark appearance preferences, and provides the explicit logout action. Account settings remain absent because platform-admin identity and credentials are IdP-managed and the console has no local account mutation contract. The former sidebar tenant-boundary card is removed; Overview's `Governance data only` banner carries the lock icon and confidentiality reminder. | | `REQ-CTL-001` | Dropdowns follow the management console's trigger, menu, option, checkmark, and scrollbar design. Open chevrons rotate but remain grey; the workspace filter uses a themed clear control. | ### Platform settings @@ -67,7 +67,7 @@ The executable baseline may use current source, durable docs, contracts, and foc | ID | Requirement | | --- | --- | | `REQ-WSP-001` | The Workspaces directory follows the Users inventory pattern with the accepted description, workspace-name search, a management-aligned All statuses/Active/Suspended dropdown, a loaded-result count, cursor-driven `Load more`, and an Active/Suspended status column. Because the producer has no lifecycle-status query, selecting a status exhausts the existing filtered workspace cursor and applies the status locally across the complete result set; unfiltered browsing remains cursor-driven. Plan details and plan filters remain out of the directory. | -| `REQ-WSP-002` | Selecting a workspace keeps the directory visible and opens an accessible, deep-linkable right-side panel. `Workspace Details` shows Kubernetes connections, virtual-machine connections, current plan, creator, and creation date in that order. Kubernetes and VM values render as neutral connected/plan-limit counters derived from the existing plan catalog, reserving red for over-limit exceptions, and `Change Plan` sits directly beside the current plan value. The console exposes no quota policy, quota usage row, quota override, or separate Manage Workspace section. | +| `REQ-WSP-002` | Selecting a workspace keeps the directory visible and opens an accessible, deep-linkable right-side panel. The workspace directory and `Workspace Details` show the creator using recorded display name, email, and immutable user ID as fallbacks. `Workspace Details` shows Kubernetes connections, virtual-machine connections, current plan, creator, and creation date in that order. Kubernetes and VM values render as neutral connected/plan-limit counters derived from the existing plan catalog, reserving red for over-limit exceptions, and `Change Plan` sits directly beside the current plan value. The console exposes no quota policy, quota usage row, quota override, or separate Manage Workspace section. | | `REQ-WSP-003` | `Workspace Access` merges the member count with a progressively loaded read-only table from the authoritative paginated `/admin/v1/workspaces/{workspaceId}/members` read. Members are grouped by descending role privilege—Owner, Admin, Member, then remaining roles—and alphabetized within a role tier. `Manage Access` deliberately switches this section into workspace-scoped management without leaving the panel. A compact person-plus action beside `Done` grants an existing user access with `createUserIfMissing: false` and an accessible tooltip. Rows can update roles or revoke individual access while the sole displayed Owner remains disabled; when multiple Owners are present, any one may be changed or revoked because another Owner remains. The producer's `LAST_OWNER` response remains authoritative if membership changes concurrently. Bulk workspace revocation is not exposed here. The Users panel remains the user-scoped way to manage one identity across workspaces. | | `REQ-WSP-004` | Workspace lifecycle shows the producer-backed active or suspended state in a prominent panel badge and the workspace directory status column. Suspension and restoration both require an exact workspace-name confirmation and supply a deterministic mutation reason without asking for a reason or ticket reference. Suspension blocks ordinary member access without deleting memberships or modifying workloads; restoration re-enables access using retained memberships. The dialog Cancel action always closes without being blocked by confirmation-field validation. | @@ -76,7 +76,7 @@ The executable baseline may use current source, durable docs, contracts, and foc | ID | Requirement | | --- | --- | | `REQ-AUD-002` | Production audit attribution identifies the authenticated human by immutable OIDC issuer and subject, with display-name/email and role snapshots for readability. The BFF service credential remains separate evidence. Human-actor filters use `adminActorSubject`; source-IP hashes, user agents, session hashes, and unrestricted metadata remain protected from browser delivery. This production requirement supersedes the prototype-only credential-attribution sentence in `REQ-AUD-001`. | -| `REQ-AUD-001` | Admin Audit follows the workspace audit-log structure with responsive Time, Event, Actor, Object, and Details columns. The Actor column shows one human-readable administrator name only, using recorded display name with email and immutable subject as fallbacks; role and credential evidence remain available in protected details rather than adding a second table line. The Details action uses the same Eye icon as the workspace audit log. Outcome is removed from the permanent table but remains available as a filter and in event details. Governance-safe filters cover event, workspace ID, human admin actor, outcome, Today, Last 24h, Past 7d, Past 30d, and a custom date-time range using only the producer's allowlisted query fields. Known action codes use concise labels while the code and outcome remain secondary context: grants and revocations display `Modified workspace access`, role changes display `Updated member role`, and suspension and restoration display `Modified workspace status`. One `Modified workspace access` filter covers all three operations through a fixed producer `actionGroup`. Object displays `workspaceId` first and then the affected subject. The compact details panel contains Time, Event type, Outcome, Actor, Object, Correlation ID, and every governance-safe projected field. `Safe metadata`, ticket references, source-IP hashes, user agents, session hashes, and unrestricted metadata remain absent. Missing correlation is labelled `Not applicable`. Events retain the management-console append-only 50-record cursor pattern, automatic near-boundary loading, and a `Load more` fallback. | +| `REQ-AUD-001` | Admin Audit follows the workspace audit-log structure with responsive Time, Event, Actor, Object, and Details columns. The Actor column shows one human-readable administrator name only, using recorded display name with email and immutable subject as fallbacks; role and credential evidence remain available in protected details rather than adding a second table line. The Details action uses the same Eye icon as the workspace audit log. Outcome is removed from the permanent table but remains available as a filter and in event details. Governance-safe filters cover event, workspace ID, human admin actor, outcome, Today, Last 24h, Past 7d, Past 30d, and a custom date-time range using only the producer's allowlisted query fields. Known action codes use concise labels while the code and outcome remain secondary context: grants and revocations display `Modified workspace access`, role changes display `Updated member role`, and suspension and restoration display `Modified workspace status`. One `Modified workspace access` filter covers all three operations through a fixed producer `actionGroup`. Object displays the recorded workspace name with immutable `workspaceId` as fallback, followed by the affected subject; the ID remains available in projected event details and as the workspace filter value. The compact details panel contains Time, Event type, Outcome, Actor, Object, Correlation ID, and every governance-safe projected field. `Safe metadata`, ticket references, source-IP hashes, user agents, session hashes, and unrestricted metadata remain absent. Missing correlation is labelled `Not applicable`. Events retain the management-console append-only 50-record cursor pattern, automatic near-boundary loading, and a `Load more` fallback. | ## Superseded And Excluded Behavior diff --git a/docs/product-specs/requirements-baseline.json b/docs/product-specs/requirements-baseline.json index fc9e1e2..b108008 100644 --- a/docs/product-specs/requirements-baseline.json +++ b/docs/product-specs/requirements-baseline.json @@ -18,7 +18,7 @@ "id": "REQ-SET-001", "summary": "Platform settings are fixed, privacy-projected, versioned, deployment-bounded, and writable only by the full platform-admin role.", "evidence": [ - { "file": "public/index.html", "contains": ["href=\"/settings\"", "data-route=\"settings\"", ">Settings"] }, + { "file": "public/index.html", "contains": ["href=\"/settings\"", "data-route=\"settings\"", ">Platform Settings"] }, { "file": "public/platform-settings-page.js", "contains": ["Member discovery", "AI policy", "Password signup", "expectedVersion", "Runtime override", "Deployment default"], "excludes": ["SMTP_PASSWORD", "OIDC_CLIENT_SECRET"] }, { "file": "lib/admin-route-policy.mjs", "contains": ["/admin/v1/system/settings", "member_discovery|ai_policy|password_signup", "admin:system:write"] }, { "file": "lib/admin-contract.mjs", "contains": ["projectPlatformSetting", "projectPlatformSettingConstraints"], "excludes": ["SMTP_PASSWORD", "OIDC_CLIENT_SECRET"] } @@ -63,9 +63,9 @@ }, { "id": "REQ-UX-001", - "summary": "The console reuses the management-console shell and clickable account menu with local System, Light, and Dark appearance preferences plus logout, without inventing local account settings.", + "summary": "The console reuses the management-console shell, keeps Overview standalone, groups resources and settings under Admin Management, places Admin Audit under Governance, and provides a clickable account menu with local appearance preferences plus logout.", "evidence": [ - { "file": "public/index.html", "contains": ["