88 lines
12 KiB
Markdown
88 lines
12 KiB
Markdown
# Individual local agent identities: implementation contract
|
|
|
|
Status: **implemented in source; native integration and Windows packaged-source/client checks pass; remaining acceptance gates are listed below**. This closes part of the individual agent identity/control gap recorded in [the requirements audit](requirements-audit.md). The current implementation and supported commands remain documented in [AGENT-API.md](../AGENT-API.md).
|
|
|
|
## Original baseline that motivated this increment
|
|
|
|
`apps/browser/agent-control.cjs` creates one random HMAC token per browser service instance. Every client shares that credential; challenges identify `local-agent`. The service checks signed global action scopes, browser destinations and current policy through `main.cjs`, but cannot distinguish individual callers. `agent-connection.json` stores the connection capability using Windows DPAPI CurrentUser. `agent-transport-client.cjs` verifies the browser's Ed25519/ML-DSA identity and uses ML-KEM/AES-GCM protected requests and authenticated response status.
|
|
|
|
`packages/core/security.mjs` originally exposed only global enabled/scopes/allowedHosts/allowSensitiveRead settings. `apps/server/public/admin.js` reconstructs that object during draft serialization. Browser audit records action/outcome in `ruleId`; the strict event ingestion schema has no principal field. This describes the previous baseline. The current source includes signed grants, the admin editor, individual transport, structured attribution and the privileged browser integration; the acceptance gates below remain authoritative.
|
|
|
|
## Permission and identity model
|
|
|
|
Add a reviewed, centrally signed individual mode and a bounded array of grants. Each grant has a stable, unique identifier, display name, enabled state, absolute expiry, action-name scopes and allowed destination hosts. Identifiers use a strict bounded character set; names are presentation only. An empty individual scope list grants nothing. Host permissions intersect global agent and browser destination restrictions; an individual allowlist cannot override any denial. Existing sensitive-content and settings restrictions continue to apply.
|
|
|
|
The effective authority is the intersection of current unexpired signed policy, global agent controls, matching enabled unexpired grant, current browser readiness, destination restrictions and the supported action adapter. Neither user input nor a pairing file can add scopes. Group and user-profile restrictions continue to reduce authority. Grant expiry must never exceed the enclosing policy's effective validity at execution time.
|
|
|
|
Each locally issued credential carries its principal ID and an opaque credential generation. Its authentication secret stays exclusively in trusted local storage and the separately DPAPI-protected pairing descriptor. The management backend receives grant metadata, never local tokens or private pairing secrets. Stable principal IDs survive display-name changes; a generation changes on credential replacement so an old credential cannot become valid again accidentally.
|
|
|
|
DPAPI protects a capability for the Windows user. It does **not** establish executable identity or protect against a compromised process running as that user. Separate grants distinguish possession of credentials; do not advertise executable-bound isolation without a separate verified mechanism.
|
|
|
|
## Migration and bootstrap boundary
|
|
|
|
In required individual mode, the existing shared full-permission endpoint and master pairing file must be disabled and removed from active use. Do not retain a compatibility credential that bypasses individual grants. A privileged bootstrap/master capability must not be able to mint arbitrary locally chosen identities or broaden centrally approved rights.
|
|
|
|
Legacy mode, if retained for an explicit migration period, must be a separately reviewed central choice with clear UI/audit labeling. Switching into required individual mode must invalidate legacy proofs, challenges and running operations before exposing the new service. Missing grants, malformed configuration or unsupported client versions fail closed. Existing clients are not silently converted into a universal principal.
|
|
|
|
Credential issuance is a trusted shell operation for an existing centrally approved grant, subject to the current policy. Never expose secrets to remote pages, ordinary renderer state, logs or management responses. Use atomic protected descriptor persistence and explicitly invalidate the previous generation before publishing a replacement. The existing trusted Agent connection control must select a named permitted grant rather than reveal a shared master path.
|
|
|
|
## Exact request lifecycle
|
|
|
|
1. Validate the bounded principal identifier and credential generation before authenticating the request. Bind the HMAC proof to route, principal, generation, timestamp, nonce and exact wire-body digest using a versioned unambiguous encoding.
|
|
2. Apply per-principal and aggregate rate/capacity limits. Replay caches must not permit one principal to exhaust all peers without aggregate containment.
|
|
3. Issue a fresh signed one-use challenge bound to that principal/generation and the browser service instance. The client verifies these bindings and the pinned browser identity before encrypting an action.
|
|
4. At submission, check challenge ownership **before consumption**. Principal A must not consume B's challenge. Consume a correctly owned challenge before decrypting, including invalid ciphertext; replay is refused.
|
|
5. Validate the action envelope, then re-evaluate current signed policy, grant generation, enabled state, expiry and action/destination authority immediately before adapter execution. Captured authorization alone is insufficient.
|
|
6. Track the running operation against principal/generation. Reconciliation, revocation and expiry abort its signal. Existing adapter context checks must observe that signal before consequential side effects and before returning protected content.
|
|
7. Recheck authority before releasing an encrypted response. Authenticate principal/generation and status in the response contract. A revoked caller receives no previously computed sensitive result.
|
|
8. Clear cryptographic buffers, release challenge state and record bounded attribution. Diagnostic export failure must not retry a protected action or weaken authorization.
|
|
|
|
Revocation cannot undo an already committed external action. Document this limit; test prevention before commit and suppression of results after revocation separately. Adapters that ignore cancellation cannot satisfy this contract.
|
|
|
|
## Policy reconciliation and restart
|
|
|
|
Every accepted signed policy refresh must reconcile all principals, including service-enabled policies. Removed, disabled, expired or narrowed grants invalidate pending challenges and abort affected running operations; global disable and policy expiry affect all principals. Do not rely solely on the current global service close condition. Schedule expiry checks independently of incoming requests and keep final execution/release checks as the authoritative boundary.
|
|
|
|
Same-version policy equivocation remains rejected. Policy rollback must not resurrect credentials. Browser restart must explicitly reconcile surviving descriptors with the new service instance and current signed grants; stale descriptors cannot authenticate an old or unrelated listener. Specify whether credentials are reissued or securely recovered, and test the chosen behavior rather than leaving stale capabilities apparently active.
|
|
|
|
## Central review and administration
|
|
|
|
Provide grant creation, scope selection, host restrictions, expiry and targeted disable/revoke in the admin console. Edits remain drafts until the existing independent publication review completes. Show principal additions, removals, permission widening/narrowing and expiry changes in review/history restore. Stable published IDs are immutable; replacement uses a new ID or explicit credential generation workflow.
|
|
|
|
Update all policy serializers, presets, group overlays and draft refresh paths to preserve grants. In particular, the current `p.agents = ...` reconstruction in `public/admin.js` must not silently drop them. Read-only roles cannot mutate grants. Actual UI tests must prove draft retention, explicit publication, reviewer visibility and targeted revocation without disabling unrelated agents.
|
|
|
|
## Durable audit, SIEM and observability
|
|
|
|
Add a strict structured agent attribution object to durable browser events and backend ingestion, with principal ID, credential generation, action and outcome. Preserve existing trace/span correlation and policy version. Use fixed bounded reasons for scope denial, expiry and revocation; never store tokens, proofs, plaintext action arguments, private keys or arbitrary exception text. Keep existing action semantics usable for forensic evidence matching rather than encoding the identity into `ruleId`.
|
|
|
|
Propagate attribution to authorized event views and ECS/CEF exports using documented mappings. Legacy unattributed events stay explicitly legacy; do not invent a principal. Ensure denial, successful execution and targeted revocation are attributable. Operational spans should cover authentication, grant evaluation, challenge ownership, dispatch and reconciliation with safe bounded attributes and real parent/child correlation. Durable security audit remains distinct from debug telemetry.
|
|
|
|
## Acceptance gates
|
|
|
|
- Two real clients with different credentials can perform only their intersected rights; one cannot consume another's challenge, replay proofs or substitute identity/generation.
|
|
- Expired, removed, disabled and replaced grants fail at authentication/execution/response boundaries. Targeted revocation aborts the affected adapter while an unrelated grant remains usable.
|
|
- Global disable, policy expiry, user-profile denial, policy rollback and restart cannot resurrect authority or permit legacy bypass in required individual mode.
|
|
- Invalid configuration and missing principal bindings fail closed. Bounded retries preserve existing action/CAS semantics and never repeat uncertain side effects.
|
|
- DPAPI descriptors contain separate local credentials; wire, ordinary logs, SIEM and backend storage contain no secret material. Tests explicitly retain the same-user capability threat boundary.
|
|
- Actual admin UI publication/review/history tests and native browser integration tests exercise the shipped path, not an isolated primitive.
|
|
- Actual exported OTEL traces show the lifecycle and revocation timing, linked to attributable durable events. Windows packaged-source verification covers every new module and client protocol.
|
|
|
|
Do not mark the feature complete until all of these gates are met in the integrated product. A disconnected token registry or schema addition is insufficient.
|
|
|
|
## Current integrated evidence
|
|
|
|
496 tests pass, including forged-authentication attribution, per-principal lifecycle,
|
|
stored proposal authorization and native ownership gates. Two real Electron
|
|
processes prove UI pairing, two actors, targeted revoke with unaffected peer,
|
|
forbidden direct/redirect receivers seeing zero requests and a generation floor
|
|
that survives restart. Real exported RPC phase spans share an `agent.request`
|
|
parent and a durable principal audit event. The Windows package is built and
|
|
verified against source, its contained parser/offline model, and both legacy and
|
|
individual shipped agent helpers. Authenticode remains NotSigned.
|
|
|
|
Unattributed worker HTTP requests are temporarily blocked in individual mode;
|
|
full worker compatibility requires reliable ownership/session isolation. Expanded
|
|
individual-grant central independent-review/history tests remain open, alongside
|
|
other full-browser and production requirements. These results do not satisfy every
|
|
acceptance gate in this contract or the complete product goal.
|