Part of #3264. ADR 0021 §5, §6, §10 item 6. Design D2 and D4 in the umbrella.
Blocked by: #3265
Purpose
A remote caller must not choose who it is, reach operator-only routes, name paths on the Host machine, or make the allocator download components. The daemon needs a trusted principal on every request so the lease side can record a requester that survives restarts.
Required behavior
- The front-end drops every identity a client claims: the
x-agent-device-tenant and x-agent-device-principal headers, params.meta.tenantId, params.flags.tenant, and params.tenant / params.tenantId on lease methods.
- After authentication it adds
x-agent-device-principal: <principal> to the outbound loopback request, using the principal from the service credential.
- The daemon accepts that header only on HTTP requests that passed the daemon-token check. It validates the value with the tenant format, keeps it as daemon-internal state, and pins the request tenant to it with tenant session isolation. It never reads a principal from the body. With an auth hook configured, the header is refused with a typed reason.
/admin/* is not served (404). The policy runs through an admitRpc hook the proxy calls after authentication.
- The front-end refuses these with HTTP 403, code
UNSUPPORTED_OPERATION and a typed details.reason, never by matching error text:
host-admin-refused for lease.allocate on the host-allocated macos-app backend;
host-path-refused for inputs naming a Host path, batch steps included: the shared HOST_PATH_INPUT_KEYS declaration, positionals each command's schema names as paths, and path install sources without an upload. Only the client's exact temp locations for screenshots and recordings are accepted, and an upload id exempts only install and reinstall;
host-component-download-refused for allowDownload anywhere in a request;
host-script-refused for replay and test.
- Anonymous
GET /health returns only { ok, service: "agent-device-host", rpcProtocolVersion }. With the credential it returns the full payload, upstream included.
- Plain
proxy keeps its header allowlist, which never forwards x-agent-device-principal.
Completion conditions
One test per spoofing case from ADR 0021 §10 item 6:
- a client principal header is replaced by the server principal;
- a client tenant header is dropped;
- body tenant fields are dropped and the daemon sees the server principal as the tenant;
/admin/leases and /admin/human-control/holds are not served;
macos-app allocation is refused, whatever its case or spacing;
- Host paths hidden in URLs, upload ids, batch steps,
trace/push positionals, launchConsole and planted /tmp files are refused;
- a path install source and a Host-path flag get
host-path-refused;
allowDownload gets host-component-download-refused;
- anonymous health is minimal;
- plain
proxy does not forward x-agent-device-principal.
Dependencies
#3265 for the front-end process. The principal contract is shared with #3269. It is proposed in comments on #3264 and stays a proposal until the lease side agrees.
Example
POST /rpc (from the remote worker)
authorization: Bearer <service token>
x-agent-device-tenant: someone-else (dropped)
POST http://127.0.0.1:<port>/rpc (forwarded by Host)
authorization: Bearer <daemon token>
x-agent-device-principal: host-svc-3f9c2a1b
Part of #3264. ADR 0021 §5, §6, §10 item 6. Design D2 and D4 in the umbrella.
Blocked by: #3265
Purpose
A remote caller must not choose who it is, reach operator-only routes, name paths on the Host machine, or make the allocator download components. The daemon needs a trusted principal on every request so the lease side can record a requester that survives restarts.
Required behavior
x-agent-device-tenantandx-agent-device-principalheaders,params.meta.tenantId,params.flags.tenant, andparams.tenant/params.tenantIdon lease methods.x-agent-device-principal: <principal>to the outbound loopback request, using the principal from the service credential./admin/*is not served (404). The policy runs through anadmitRpchook the proxy calls after authentication.UNSUPPORTED_OPERATIONand a typeddetails.reason, never by matching error text:host-admin-refusedforlease.allocateon the host-allocatedmacos-appbackend;host-path-refusedfor inputs naming a Host path, batch steps included: the sharedHOST_PATH_INPUT_KEYSdeclaration, positionals each command's schema names as paths, andpathinstall sources without an upload. Only the client's exact temp locations for screenshots and recordings are accepted, and an upload id exempts onlyinstallandreinstall;host-component-download-refusedforallowDownloadanywhere in a request;host-script-refusedforreplayandtest.GET /healthreturns only{ ok, service: "agent-device-host", rpcProtocolVersion }. With the credential it returns the full payload,upstreamincluded.proxykeeps its header allowlist, which never forwardsx-agent-device-principal.Completion conditions
One test per spoofing case from ADR 0021 §10 item 6:
/admin/leasesand/admin/human-control/holdsare not served;macos-appallocation is refused, whatever its case or spacing;trace/pushpositionals,launchConsoleand planted/tmpfiles are refused;host-path-refused;allowDownloadgetshost-component-download-refused;proxydoes not forwardx-agent-device-principal.Dependencies
#3265 for the front-end process. The principal contract is shared with #3269. It is proposed in comments on #3264 and stays a proposal until the lease side agrees.
Example