Managed desktop admission implementation

Managed desktop admission implementation

Tracking: Sandbox #853. The delivery plan remains the full integration scope. The current production coverage report measures line and branch gaps; its required gate remains unmet. Earlier unmeasured-coverage notes below describe the historical runs before this measurement.

One-use attachment grants

`management/src/desktop_grants.rs` provides a file-backed SQLite grant store. It follows the existing effect ledger's WAL and synchronous FULL durability settings. It does not start a listener or enable desktop capability discovery. It adds no dependencies or changes to the legacy SSH/PTY authentication path.

The admission service must derive the binding from verified delegation and trusted workload identity. A binding includes subject, workspace, browser session audience, Bridge workload identity, desktop, instance, enrolled incarnation, and authorization generation. It is not deserializable from a browser request. The grant authorizes only the internal attachment-redemption operation; it is not a general API token or guest login credential.

Issue produces 256 bits of OS randomness, returns backend-only material in a zeroizing wrapper with redacted Debug output, and persists only its SHA-256 digest. Expiry is at most 60 seconds and never later than the verified delegation expiry. Empty or in-memory SQLite paths are rejected. Expired rows are removed during issuance.

Redemption atomically deletes the row only when all binding fields match and the grant is inside its validity window. Separate connections to the same database cannot both succeed. Unknown, expired, consumed and foreign tokens all return the same denial; mismatched attempts do not consume the owner's grant. Database failures return unavailable and never authorize admission. Successful consumption and explicit invalidation survive reopening the store.

Required integration before rollout

  • Verify delegation audience/actions and authoritative user/session/membership

status before issue and redeem. Grant possession alone is insufficient.

  • Bind actual gateway routes and SSH certificate principals to enrollment and

incarnation before dialing, then wire capability/create/get/attach/reconnect/ revoke authorization into the managed v2 path.

  • Serialize policy fencing and grant issuance using the desktop broker's durable

authority. `invalidate_desktop` removes currently outstanding grants; it does not prevent a caller with stale policy from issuing another grant.

  • Recheck authoritative identity on active renewal, enforce worker cancellation

and partition deadlines, and invalidate grants on logout or membership change. Deleting grants alone does not stop a running attachment or log out a guest.

  • Integrate absolute/monotonic deadline handling and altered-clock tests with

the broker. This primitive uses wall-clock validity; its tests do not qualify the distributed lease or clock-skew contract.

  • Qualify the real cross-instance SSH negative fixture and live stop deadlines.

Changed-module coverage and CI remain separate delivery evidence.

Admission coordinator

`management/src/desktop_admission.rs` composes delegation verification, current identity status, scoped enrollment, the durable grant store and a TCP dial. It is used by the optional authenticated capability-discovery route described below. The active transport entry point is not yet mounted on an attachment API.

The trusted identity adapter must verify organizational delegation and the Bridge workload proof together. Its output includes the subject, workspace, user session, browser-session audience, workload identity, gateway audience, allowed actions and expiry. These types cannot be deserialized from requests. The coordinator also checks the expected gateway audience and server-selected action. Discovery, create, get, attach, reconnect and revoke all require a current user/session/membership query. Only attach/reconnect can issue or redeem a transport grant. Proofs have redacted Debug output and zeroizing storage.

The enrollment adapter resolves within the verified subject/workspace. Returned ownership, desktop ID, instance/incarnation, generation and rights are checked before redemption. It supplies the SSH address and expected host key; callers cannot supply a destination or guest principal. After redemption, status and enrollment are checked again before dialing. Unknown/foreign/stale bindings share one denial, while source, storage, timeout and network errors return a generic unavailable error without endpoint or foreign-record details. The whole connect operation has a five-second deadline. Blocking SQLite work runs off the async executor; cancellation may consume a grant but cannot dial later.

`recheck` repeats verification and authoritative reads, rejecting logout, membership removal, user disablement, source unavailability, and changes to the binding, route, host key, generation or user session. It does not renew a worker lease or cancel an existing stream by itself. The caller must close on failure; the owned `proxy` entry point below enforces this for gateway streams. The internal dial returns the enrolled SSH identity and expected host key for the worker's required SSH authentication; raw TCP success is not SSH readiness.

Remaining production obligations:

  • Configure and live-qualify the organizational identity/workload verifier and

fresh status provider. The Keycloak adapter below is available; fixture responses do not qualify the deployed organizational issuer.

  • Wire the durable enrollment store below to authenticated guest enrollment.

Incarnation changes must also update guest principals. An IP address alone cannot establish workload identity.

  • Serialize policy fencing, issuance and controller admission through the

durable broker (#854). A second read reduces stale-read exposure but is not a transaction with policy mutation or a distributed ownership fence.

  • Mount the remaining authenticated v2 handlers and worker connector behind the

rollout gate, perform pinned SSH/RDP authentication, and enforce live cancellation and monotonic worker deadlines.

The coordinator tests use injected identity/enrollment providers and real loopback TCP listeners. They count effect-boundary accepts, including a race of 100 redemptions, and check that denials never reach a target. These are component integration tests, not organizational authentication, authenticated HTTP, real RDP, full Cockpit or multi-user qualification.

On 2026-09-13, the coordinator's ten tests passed within the full library run (992 passed, one existing live OpenSSH fixture ignored). They include a real five-second timeout against a stalled status provider, and revocation between grant consumption and target dial. `make lint`, the documentation link checker and `git diff --check` also passed. Line/branch coverage has not been measured; `cargo llvm-cov` is unavailable in this workspace.

Active gateway stream enforcement

`DesktopAdmission::proxy` owns both the incoming connection and enrolled target socket. It is the public transport entry point; bare `connect` is crate-private. The worker must still authenticate SSH and RDP inside this byte stream. A successful TCP proxy does not bypass pinned guest authentication or grant a guest login.

The proxy selects between byte transfer and an authorization monitor. A single-use broker fence channel is terminal: either sending the fence or losing its sender ends admission/renewal and drops both owned connections. The monitor continues running under traffic, and byte transfer continues while a fresh status query is pending. A fence can interrupt that query immediately rather than waiting for the identity provider's timeout. Returning a stop reason acknowledges local socket teardown, not guest logoff or cleanup.

Active gateway leases last 60 seconds and renew every 20 seconds. Each renewal calls the existing fresh identity and enrollment checks; denial or unavailable status closes the stream. Renewal expiry is calculated from the request's monotonic start time, so a delayed response cannot start a fresh 60-second window on receipt. The initial lease includes admission latency. The admitted delegation/certificate expiry is converted once into an immutable monotonic deadline; renewal never extends that deadline, even if a later token has a longer expiry.

Network tests exercise two real loopback TCP peers and the production timer constants. They verify fence and owner-loss closure, no grant consumption or dial when already fenced, removal of workspace membership, an unavailable source, continued traffic and immediate fencing during a stalled renewal, original authorization expiry, and a healthy stream surviving beyond its initial 60-second lease. They record both-peer closure times in test output. The identity and enrollment sources in these timing tests are controlled in-process fixtures; the separate Keycloak tests qualify its HTTPS adapter.

Remaining enforcement scope: connect this entry point to the authenticated v2 listener and durable broker; measure source-to-stop using the deployed issuer; implement worker-local expiry, incarnation/sequence fencing and partitioned controller handoff; terminate guacd/SSH and their queued input; and enforce the desktop's separate idle, detach-grace and immutable eight-hour authorization epoch. The local proxy's delegation deadline does not replace those #854/#856 requirements. Production startup enables discovery only with explicit configuration; workers remain unavailable.

Verification on 2026-09-14: the full management library run passed 1006 tests with one existing live OpenSSH fixture ignored. Six new stream tests use the production 20/60-second timers. Observed both-peer closure: membership denial 19,992 ms, stalled-source timeout 24,992 ms, original authorization expiry 2,153 ms, and four fence/owner-loss cases under 1 ms at millisecond resolution. A healthy stream passed a marker roundtrip after 61,001 ms with six status reads, then closed on fence. Formatting, documentation links and diff checks passed. These measured local gateway results do not establish deployed issuer propagation, guacd cleanup or distributed partition behavior.

Durable enrollment and policy fencing

`management/src/desktop_enrollment.rs` persists instance ownership, workspace, incarnation, numeric SSH route, expected host key and revision in SQLite with WAL/FULL durability. Desktop policies bind to that enrollment and carry an independent authorization generation. Every read is scoped by verified subject and workspace, reads current committed state, and requires an active instance and an unfenced policy with matching ownership and incarnation.

Trusted enrollment writers supply the revision captured before provisioning. An immediate transaction rejects stale writes and commits replacement enrollment and all affected desktop fences together. Ownership changes require a new incarnation. Retired incarnations and previously used incarnations cannot be reactivated; tombstones survive restart. Explicit policy activation checks both the current instance revision and previous policy generation. Existing desktop IDs cannot move to another instance.

This store does not authenticate guests or establish RDP readiness. Production provisioning must verify ownership, route and host key before committing and rotate guest SSH principals when incarnation changes. Grant issuance and controller ownership still need the durable lifecycle broker in #854.

Active proxies additionally poll enrollment policy every second with a two-second query deadline. Denial, changed binding or an unavailable policy source drops both gateway sockets. This monitor is independent of the twenty-second identity renewal. A test retires an instance through a separate SQLite connection and requires both real TCP peers to close within five seconds. These local timings do not qualify worker teardown or distributed controller partitions.

Authenticated provisioning enrollment

`POST /api/v2/instances/{instance_id}/desktop-enrollment` accepts enrollment facts from an explicitly authorized provisioning workload. Management startup reads `provisioner_certificates` from the desktop configuration; the default is empty. Each listed leaf thumbprint must also have an authenticated workload/client mapping in `bridge_certificates`. Listing a workload for discovery does not implicitly grant provisioning authority.

The request requires actual verified mTLS evidence from that provisioner and certificate-bound user delegation with Create rights. The admission coordinator checks certificate validity, delegation and fresh user/session/workspace status. Subject and workspace are copied from verified delegation. The JSON payload cannot provide either, or an actor field; unknown fields are rejected.

{
  "incarnation": "f3d75e94-d519-4f66-881e-e9d5df9a45bc",
  "ssh_address": "192.0.2.10:22",
  "ssh_host_key": "<independently verified host-key pin>",
  "expected_revision": null
}

The provisioner must derive instance ID, incarnation, route and host-key pin from its authenticated provisioning result, and rotate the guest's forwarding-only SSH principals when incarnation changes. It must not relay browser-supplied routing facts. This is a privileged provisioning assertion, not guest attestation performed by the endpoint. The existing runtime provisioning callers have not yet been wired to this API.

A new record uses a null/omitted revision; replacement requires the observed revision. Success returns only instance ID, incarnation and committed revision. Foreign-owner updates are denied inside the same transaction even when the caller supplies a new incarnation or knows the revision. Stale revisions and incarnation replay return a generic conflict. The existing replacement transaction fences affected desktop policies; it never activates a desktop policy or claims RDP readiness.

Payload reading is bounded to 8192 bytes and one second. Enrollment authorization and writing have a five-second coordinator deadline, capped once by the original authorization expiry on a monotonic clock. The blocking writer checks both delegation/certificate expiry and the monotonic deadline after entering its transaction and again before commit, rolling back expired queued work. A response timeout cannot undo a commit already in progress; callers reconcile the durable revision through authorized discovery before retrying. IdP status and SQLite commit are not a distributed transaction.

Tests exercise the complete mTLS HTTP server, including a ten-request revision race (one success, nine conflicts), foreign ownership, stale incarnation, missing provisioner role, missing Create rights, revoked membership and unavailable status. Forged operator/certificate headers and owner fields cannot establish enrollment. A controlled HTTPS Keycloak fixture loads startup configuration and creates its record through this endpoint before discovery. Storage tests reject expired queued writes and retain the original revision. These are component results; production provisioner integration and guest principal rotation remain required.

Authenticated capability discovery

`HttpServer::with_desktop_admission` optionally mounts `GET /api/v2/instances/{instance_id}/desktop-capability`. Startup rejects this option without a TLS listener requiring client certificates. Each request needs verified Bridge mTLS evidence and exactly one bounded Bearer delegation; operator roles and certificate headers cannot supply either proof. The route uses its own user/workload authorization independently of the legacy operator middleware. It rejects query strings and request bodies and emits non-cacheable responses. Body inspection permits zero payload bytes and has a one-second deadline, so undeclared or stalled bodies are rejected before delegation verification or status queries. Duplicate credentials, malformed/oversized tokens and invalid instance IDs are also rejected before contacting the identity authority.

Discovery verifies current user/session/membership and resolves the instance within the verified subject/workspace before a desktop policy exists. Unknown and foreign instances share the same denial. The response contains the instance, incarnation and revision but no owner, workspace, route or host key. It follows `rdp-cockpit.v1` and reports `supported: false`, `readiness: unknown`, with all features disabled until guest RDP readiness and isolation are verified.

Tests exercise the complete management HTTP server over real mutual TLS with legacy operator authentication configured, plus a direct router request carrying forged operator identity. Missing or foreign client certificates, invalid delegation, foreign instances/workspaces, revoked membership and source outage all fail closed. The identity source remains a controlled fixture.

The builder defaults to disabled. Management startup opts in when `AGENTIC_DESKTOP_CONFIG` identifies an absolute TOML configuration file. Missing configuration leaves the route disabled; an empty, malformed or unreadable value fails startup. The loader requires an HTTP TLS configuration with client certificate authentication before opening storage. It constructs the Keycloak adapter and shares the configured durable database between enrollment and grants.

The example configuration lists the issuer, confidential gateway client, exact secret-file path, optional issuer CA, accepted Bridge certificate-to-client mappings, and workspace group/action mappings. Version must be 1; unknown fields and unknown actions are rejected. Configuration and CA files are bounded to 64 KiB, cannot be symlinks, and must be operator-owned and not writable by others. Secret preflight uses the same private, bounded file checks as every request. All paths are absolute. The existing database parent must be mode 0700 and owned by the service UID; the database must be a private service-owned regular file with one link. Startup creates a missing database with mode 0600 and never repairs existing permissions. These storage permission checks currently require Unix.

Startup performs no issuer request, guest mutation or automatic enrollment. An explicitly authorized provisioner can subsequently use the enrollment API above; ordinary discovery workloads cannot write enrollment. An empty database denies every instance. Authenticated provisioning must populate verified ownership/incarnation/route/key records; startup must not infer these from an image name, heartbeat or operator actor string. The configured issuer contract must also be qualified before enabling discovery in a deployed service. Create/get/attach/reconnect/revoke handlers, real Bridge token exchange, guest enrollment wiring and live organizational qualification remain required.

Verification on 2026-09-14: 1020 management library tests passed, with the existing live OpenSSH fixture ignored. The new increment adds nine durable enrollment tests, one stalled-policy test and four authenticated HTTP tests. A committed external retirement closed both peers in 994 ms; a stalled policy read closed them in 3001 ms. The binary check, formatting, documentation links and diff checks passed. Line/branch coverage remains unmeasured.

Local library log: `/tmp/desktop-853-enrollment-library.log`, SHA-256 `3cd1e1ea55306456973ef263ace9b8df2a17d1bdccd03c28c9bc2bd5b78746f5`. Binary check log: `/tmp/desktop-853-enrollment-bin.log`, SHA-256 `8f282a0f0ea8b9939d617695c68a245704180a324eb2dde47812c967bc0cd9d5`. These are local component results, not deployed Cockpit/RDP qualification.

Startup integration verification on 2026-09-14: 1026 library tests passed (one existing live OpenSSH fixture ignored); binary compilation, formatting, documentation links and diff checks passed. Six startup tests cover configuration, secret and database rejection plus persistent reopen. The extended HTTPS/mTLS fixture loads the production configuration, discovers a durable enrolled instance, and denies a foreign certificate, removed membership and retired instance. No deployed issuer, configuration, enrollment or guest was changed; temporary fixture files are removed and fixture listener tasks aborted on scope exit.

The first focused run failed one case and the first full run failed two cases because fixture configuration/CA files inherited the environment's group-write umask. Explicit fixture file modes fixed setup; production checks were retained. Final library log `/tmp/desktop-853-startup-library.log` SHA-256: `06094bca7ce91b022a3d1ade07b13f82acecddb16388445a947817f2b2c81136`. Binary log `/tmp/desktop-853-startup-bin.log` SHA-256: `21c74f1bb3a209a056e041e0a6e231e69b8ef72eca02cd24a378fbb20fe7f7e7`. Failed full-run log `/tmp/desktop-853-startup-library-attempt1.log` SHA-256: `b8af81f7b3b3b55fdf9d523cde17f238414d645ed55d32c6241ce6ef8817206f`. Coverage and live organizational qualification remain outstanding.

Keycloak identity adapter

The real Keycloak 26.7.3 experiment confirms native certificate-bound standard exchange and introspection, including rejection of a wrong/missing certificate and logout invalidation before token expiry. The current Bridge's browser-session claim is still absent from that native baseline; the gateway continues to require it. This experiment does not qualify deployed issuer/Bridge behavior or the full desktop lifecycle.

An opt-in issuer browser-binding provider now supplies that claim for the configured Bridge client/certificate. Its live qualification covers distinct browser bindings sharing one user session, preserved introspection, malformed requests, rebinding/tampering denial and separate-client bearer-to-bound exchange. The Bridge still needs to derive, submit and verify the authenticated browser binding and configure its issuer mTLS transport. These component tests do not qualify the deployed identity path.

`management/src/desktop_keycloak.rs` implements `DesktopIdentityAuthority` with online token introspection and fresh user, session and group reads. Configuration is explicit: HTTPS realm issuer, confidential gateway client ID, a private client-secret file, accepted Bridge certificate thumbprints mapped to exchange client IDs, and workspace-to-group-ID/action mappings. No production issuer, group name, shared operator credential or environment default is assumed. The adapter is constructed by the optional startup configuration above. The discovery router accepts that admission service.

The TLS listener now supplies `DesktopMtlsPeer` from the verified client leaf. The adapter requires this typed evidence, checks certificate validity on every verification, and limits delegation lifetime to the leaf's expiration. An actor string, certificate header or caller-provided thumbprint cannot create it. The existing CN-based operator policy is unchanged.

The gateway uses the configured confidential client to introspect delegation. It requires active status, matching issuer, a single gateway audience, the configured exchange client (`azp`), unexpired `exp`, valid `nbf` when present, and `cnf.x5t#S256` matching the actual Bridge leaf. This relies on authenticated HTTPS introspection at the pinned issuer, not on decoding an unverified JWT. See Keycloak OIDC endpoints.

The issuer must also supply `sub`, `sid`, `workspace_id`, `desktop_browser_session_audience`, and action scopes. Mapping is intentionally bounded: `desktop:view` grants discovery/get, `desktop:create` grants create, `desktop:attach` grants attach/reconnect, and `desktop:close` grants revoke. The enrollment adapter still owns per-instance entitlement and policy fencing. These desktop claim requirements are an integration contract, not a claim that stock Keycloak or the existing realm already produces them.

Current status does not reuse token groups. Each check obtains a service token and reads the Keycloak Admin API user record, online sessions and current groups. The exact subject must remain enabled, the exact session must belong to that subject, and current configured workspace groups must still cover the delegated rights. Group removal or a rights reduction denies renewal even while token introspection remains active. The service account requires access to those reads; the adapter has no write, impersonation or password-reset operation.

Verification and status each have a whole-operation five-second deadline; the coordinator also bounds the combined connect operation. HTTPS redirects and proxy environment configuration are disabled. JSON bodies are limited to 64 KiB; membership results over 100 entries fail closed. Secret files must be regular, at most 4096 bytes, and inaccessible to group/other users on Unix; symlinks and control characters are rejected. Platforms without this file-permission check return unavailable. Proof/secret buffers are zeroizing, and errors contain no source URL, identity metadata, credentials or response body.

Compatibility gap identified during implementation: Cockpit's current Keycloak exchange caches an ordinary exchanged access token. It does not yet request certificate-bound exchange or mint the required browser-session claim. Its backend sends a client certificate, but that alone cannot add those issuer bindings to an existing bearer token. Such tokens are denied. Complete the coordinated issuer/Bridge exchange contract and its live negative tests before enabling desktop admission; do not fill missing claims from HTTP headers.

The adapter tests exercise a real HTTPS fixture and the management mTLS listener. They cover missing client certificates, a different CA-valid client, forged certificate headers, issuer/audience/action/session/certificate mismatches, current membership removal despite active introspection, response bounds, redirect refusal and a stalled issuer. They establish transport and adapter behavior against a controlled issuer fixture, not a deployed Keycloak realm or the full Cockpit login flow.

Adapter verification on 2026-09-13: eight Keycloak adapter tests passed in the full management library suite (1000 passed, one live OpenSSH fixture ignored). `cargo check --manifest-path management/Cargo.toml --bin agentic-mgmt`, `make lint`, the documentation link checker and `git diff --check` passed. No deployed realm, login, secret, group membership or client configuration was changed by this verification.

Verification

`cargo test --manifest-path management/Cargo.toml --lib` passed 980 tests on 2026-09-13. Six grant tests cover 100 simultaneous independent-connection redemptions (one success, 99 denials), every binding dimension, delegation and grant expiry, durable consumption/revocation, secret persistence/debug output, and malformed/unavailable storage. Formatting and `git diff --check` passed. These are automated component results, not authenticated RDP or Cockpit UAT.

Instance-bound SSH certificates

Trusted provisioning completion

`AGENTIC_DESKTOP_ENROLLMENT_CONFIG` opts `provision-vm.sh` into the completion client, after guest readiness and before recording final provisioning success. It requires a started VM, `--wait-ready`, the seed's incarnation, and a private JSON configuration using the example fields. Configuration, delegation and private-key files must be private regular files owned by the provisioner or root. Symlinks, hard links, unrecognized fields, HTTP origins, redirects and ambient proxies are rejected or disabled.

The guest host-key file must come from independently trusted provisioning state, such as a host key placed in the seed or an authenticated hypervisor inspection. The client does not use `ssh-keyscan`, ordinary readiness/TOFU helpers, browser actor fields or a guest's self-reported host key to establish that pin. It opens its own pinned SSH connection using the provisioning SSH key, requires clean cloud-init completion, checks root ownership of the principal path, and compares the exact instance/incarnation principal before sending enrollment. Guest output and execution time are bounded. This is a trusted provisioning control-plane inspection, not an interactive desktop attach.

The shell hook uses the dedicated `desktop-inspect` account and the private `inspection_key` specified in its configuration, independently of `SERVICE_USER` and runtime SSH keys. The provisioner must prepare a separate inspection key and configuration for each instance before seed generation. Only its derived public key enters the guest. Neither the key nor a runtime-access fallback is generated implicitly by the completion client.

When enrollment configuration is present, the seed adds this account with no sudo, supplementary groups or home creation. The root-owned inspection policy disables forwarding, PTYs, user RC files and CA authentication, and forces an isolated Python command. The fixed command ignores caller command text and stdin; it returns only the root-owned principal after clean cloud-init completion. It exposes no shell or file-transfer operations. The guest verifier checks both accounts and their effective SSH policies before activation. Managed profiles can therefore keep runtime SSH keys omitted. This does not prove isolation from a runtime account with guest-root privileges or replace fresh gateway authorization of desktop operations.

After guest verification, the client reads the latest delegation from its private file and sends one HTTPS/mTLS enrollment request. The gateway still independently requires the configured provisioner role, certificate-bound Create delegation, fresh identity status and durable ownership/revision checks. No subject/workspace field is accepted from this configuration or sent in the request. A matching instance/incarnation and positive revision are required to confirm success.

The whole CLI operation has a 30-second deadline; its HTTP socket timeout is five seconds. There is no automatic retry. A timeout or malformed success response may follow a committed write: provisioning records `desktop_enrollment_unconfirmed`, and the trusted controller must reconcile the revision before retrying. This does not advertise RDP readiness. Issuer/Bridge token minting and automatic production host-pin acquisition remain integration requirements.

Seven completion-client tests use a real local HTTPS server requiring a client certificate, with a controlled guest proof. They cover request fields, fresh credential reading, guest mismatch before HTTP, private-file checks, invalid pins, redirect denial, TLS trust errors, response mismatch and no retry on conflict. These are transport/component checks. A separate combined live qualification passes real guest inspection through the completion client and real admission server/store, with a controlled identity authority. Deployed issuer/Bridge and RDP qualification remain incomplete. Run `python3 images/qemu/tests/test-desktop-enroll.py` for the component checks. An optional `guest_ssh_port` defaults to 22 and binds both the host-key lookup and the enrolled route; invalid port types and ranges are rejected.

`DesktopSshIdentity` derives a versioned principal from two non-nil UUIDs: the enrolled instance and its current incarnation. The desktop signer accepts this typed identity, rather than a caller-supplied principal, and caps the certificate lifetime at 60 seconds. It clears default certificate extensions and enables only port forwarding. Signing uses a mode-0700 scratch directory on Unix, cleans it on failure, and returns a generic signing error without disclosing the private CA path. Existing SSH lease signing remains unchanged.

The shipped guest account policy restricts the dedicated `desktop-tunnel` account to certificate authentication and local TCP forwarding to `127.0.0.1:3389`. It explicitly disables authorized keys and external key/principal commands, passwords, interactive authentication, session channels, user RC files, agent/X11/tunnel/socket forwarding, and remote listeners. The live fixture consumes this exact file, substituting only its private paths and current test account. Permissive global key and forwarding settings in the fixture confirm that the account policy overrides them.

VM seed provisioning now opts in through `AGENTIC_DESKTOP_SSH_INCARNATION_ID`, `AGENTIC_DESKTOP_SSH_CA_PUBLIC_KEY` (an Ed25519 public-key file), and `AGENTIC_DESKTOP_SSH_WORKER_ADDRESS` (the worker's guest-visible source IP). `provision-vm.sh` supplies the canonical `AGENT_INSTANCE_ID` and allocated guest address, then runs the seed transform after profile/loadout generation and before ISO creation. No desktop inputs means a byte-for-byte no-op; partial/invalid inputs fail without modifying the seed. The incarnation must come from trusted provisioning lifecycle state. The script does not invent one or accept browser-provided ownership.

The Ubuntu seed creates a locked-password `desktop-tunnel` account with no sudo, no supplementary groups, no home creation and a nologin shell; it writes the CA, principal, policy and verification inputs as root-owned files. The final cloud-init command invokes the guest verifier. It checks account/group and trust-file permissions, appends the policy include, validates syntax and effective policy for the worker/guest IP pair on port 22, requires PAM and DNS-independent hostname matching, then reloads or starts SSH. It establishes and checks `/run/sshd` for socket-activated Ubuntu installations. Failed syntax/effective-policy checks restore a newly appended include and do not request a reload. Verification failure fails that cloud-init command.

The include belongs at the end of the daemon configuration after global directives. Do not place this Match block in the existing early `sshd_config.d` include: subsequent global-only directives may then be invalid. Earlier matching blocks can also override this policy; `sshd -t` and `sshd -T -C user=desktop-tunnel,host=localhost,addr=<worker-address>` must validate the effective restrictions and trust paths before enrollment. Account eligibility under Ubuntu 24.04.3's actual PAM configuration passed the booted-guest qualification, including a successful forward through the locked, nologin account and the prohibited-channel tests. Other images still require qualification; the unprivileged daemon fixture alone does not establish account eligibility.

The principal file must contain only the enrolled instance/incarnation principal. The runtime must replace it atomically with a fresh incarnation before publishing replacement enrollment, preserving root ownership and permissions. The live test checks that the same daemon rejects the previously accepted certificate and accepts the new certificate without a reload. This tests authentication of new connections; it does not revoke established channels. Runtime rotation wiring, controller fencing of existing workers, independent route/host-key verification, and authenticated RDP readiness remain required. The seed transform does not call the enrollment API, declare readiness or prove separation from privileged runtime accounts. No guest policy is installed or account created by management startup. Existing seeds and non-opted-in provisioning remain unchanged.

Seed/verifier regression coverage: five tests passed, covering no-op behavior, typed identity and real CA material, account/trust emission, atomic rejection, effective-policy failure rollback and unsafe permissions. Existing cloud-init transport (125 checks), loadout generation (184 checks) and provisioning dry-run regressions also passed. These checks do not substitute for a booted Ubuntu VM or an authenticated RDP session.

The live Linux fixture uses two unprivileged OpenSSH daemons, a generated shared CA, individually pinned host keys, and a counted loopback TCP target on 3389. Its standalone daemon configuration enables `StrictModes`, restricts `PermitOpen` to `127.0.0.1:3389`, and uses `MaxSessions 0` to deny session channels. It does not read or modify the host daemon configuration. See the OpenSSH daemon policy reference and certificate options.

Run the explicit fixture with:

cargo test --manifest-path management/Cargo.toml --lib live_desktop_certificates -- --ignored --nocapture

It requires Linux, `sshd`, `ssh`, `ssh-keygen`, `getent`, the current user's private passwd home, and unused loopback port 3389. An occupied port fails the test; do not stop an existing service to make it pass. Ordinary library tests mark this fixture ignored rather than claiming live coverage.

The original 2026-09-13 validation passed 982 library tests with this fixture ignored, followed by an explicit passing live run. The expanded fixture now consumes the shipped policy and validates rotation and additional denied channels. Its controlled outcomes are:

CaseExpected and actual
A certificate to A; B certificate to BTwo marker reads and two target accepts
A certificate to B; B certificate to APublic-key denial; no additional target accepts
Old A incarnation to current APublic-key denial; no additional target accepts
Legacy shared `agent` principal to APublic-key denial; no additional target accepts
Alternate forward destinationAdministratively prohibited
Mismatched pinned host keyHost-key verification failure before target dial
Shell sessionDenied
Bare key allowed by global configurationDenied by the account policy
SFTP subsystem and remote forwardingDenied
Atomic principal replacement, same daemonOld certificate denied before target dial; new incarnation accepted
CleanupDaemon groups stopped/reaped, target joined, temporary directory removed

The fixture first failed three times: the initial attempt exposed a principals file permissions error; diagnostic logging confirmed it on the second attempt; the third exposed permissive temporary-directory defaults. Explicit 0644 principals-file and 0700 directory modes fixed setup while retaining `StrictModes yes`. The final implementation and repeated live run passed.

Runtime: OpenSSH_9.6p1 Ubuntu-3ubuntu13.19, OpenSSL 3.0.13. Daemon SHA-256: `f30333b455e6fe10536d544992ea3dfa87cc1d83b8af7945a815c86f8b4f6f6d`. Final live-log SHA-256: `f4bb3ab5e966156155594e5073a6181f6044aed3f171aced2d3a0d2462335974`. Full library-log SHA-256: `fb35af6d43c86c446a1bc3aaa2ed7421cec6616564f13461211172a3241eb7ed`.

Expanded guest-policy fixture: one explicit test passed in 6.07 seconds, with 1033 unrelated tests filtered out. Effective account settings, all negative channels, principal rotation and cleanup passed; no host users or services were changed. Formatting, documentation links and diff checks also passed. Log `/tmp/desktop-853-guest-policy.log` SHA-256: `c3421b3212616115016a5578870ee7d78d4e6ce8729c00b41516195d96645731`. This run did not recompute coverage or change production Rust code; the earlier coverage table and its unmet overall gate remain the measured baseline. Logs for this run are retained under `/tmp/rdp-issues/`; these hashes identify local evidence, not published build artifacts. This qualifies the certificate and local forwarding fixture only, not RDP login, worker isolation, active revocation, or the full Cockpit path.