6.0 KiB
Context
The Cloud API currently chains configured bearer credentials, browser user sessions, and repository-backed Host credentials. Three JSON environment variables populate the first provider and a second provider protects first Host enrollment. The deployment image already contains the Console bundle, but Compose separately supplies its fixed location. The Console also retains a bearer-token compatibility screen and an administrator-only Users view.
The intended deployment has one trusted Cloud endpoint. A human operator signs
in with a Cloud account created or recovered through device-cloud-admin; a
new Host Agent registers itself once, generates its own high-entropy Host
secret, and persists the returned Host identity locally. Subsequent Host
requests continue to authenticate against the stored digest.
Goals / Non-Goals
Goals:
- Start Cloud production without any static credential JSON or startup requirement for one.
- Authorize browser operations by the existing user session and preserve its role, CSRF, expiry, audit, and interactive CLI controls.
- Accept direct first Host registration, then strictly bind all later Host operations to the registered Host identity and persisted secret.
- Remove obsolete Console and Compose configuration paths while keeping the
Jenkins-built Console available at
/console/.
Non-Goals:
- No personal-access-token, service-account, or replacement SDK credential scheme in this change.
- No user-directory view in the Console. Account provisioning and recovery remain administration-CLI operations.
- No Host enrollment approval, rate limiting, or multi-tenant trust policy. Network reachability to the trusted Cloud endpoint remains the boundary explicitly chosen for this deployment.
- No change to durable task, lease, device, or user database schema.
Decisions
D1. Remove configured bearer providers instead of leaving empty compatibility configuration
CloudControlConfig will no longer parse the three credential JSON variables
or require credentials in production. The Cloud application will compose public
authorization from UserSessionAuthProvider only, while Host operational
authorization remains RepositoryHostAuthProvider. An empty production user
table is allowed so an administrator can execute the interactive bootstrap CLI
after migrations complete.
Leaving the variables optional was rejected because it preserves two authentication modes and makes operators believe a deployed secret is needed. Adding user-issued API tokens was rejected because the requested outcome is no manual token configuration and it introduces a separate credential lifecycle.
D2. Direct enrollment is a single unauthenticated bootstrap operation
The enrollment endpoint accepts a fresh Host's generated instance identifier
and candidate secret without an Authorization header. It stores only the
candidate secret digest and returns a generated host_id; the Host persists
that result atomically. The configured enrollment-token provider and static
Host configuration are removed. Heartbeat, device enrollment, claim, renewal,
and result endpoints remain Host-bound and require the persisted bearer secret.
An opt-in enrollment flag was rejected because the target deployment always uses direct trust and the flag is another operational switch that can silently block first-run setup. Keeping token enrollment as a fallback was rejected for the same reason as D1.
D3. Keep user administration off the Console, not out of the control plane
The Console retains login, logout, current-account display, and password
change. It removes bearer-token controls, token storage, and all Users routes,
navigation, client calls, and components. The authenticated user administration
API and device-cloud-admin CLI remain available for controlled provisioning
and recovery; the CLI is the documented initial-admin path.
Removing the administration API entirely was rejected because it would make recovery tooling less complete and is unrelated to eliminating deployment tokens.
D4. Treat packaged static assets as an image contract
The Dockerfile sets CLOUD_CONSOLE_STATIC_DIR=/app/console-static after copying
the built SPA. Compose does not repeat this invariant. Application code keeps
the variable optional for source-based local development, where no bundled
directory exists.
Risks / Trade-offs
- [Risk] Any network caller can create a Host identity. → The accepted mitigation is reverse-proxy/firewall control of the trusted Cloud endpoint; every operation after enrollment still requires the Host-generated secret.
- [Risk] Existing integrations using static bearer tokens stop working. → This is intentional; create Cloud user accounts before deployment and migrate human workflows to session login.
- [Risk] An upgrade before an administrator exists leaves no human API
access. → Run the documented interactive
device-cloud-admin users createcommand immediately after migration and before exposing the Console. - [Risk] Existing static or token-enrolled Hosts cannot rely on removed configuration after upgrade. → Preserve their already persisted dynamic identities where present; otherwise perform a fresh direct enrollment.
Migration Plan
- Build and deploy the image with the schema migration already included.
- Start the Cloud API using only database, production, and network settings.
- Run
device-cloud-admin users createinteractively to bootstrap an administrator, then verify Console login over HTTPS. - Start each new Host Agent with its persistent identity path; it directly enrolls once and then uses its durable secret for future starts.
- Remove the legacy credential JSON values from the deployment secret store.
Rollback requires restoring an earlier image and its matching static credentials before restarting any Host that lacks a durable enrolled identity.
Open Questions
- None. The user explicitly accepts direct Host enrollment behind the network perimeter and does not require a Console user-directory surface.