Security
Source-of-truth code paths
- Relay auth and rate limiting:
packages/relay/src/index.ts - Protocol-level auth and error schemas:
packages/shared-protocol/src/index.ts - Relay security integration tests:
packages/relay/test/integration.test.mjs
Baseline controls
- Token-first WebSocket authentication
- Role-based command authorization
- Action-scope authorization for controller commands
- Strict schema validation
- JWT issuer and audience validation
- Optional previous-signing-key verification window for secret rotation
- Refresh token revocation endpoint (
/api/auth/revoke) - Durable relay-side refresh session persistence with startup pruning of malformed/expired entries
- Controller client secret hashing at rest (relay stores salt+hash, never plaintext)
- Controller-side client secret storage prefers OS keychain; env var fallback supported (
OTTO_CONTROLLER_CLIENT_SECRET) - Node-owned ACL gating for controller-to-node command routing
- Refresh token rotation on successful HTTP refresh
- Per-session command rate limiting
- Replay protection via command
replayNonceand timestamp acceptance window - Origin allow-list checks for browser-originated node WebSocket upgrades
- Node WebSocket upgrade rejects disallowed Origin with HTTP 403 when allow-list is configured
- Redacted logs by default
- Auth-required command preflight that can redirect users to first-party login pages without credential capture
- Debugger-backed network interception is scoped to managed
tabSessionIdand validated against declared site - Interception header emission redacts sensitive fields (
Authorization,Cookie,Set-Cookie,Proxy-Authorization) - Setup-time extension artifact checksum verification before extraction
Command security model
Threat boundaries:
- Relay auth and scopes protect command ingress.
- Runtime controller command authorization is bearer-token based (access token scopes + node ACL grants), not client-secret based.
- Command auth preflight protects website-session prerequisites.
- Browser credentials remain user-managed and never transmitted through Otto command payloads by design.
Current controls:
- Site matching before command execution (
site_mismatchon mismatch). - Explicit manual handoff for website login (
manual_login_required). - No automatic credential entry or scraping of secret fields.
Abuse and failure guardrails
- Pairing approval is first-wins; repeat approval attempts return deterministic
pairing_not_pending. - Controller clients registered via
/api/controller/tokenare denied node command routing until node-owned ACL grants access (acl_missing_node_grant). - Controller client secrets are used for
/api/controller/tokencredential exchange and are never transmitted in runtime command frames. - Malformed or expired access tokens are rejected during WebSocket auth with
invalid_access_token. - Malformed command envelopes (for example missing
targetNodeId) are rejected before routing. - Queue depth and per-session rate limits are enforced to reduce starvation and abuse pressure.
- Command auth flow never automates end-user credential submission; failed login preflight returns
manual_login_requiredafter optional navigation to site login page. chrome.debuggerinterception remains explicit opt-in via listener subscribe actions and cannot suppress Chrome debugger infobar.- Command-level debugger focus emulation remains explicit opt-in via
metadata.requiresDebuggerFocus=true. - Debugger attachment reuse is ownership-scoped: runtime detaches only attachments created by that feature path to prevent cross-feature disruption.
- Fetch-domain interception always continues paused requests to avoid traffic deadlock if body retrieval fails.
- Hybrid interception duplicate suppression bounds equivalent cross-source response replay, reducing repeated payload forwarding surfaces.
Operational security checklist
- Keep
OTTO_TOKEN_SECRETout of source control and rotate regularly. - Set
OTTO_EXTENSION_ORIGINin production to restrict browser node upgrades. - Use least-privilege controller scopes for automation principals.
- Audit logs for repeated
forbidden_action,replay_rejected, and lock conflict patterns. - Treat command input payloads as untrusted and validate fields in command logic.
- Keep controller and extension settings separated; do not copy controller tokens into extension storage.
- Prefer a protected filesystem location for
OTTO_LOG_DIR; it now contains durable refresh-session data (refresh-sessions.jsonl). - Keep refresh token lifetime bounded via
OTTO_REFRESH_TTL_DAYSand avoid unnecessarily large values. - Keep
OTTO_ALLOW_REMOTE_CONTROLLER_REGISTRATIONdisabled unless required; when enabling remote registration, setOTTO_CONTROLLER_REGISTRATION_SECRETand restrict network ingress. - Treat node ACL grant actions as end-user authority operations and audit
controller_acl_granted/controller_acl_revokedevents. - Treat debugger-focus metadata as privileged reliability tooling; enable only for commands with demonstrated background-tab stalling behavior.