Host Agent: - One-time local operator account bootstrap (PBKDF2-HMAC-SHA256, atomic 0600-permission write) gating the daemon's first unattended start via a new `setup` CLI subcommand. - Default control-plane URL now https://amcp.home.jerryyan.top (env var override unchanged). - Enrollment no longer requires a pre-issued token; falls back to zero-token self-service enrollment when none is configured. Cloud control plane: - CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED (default false) opt-in flag. - SelfServiceEnrollmentAuthProvider + ChainedEnrollmentAuthProvider: configured tokens still take priority; self-service only applies when no token matches, preserving edge-host-enrollment's token-bound path. - Fixed a latent bug in sql_repository.py::enroll_host: the token-conflict lookup used `== enrollment_token_digest`, which SQLAlchemy compiles to `IS NULL` when the value is None, so every self-service enrollment after the first would have falsely collided with an existing NULL-digest host. Skipped that lookup entirely when the digest is None. Docs/deploy: .env.example, compose.yaml, compose.deploy.yaml, CLOUD_DEPLOYMENT.md, MACOS_IPHONE_SETUP.md updated for the new flag, URL default, and required `device-host-agent setup` step. Verification: 494 non-integration tests pass; openspec validate --strict passes. PostgreSQL-backed contract tests and full manual end-to-end verification were not run (no Postgres/Docker or reachable cloud-api in this environment); noted as unchecked in tasks.md 7.2/7.4.
This commit is contained in:
@@ -84,6 +84,46 @@ Explicit `HOST_AGENT_HOST_ID` plus `HOST_AGENT_TOKEN` takes precedence and keeps
|
||||
the previous legacy behavior, including locally selected device IDs. This is
|
||||
the rollback and staged-migration path for existing deployments.
|
||||
|
||||
## Self-Service Edge Enrollment (Zero-Token)
|
||||
|
||||
The Host Agent's default `HOST_AGENT_CONTROL_PLANE_URL` is
|
||||
`https://amcp.home.jerryyan.top`. This is a single-operator home deployment
|
||||
default; override the environment variable for local/dev/test runs pointed at
|
||||
a different Cloud API.
|
||||
|
||||
When `HOST_AGENT_ENROLLMENT_TOKEN` is not set and no cached identity exists,
|
||||
the Host Agent enrolls with no bearer credential at all. The Cloud API only
|
||||
accepts that request when `CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED=true`
|
||||
(default `false`); a configured enrollment token, if presented, always takes
|
||||
priority over self-service. This trades away any approval step: **any caller
|
||||
that can reach the control-plane URL can register itself as a new Host.**
|
||||
There is no rate limiting or throttling on this path by design — the intended
|
||||
mitigation is network-perimeter control (firewall/reverse-proxy access to the
|
||||
URL), not an in-process limiter. Only enable the flag on a deployment where
|
||||
that network boundary is enforced.
|
||||
|
||||
Before the Host Agent's first unattended start, create the one-time local
|
||||
operator account interactively:
|
||||
|
||||
```bash
|
||||
uv run --package device-host-agent device-host-agent setup
|
||||
```
|
||||
|
||||
This prompts for a username/password and writes a PBKDF2-hashed credential
|
||||
file to `HOST_AGENT_LOCAL_ACCOUNT_PATH` (default
|
||||
`tasks/host_local_account.json`), gating only this first-run bootstrap step —
|
||||
it is not re-checked on subsequent unattended restarts. Running the daemon's
|
||||
default command without a controlling terminal before this file exists fails
|
||||
fast with a message naming the `setup` step, instead of hanging on a prompt
|
||||
no one can answer.
|
||||
|
||||
Running under Compose, create the account once before `docker compose up`:
|
||||
|
||||
```bash
|
||||
docker compose run --rm host-agent device-host-agent setup
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## PostgreSQL Deployment
|
||||
|
||||
Start from `.env.example`, replace every `change-me-*` value, and keep the
|
||||
@@ -154,6 +194,10 @@ renewal, and result operations for that host.
|
||||
pool, or operate as a Host; they can only create one durable Host binding.
|
||||
Use high-entropy values generated by the deployment secret manager.
|
||||
|
||||
`CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED` (default `false`) additionally accepts
|
||||
Host enrollment requests with no bearer token at all — see
|
||||
[Self-Service Edge Enrollment](#self-service-edge-enrollment-zero-token).
|
||||
|
||||
Do not place bearer tokens in command history, image layers, Compose files, or
|
||||
logs. Use environment injection or the deployment platform's secret manager.
|
||||
Rotate a token by deploying the updated Cloud API credential set and Host Agent
|
||||
|
||||
Reference in New Issue
Block a user