Runtime Boundaries
Where each part of Nixstasis runs, what it trusts, and how it is kept apart from the rest. How each component works inside is covered in the Module Reference.
Processes
- Phoenix runs as one OTP release. Stateful work runs in supervised processes
(Server Application); each browser terminal session is its own
SshClientprocess that wraps an OSsshcommand through an Elixir Port. - The client runs
nixstasis pollas a long-running service that loops on a ticker. Scripts run concurrently, and commands run concurrently unless they change files, keys, or policy. frpcruns only while remote access is granted, as a transient systemd unit or a poll-ownedfrp-sessionchild, with a one-hour timeout (Client FRP Manager).- Caddy, FRPS, and PostgreSQL run as Compose services (Deployment).
Untrusted inputs
| Input | Enters through | Trusted after |
|---|---|---|
| Operator actions | LiveViews and controllers behind Caddy | AuthCrunch authentication, the proxy credential, and Phoenix role and scope checks (Server Web). |
| Device requests | /api/v1/devices/... | Device token authentication; registration is public but proves possession of client-saved secrets (Server Devices). |
| Device telemetry | Heartbeats | Size and shape validation before any side effect (Server Monitoring). |
| JSON:API requests | /api/json | AuthCrunch, then route and data-layer policies (Server Domain). |
| E2E requests | /e2e | The E2E gate and runner authentication (Server E2E). |
| Certificate requests | Caddy's check_domain ask | Phoenix approval for a device with an active lease. |
| Laptop TLS diagnostics | /_nixstasis/laptop/tls_observations | A development-only token; not a product API. |
| Scripts and command policies | Local files and server commands | Client-side parsing, schema checks, and execution limits (Client Starlark Runtime). |
Phoenix ignores forwarded identity headers and the client-IP header unless the request also carries the secret credential Caddy adds, so nobody can fake them by calling Phoenix directly. Device scope is never taken from identity claims at all.
Browser terminal authorization
A browser terminal passes through four parties, and each one has to agree before a session opens:
- Phoenix checks the operator's remote-access permission, generates an ephemeral key pair, creates a session
reference, and queues
ssh_authorizefor the device. It activates the browser token only after the device reports success, and queuesssh_revokeon close, expiry, or failure. - The client validates the authorization and keeps the public key only in its in-memory SSH authority, never on disk.
- The OpenSSH helper,
nixstasis ssh-authorized-keys, runs as the lockednixstasis-ssh-authorityuser. It validates%u %t %k, asks the client over/run/nixstasis/ssh-authority.sock, and prints a key only on an exact allow. It cannot run client commands and has no file fallback. - OpenSSH authenticates the
nixstasis-supportaccount withAuthorizedKeysFile none. FRP only carries the connection.
Because the key only ever lives in the client's memory, a client restart, an expired or revoked session, or a broken
socket all end in the same place: access denied. On the server, each session keeps its
private key and pinned host key in a 0700 temporary directory with 0600 files, and connects with ssh through an
ncat HTTP proxy to the FRPS TCP mux. The wire format is in
API & Runtime Contracts.
Network exposure
| Service | Exposure |
|---|---|
| Caddy | Ports 80 and 443 on BIND_HOST: the only public HTTP ingress. |
| FRPS | FRPS_BIND_PORT on BIND_HOST for device tunnels; its HTTP vhost, TCP mux, and dashboard ports are internal. |
| Phoenix | PORT on PHOENIX_BIND_HOST, which stays 127.0.0.1 in production, for local diagnostics only. |
| PostgreSQL | Internal, unless DATABASE_URL points at an external database. |
Devices call the server at the configured api.url, normally https://nixstasis.<base-domain> through Caddy. Caddy
reaches Phoenix and FRPS on the internal Compose network, and FRPS reaches Phoenix's /internal/frp plugin endpoint the
same way.