Files
agentic-mobile-control/openspec/changes/remove-static-cloud-credentials/design.md
T
2026-07-13 19:45:53 +08:00

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 create command 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

  1. Build and deploy the image with the schema migration already included.
  2. Start the Cloud API using only database, production, and network settings.
  3. Run device-cloud-admin users create interactively to bootstrap an administrator, then verify Console login over HTTPS.
  4. Start each new Host Agent with its persistent identity path; it directly enrolls once and then uses its durable secret for future starts.
  5. 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.