Skip to content

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.

  • 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.
  1. 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.
  2. 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.
  3. Configurationconfig.yaml for security, the hydra: section, and bootstrap; everything else is dynamic via the Web UI. See the Configuration reference.
  4. 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”
Featureapp.shellwatch.aiSelf-hosted
Admin roleNot exposedFirst passkey ever registered becomes the admin account
WebAuthn relying-party IDshellwatch.aiWhatever you set in security.rpId
Web Push (VAPID)Already configuredYou generate VAPID keys and add them to config.yaml
IP allowlistPermissive (account auth + passkey gate is the boundary)Defaults to loopback; widen with security.allowedNetworks
Account self-registrationOpenOff by default; first-user bootstrap always works
Multi-userMulti-tenant by accountSingle 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.