Files
2026-07-13 17:55:40 +08:00

29 lines
4.9 KiB
Markdown

## Why
The Host Agent today cannot start unattended on a fresh install: it requires an operator to hand-carry an out-of-band `HOST_AGENT_ENROLLMENT_TOKEN` (issued via `CLOUD_ENROLLMENT_TOKENS_JSON` on the cloud side) before it will enroll, and it accepts any `HOST_AGENT_CONTROL_PLANE_URL` an installer happens to set, which for the single home deployment at `https://amcp.home.jerryyan.top` is unnecessary ceremony. There is also no local gate preventing the daemon from starting with no operator ever having touched the machine, and no local record of who set it up. This change removes the pre-issued-token requirement for this deployment, fixes the control-plane address, and adds a one-time local setup gate, while leaving the existing token-based and static-credential paths intact for other deployments.
## What Changes
- Add a Host Agent CLI first-run bootstrap: if no local account file exists, `device-host-agent` interactively prompts (via `getpass`) for a username and password, hashes the password, and persists the credential atomically with restricted file permissions before continuing. On later starts, if the account file exists, the daemon starts straight into background polling with no prompt.
- Change `host_agent/config.py::load_host_agent_config` default for `HOST_AGENT_CONTROL_PLANE_URL` from `http://127.0.0.1:8001` to `https://amcp.home.jerryyan.top`; the environment variable still overrides it for development/testing.
- **BREAKING** (new deployments only, additive for existing ones): Remove the hard requirement for `HOST_AGENT_ENROLLMENT_TOKEN` when no local identity and no static `HOST_AGENT_HOST_ID`/`HOST_AGENT_TOKEN` are configured. `host_agent/enrollment.py::resolve_host_identity` now falls back to an unauthenticated self-service enrollment call when no enrollment token is configured, instead of raising `HostAgentConfigurationError`.
- Add a cloud-side self-service enrollment mode, gated by a new `CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED` flag (default `false`): when enabled, `POST /internal/v1/enrollments` accepts requests with no enrollment-token bearer credential and stores `enrollment_token_digest = NULL` for that Host row (the column is already nullable). When the flag is disabled, current behavior (token required, `401` otherwise) is unchanged.
- Existing static-credential (`HOST_AGENT_HOST_ID`/`HOST_AGENT_TOKEN`), token-based enrollment (`HOST_AGENT_ENROLLMENT_TOKEN` + `CLOUD_ENROLLMENT_TOKENS_JSON`), and revocation paths from `edge-host-enrollment` are unchanged and remain fully supported side by side with self-service enrollment.
- No changes to `host_agent/heartbeat.py` — periodic device-status reporting already exists and continues to run against the (now-fixed) control-plane URL; this change only confirms the wiring stays intact once the URL and enrollment path change.
## Capabilities
### New Capabilities
- `host-agent-local-bootstrap`: First-run interactive local account creation gate for the Host Agent CLI — credential storage format, hashing, one-time prompt behavior, and non-interactive skip on subsequent starts.
### Modified Capabilities
- `edge-host-enrollment`: Add a cloud-side self-service enrollment mode that does not require a pre-issued enrollment token, and change the Host Agent's default control-plane URL and fallback behavior when no enrollment token is configured. (Note: this capability's spec currently lives only in the not-yet-archived `openspec/changes/edge-host-enrollment/` change, not in canonical `openspec/specs/`; this change's delta is authored against that pending spec and should be reconciled when `edge-host-enrollment` is archived.)
## Impact
- **Host Agent (`apps/device-host-agent`)**: new `host_agent/local_account.py` (or similarly named) module and CLI wiring in `host_agent/cli.py`; `host_agent/config.py` default URL change; `host_agent/enrollment.py::resolve_host_identity` fallback behavior change; new local credential file (default path under the existing `tasks/` state directory, permissions `0600`).
- **Cloud Platform (`packages/cloud-platform/cloud`, `apps/cloud-api`)**: `control_config.py` gains `CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED`; `internal_api/api.py::enroll_host` gains a self-service path; no schema migration required (`enrollment_token_digest` is already nullable).
- **Dependencies**: no new third-party dependency for password hashing is planned (stdlib-based); to be confirmed in design.md.
- **Deployment/docs**: `.env.example`, `compose.yaml`/`compose.deploy.yaml`, and `docs/CLOUD_DEPLOYMENT.md` need the new flag documented; `docs/MACOS_IPHONE_SETUP.md` needs the first-run local-account step documented.
- **Security posture**: enabling `CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED` means any network caller reaching the control-plane URL can self-register as a Host with no approval step; this is an explicit, accepted tradeoff for the single-operator home deployment and is opt-in (default off) for other deployments.