Same-machine access
CLI automatically opens a loopback URL with a fragment token.
Supports same-machine access, LAN or VPN, SSH tunnels, and owner domains behind HTTPS reverse proxies. Every Web startup generates an instance token; HTTP, SSE and WebSocket share authentication rules. OpenProgram operates no public relay and adds no accounts, RBAC or project permissions.
Web UI is a complete owner-administration authority boundary. Multi-person collaboration continues through channel-speaker attribution and restricted capabilities, not Web accounts.
CLI automatically opens a loopback URL with a fragment token.
Requires explicit configuration of complete exact allowed_origins.
The browser connects to a local forwarded port; SSH encrypts transport.
web.host; the instance token still authenticates.nginx or Caddy terminates HTTPS on the same machine, preserving public Host.
Owner process tokens, canonical Origin, fragment bootstrap, shared HTTP/WS authentication, auth-url, minimal health and non-retrievable secrets are in production paths; complete deployment acceptance remains unfinished.
OwnerAuthState Generate a 32-byte token, hold a per-state lock, write an owner-only token file and token-free access.json, and derive a profile-specific cookie.OwnerAuthMiddleware Validate Host/request origin and cookie or Bearer before route dispatch.
Attach current-profile owner authority only after authentication; validate WebSocket before accept() . Uvicorn uses proxy_headers=False.
Both plaintext-reveal paths are removed: legacy account routes return 410, config-key reveal queries return 404, and credential responses retain masks only; replace/preserve/delete schemas are implemented.
Still missing direct-HTTP startup warnings and independent acceptance for SSE, real-browser bootstrap/WS, restart, bind failure, multiple profiles and nginx/Caddy smoke deployments.
| Current object | Implemented | Missing or risky |
|---|---|---|
OwnerAuthState | Token, lock, owner-only files, access snapshot, cookie and constant-time comparison | Real bind-failure, restart and multi-profile acceptance missing |
| Canonical Origin | Effective Origin, non-loopback fail-closed behavior and local HTTP networks | Startup output still lacks complete fields and direct-HTTP warnings |
OwnerAuthMiddleware | HTTP/WS, cookie/Bearer, Host/Origin/CSRF, owner authority | Independent SSE and real-browser WS acceptance missing |
| Fragment bootstrap | Backend, frontend gate, synchronous clearing, no Web Storage and auth-url | Browser-level end-to-end acceptance missing |
| Reverse proxy | proxy_headers=False and loopback-only forwarded scheme | nginx/Caddy HTTPS, WS and SSE smoke deployments untested |
| Provider secret | Some responses masked by default | Two reveal paths return plaintext |
/healthz | Returns only status=ok; details are under protected /api/diagnostics | Complete response-class cache audit remains unfinished |
Covers existing reference material and additional systems with remote Web, authentication, reverse-proxy or secret-handling capabilities; explicitly marks absent target capabilities as not applicable.
| System | Defaults and authentication | Remote method | Adopt | Rejected or not applicable |
|---|---|---|---|---|
| OpenClaw | Loopback; external access requires authentication and explicit Origin | SSH, reverse proxies and other external-network methods | fragment, Host/Origin, proxy trust | Tokenless identity replacement |
| Jupyter Server | Notebook 4.3 defaults to tokens, exchanged for cookies on first access | SSH, HTTPS public server | Automatic token → cookie experience | Query tokens and password mode |
| Hermes Agent | Loopback; external bind requires an auth provider and fails closed | Session cookie; one-time WS ticket | External fail-closed and secret redaction | Auth-provider accounts, a second ticket system and reveal |
| Agent Zero | Local; optional single login; cookie + CSRF | Reverse proxy, VPS and built-in tunnel | cookie/CSRF/Origin/WS, secret mask | Optional authentication and built-in public tunnel |
| OpenHands | session API key; HTTP/WS | SSH, nginx/HTTPS | Instance key and loopback backend | Tokens exposed through public HTML/browser storage and plaintext secret retrieval |
| opencode | Loopback; no authentication without a password | External host and CORS origins; separate WS ticket | loopback default | Optional authentication and second WS ticket |
| Open WebUI | Accounts, roles and JWT/cookie | nginx/Caddy, WS, SSE | Proxy, Upgrade and SSE-buffering configuration | Accounts, groups and RBAC |
| Dify | account/workspace/role | Reverse proxy | Credential obfuscation and replacement semantics | Tenant and role layers |
| LibreChat | Registration, JWT, roles and ACL | nginx HTTPS/WS | Proxy details | User-identity data model |
| AnythingLLM | Single or multiple users; optional single-user password | Docker port | Single owner-credential concept | Optional authentication and role system |
| AutoGen Studio | Loopback; no authentication by default; optional OAuth/JWT | research prototype | loopback default | query/localStorage token, post-accept auth |
| SWE-agent | Current repository provides a command-line agent | No owner Web-control UI | Record capability absence | Remote-security design not applicable |
| pi-mono / pi-ai | TUI, SDK, RPC/provider transport | No packaged owner Web server | Record capability absence | Remote Web ownership not applicable |
| WeClaw | WeChat gateway + HTTP API | Configurable listen address | Distinguish external HTTP from Web UI | No browser owner UI |
| Codex CLI | App-server and experimental WS | service-mediated pairing/remote control | Record protocol differences | Do not adopt relay-based remote control |
| Type | Framework or capability | OpenProgram treatment |
|---|---|---|
| Instance credential | Jupyter, OpenClaw, OpenHands | Adopt: one owner, one root key, two transport credential forms |
| Application account | Open WebUI, Dify, LibreChat, AnythingLLM | Reject: no registration, roles or resource ACL needed |
| Hosted remote control | pairing, hosted tunnel, relay service | Reject: OpenProgram operates no external service |
The final design keeps only one root key (instance token), two transport credential forms (Bearer header and profile-scoped HttpOnly cookie), one browser bootstrap and one HTTP/WS/SSE authentication rule set.
Run one Web process per profile/state instance. Rotate tokens on every startup. Browsers bootstrap once with fragments, then use derived HttpOnly cookies; native clients use Bearer headers.
<state>/web.lock, then atomically write the owner-only token and token-free access.json.openprogram_owner_<owner16> Use a fixed HMAC domain; after decoding, compare only with compare_digest.Static shells/assets (including the prebuilt-file-only GET/HEAD /docs documentation site), ownership challenge, bootstrap and minimal health. Source checkouts read docs/_site; installed packages read the same prebuilt site placed under openprogram_server/_webui/_frontend/docs during release. Staleness checks run only when source workers register routes, not on HTTP requests. The shell uses CSP frame-ancestors 'none' and X-Frame-Options: DENY.
GET/HEAD/OPTIONS must be side-effect-free. Unsafe methods and WS require Origin equal to request_origin; only safe requests may omit Origin.
HTTP, SSE and native WS use Bearer. Reject query tokens. Except for bootstrap, any Authorization header prevents invalid Bearer credentials from falling back to cookies. Bootstrap with Authorization returns the same 401 directly.
<state>/web/access.json Freeze bind, port, effective Origin and token fingerprint, but not the token. After verifying worker PID/port, CLI sends a fresh 32-byte nonce only to GET /api/auth/challenge and locally verifies HMAC-SHA256(raw_token, "openprogram-web-challenge-v1\0" || nonce || "\0" || revision); the token is never sent to the probed port.| Request form | Credential | Origin | Failure result |
|---|---|---|---|
| Cookie + unsafe HTTP | Required | Equal to request_origin; reject missing | 401 or 403 |
| Cookie + WebSocket | Required before accept | Equal to request_origin; reject missing | Upgrade HTTP 401/403 |
| Cookie + safe HTTP/SSE | Required | Must match when present; same-origin navigation may omit | 401 or 403 |
| Bearer HTTP/SSE | Required | May omit; must match when present | 401 or 403 |
| Bearer WebSocket | Required before accept | Native clients may omit; must match when present | Upgrade HTTP 401/403 |
| Listener ownership challenge | No credential; nonce + optional revision | May omit; must match when present | 400 or 403 |
| Fragment bootstrap | Body token only; Authorization rejected | Equal to request_origin | 401 or 403 |
ssh -N -L 18100:127.0.0.1:18100 owner@remote-host openprogram web auth-url \ --base-url http://127.0.0.1:18100
{
"web": {
"host": "127.0.0.1",
"allowed_origins": ["https://agent.example.com"]
}
}
# proxy preserves Host and Upgrade,
# overwrites X-Forwarded-Proto, clears unused forwarded headers,
# and disables SSE buffering
localhost and actual listen literal; other Origins require explicit config.json configuration. HTTP permits only that localhost, loopback, RFC 1918, IPv4/IPv6 link-local, IPv6 ULA and RFC 6598 range; other addresses and DNS names require HTTPS. Each request accepts one valid Host;http/ws and https/wss map to their corresponding browser Origins. Uvicorn uses proxy_headers=False; OpenProgram does not trust X-Forwarded-For and accepts only one X-Forwarded-Proto from the original loopback peer. The request_origin constructed from scheme + Host must be valid.Account reveal routes return 410; config-key status retains masks but returns 404 on a reveal query.
Return has_value and masked/masked_key. Values at least 12 characters use sk-…abc4, hiding at least five characters; shorter values use eight fixed dots.
Config keys use POST /api/config for replacement andDELETE /api/config/key/{env} for deletion; API-key accounts use dedicated update for replacement andaccounts/remove to delete the entire account. Empty values and masks are invalid.
Accept connections only after configuration validation, secure token persistence and fingerprint calculation. Executable behavior tests establish completion, not documentation.
GET/HEAD /docs and /docs/** can read prebuilt static docs in both source checkouts and installed packages without scanning source or triggering builds; the wheel must include openprogram_server/_webui/_frontend/docs/index.html. Host validation still applies; unsafe methods and private APIs are excluded.Keep the three levels strictly separate. Design prose is not implementation evidence; classify using current production code and tests only.
OwnerAuthState token, lock, cookieproxy_headers=False and loopback-only forwarded schemeenv/headers/bearer/client secrets all masked, preserve/replace/delete editing, configuration file 0600 atomic writingBackendEndpoint drives Bearer authentication for TUI and MCP CLI