Self-hosting overview
ShellWatch is open source. The same binary that runs app.shellwatch.ai runs anywhere you can run a Node.js process or a container — plus Ory Hydra alongside it as the OAuth2/OIDC authority for all clients (web UI, MCP, agent). Hydra stores its data in file SQLite next to ShellWatch’s own — there’s no separate database server to run.
You only need to read this section if you want to host the broker yourself. If you’re happy using app.shellwatch.ai, you can skip everything here — the rest of the documentation is written from the point of view of an app.shellwatch.ai user, and self-hosted deployments behave identically once they’re configured.
When self-hosting makes sense
Section titled “When self-hosting makes sense”- Your SSH targets are on a private network and the broker needs to live on it.
- You want to pin the WebAuthn relying-party ID to your own domain.
- You need your own VAPID keys for Web Push, your own OAuth flow URL, your own CIDR allowlist, etc.
- You’re running in an air-gapped or regulated environment.
What you’ll set up
Section titled “What you’ll set up”- The broker + OAuth authority — ShellWatch and Ory Hydra (both store data as file SQLite under
./data; no separate database server). The Docker compose setup runs both as a profiled two-service stack. - A reverse proxy — terminate TLS, forward to the broker, and route the
/oauth2/*surface to Hydra (same domain or a separate auth subdomain). See Reverse proxy & TLS. - Configuration —
config.yamlfor security, thehydra:section, and bootstrap; everything else is dynamic via the Web UI. See the Configuration reference. - Hardening — flip the production-grade defaults. See the Hardening checklist.
What’s different on a self-hosted instance
Section titled “What’s different on a self-hosted instance”| Feature | app.shellwatch.ai | Self-hosted |
|---|---|---|
| Admin role | Not exposed | First passkey ever registered becomes the admin account |
| WebAuthn relying-party ID | shellwatch.ai | Whatever you set in security.rpId |
| Web Push (VAPID) | Already configured | You generate VAPID keys and add them to config.yaml |
| IP allowlist | Permissive (account auth + passkey gate is the boundary) | Defaults to loopback; widen with security.allowedNetworks |
| Account self-registration | Open | Off by default; first-user bootstrap always works |
| Multi-user | Multi-tenant by account | Single SQLite file, all accounts share it |
A passkey is non-portable across relying parties — a credential registered on app.shellwatch.ai can’t be used against your self-hosted instance, and vice versa.