feat: graduate Host Console to Community admins (#1669)

* feat: graduate Host Console to Community admins

Make Host Console available to Community and Admiral admins (system:console), add host-console-community for mixed fleets, and keep opaque API tokens off the host shell.

* docs: document Host Console deep links

Cover root and stack-scoped Console URLs, correct the phone treatment note, and pin parse/build round-trips in senchoRoute tests.

* fix: bind Host Console socket to the resolved node

Treat unresolved activeNode as loading, target the WebSocket with an explicit nodeId, and wait for stack deep-link hydration so the shell cannot open on the wrong node or compose root. Add regression coverage for node/stack retargeting and fail-closed directory resolution.

* fix: harden Host Console node binding, audit acting_as, and console_session tokens

Reject unknown or malformed nodeIds before spawning a PTY. Record hub operators in audit_log.acting_as for remote console_session bridges. Path-scope and one-time-consume console_session JWTs so Host Console mints cannot open container exec or be replayed.

* test: expect acting_as in audit CSV export header

Align the CSV export assertion with the P0-2B acting_as column added to audit log exports.
This commit is contained in:
Anso
2026-07-23 12:59:53 -04:00
committed by GitHub
parent ed5ca9c4f6
commit dd54a2e483
43 changed files with 1230 additions and 199 deletions
+2 -6
View File
@@ -43,11 +43,7 @@ export function isViewHidden(view: ActiveView, ctx: ReachabilityContext): boolea
if (!ctx.isAdmin && (view === 'auto-updates' || view === 'scheduled-ops')) return true;
if (!ctx.can('node:read') && view === 'fleet') return true;
if (view === 'host-console') {
// Defer experimental hide until ready so enabled deep links survive cold load.
if (experimentalDiscoveryReady(ctx) && !ctx.experimental) return true;
if (!ctx.isPaid) return true;
if (!ctx.isAdmin) return true;
return false;
return !ctx.can('system:console');
}
if (!ctx.isPaid) {
if (view === 'audit-log') return true;
@@ -67,7 +63,7 @@ export function isViewCapabilityLocked(view: ActiveView, ctx: ReachabilityContex
export function isFleetTabHidden(tab: FleetTab, ctx: ReachabilityContext): boolean {
if (!authzReady(ctx)) return false;
if (tab === 'container-labels' && !ctx.containerLabelsEnabled) return true;
// Defer experimental hide until ready (same cold-load contract as host-console).
// Defer experimental hide until ready so deep links survive cold load.
if ((tab === 'routing' || tab === 'secrets') && experimentalDiscoveryReady(ctx) && !ctx.experimental) {
return true;
}