Security

Security architecture

How Obacht routes traffic, executes commands, and handles keys. A description of the trust boundaries, written for people who evaluate them.

Obacht is designed to store the minimum required to operate the service. The capability that would be most damaging if disclosed, the key material that authorises commands on devices, is never held on Obacht's servers in usable form. The following describes the actual mechanisms.

Traffic

TLS termination on the device

Inbound requests to a custom domain reach the Obacht proxy first. On port 443 the proxy runs nginx in stream mode with ssl_preread: it reads the SNI server name from the TLS ClientHello to select an upstream and forwards the encrypted TLS connection to that device's WireGuard address. It performs no TLS handshake and does not decrypt the payload. The certificate is held by the device and TLS terminates there, in the device's own web server (Caddy). Port 80 is forwarded equivalently for ACME HTTP-01 and redirects.

client

TLS →

obacht proxy

SNI read · no decrypt

wireguard

encrypted →

device

TLS terminates

Client → Obacht proxy (SNI routing via ssl_preread, no decryption) → WireGuard → device (TLS terminates here)

  • Routing uses $ssl_preread_server_name only; the proxy holds no session keys
  • The Let's Encrypt certificate is issued to the device, not to Obacht
  • Cleartext exists only on the device and the visiting client

Network

Outbound WireGuard, no inbound exposure

Each device initiates a WireGuard tunnel to the gateway and maintains it. There are no inbound connections, no port forwards on the local router, and no publicly reachable listener on the device. Return traffic to a bound domain traverses the tunnel the device established.

Control plane

Two command paths, both authorised by the user key

State-changing actions on a device (template install, domain binding, opening a shell) use one of two transports. Both are authorised by the same Ed25519 key pair, held by the user.

SSH

SSH bridge

For an interactive shell and file access, the dashboard establishes an SSH session to the device through the SSH gateway. The user's private key is decrypted in the browser for the session and used to authenticate the SSH connection. The key is not persisted on the gateway.

WS · signed

Signed mutations over WebSocket

For structured mutations (domains, bindings, instances) the dashboard constructs a mutation object (device_id, op, params, nonce, iat, exp), canonicalises it (JCS) and signs it with the user's private key (Ed25519) in the browser. On this path the key does not leave the browser, not even to the gateway. The API relays the signed envelope to the device over its WebSocket without inspecting it. The agent verifies the signature against a locally pinned public key, the device binding, the expiry window and nonce freshness, then dispatches the operation. Unsigned upsert/delete events on the WebSocket are permanently denied.

Key handling

The signing key is not held by Obacht

Obacht stores the user's key as a passphrase-encrypted ciphertext (salt, IV, algorithm) and the corresponding public key, which devices pin. The passphrase and the decrypted private key exist only in the browser, for the session, entered by the user. Neither reaches Obacht.

A compromise of Obacht's servers yields encrypted key ciphertext and public keys. It does not yield the ability to sign a mutation or authenticate an SSH session. Issuing commands to the fleet would require each user's passphrase, which Obacht does not possess.

Templates

Two independent signatures per install

A template install carries the registry's minisign signature over the manifest and the user's signature over the mutation. The agent verifies both: the registry signature against an embedded trust bundle, the user signature against the pinned public key. A compromised backend can forge neither, so it cannot deliver a modified manifest or install in the user's name.

Device defaults

Restricted by default, on-device audit log

Devices operate in restricted mode by default: signed templates only, no arbitrary command execution. Power mode is an explicit unlock and can be revoked. Mutations and command executions are recorded in an append-only audit log stored on the device.

Minimal retained state

These choices follow one principle: retain as little as possible, and keep the capability that could compromise a fleet, signing commands, off Obacht's infrastructure. The result is a remote management plane in which no single server holds the means to act on devices.

Docs
Security architecture — obacht