What the console does not protect against
The limits of the console's sign-in, stated as limits — including the one named cookie exception.
The sign-in described here is the same on both consoles: the current one served by
saphan-gateway and the previous one served by the projection server. Where they differ, the
paragraph says so.
The Secure cookie rule — a named exception, not a shrug
The rule, in full:
| Connection | Secure | Why |
|---|---|---|
HTTPS (or a request carrying X-Forwarded-Proto: https) | on | The normal case. |
Plain HTTP from a loopback address (127.0.0.1, ::1) | off | Both consoles listen on loopback by default. A Secure cookie is discarded by every browser on such a connection, so sign-in could not work at all — and it would fail with nothing to see. |
| Plain HTTP from anything else | on | The cookie will be discarded and sign-in will fail. That is the correct failure. A console reachable in plaintext from off-host is the thing that must not quietly work. |
"Loopback" means the address the server observed on the connection — the peer address of the
socket. It is deliberately not the Host: header, and not the name you typed in the browser.
The header is set by the client; a remote client that sends Host: localhost gets row three and a
Secure cookie.
HttpOnly and SameSite=Lax are unconditional. The session cookie's path is /console, so it is
not sent to the server's other surfaces.
The login page warns you when it is in row three, so a silently discarded cookie is never what you got.
What this does not protect against
Written here rather than left to be discovered:
- One administrator, no roles. There is exactly one console administrator, and everyone who signs in is that administrator. What protects a recorded act is the passkey that signs it, not the sign-in.
- A restart clears the attempt counters and all sessions. Whoever can restart your server is not slowed by the attempt limit. A session otherwise lasts 12 hours.
- The attempt limit keys on the client address. Five failures from one address within 15 minutes lock that address out. An attacker with many source addresses is not stopped by it. The memory-hard key derivation — argon2id, 64 MiB and three passes per attempt — is what makes distributed online guessing expensive; the limiter makes the single-host case pointless.
- Behind a reverse proxy, the attempt limit is a SHARED bucket. The address the server observes
is the proxy's, so every client counts into one counter: five failures from anyone lock out
everyone, for fifteen minutes. The console will not read
X-Forwarded-Forto get around this, because a header any client can set is a header any client can use to give itself a private bucket — and there is no setting that declares a trusted proxy for the console. Put the console behind loopback (an SSH tunnel) or terminate TLS on the server itself rather than in front of it. - The login form carries no CSRF token. It changes no record state, and a cross-site POST to it
can at worst sign you in as yourself. On the previous console, the act routes (
POST /console/actandPOST /console/act/preview) require a session-bound token, which a restart invalidates with the session — reload the page after a restart. - This is not browser OAuth. The console's sign-in is the administrator credential; it does not sign you in against an identity provider.
The terminal
- The current console's Terminal runs as the gateway's user. The
saphanCLI it starts has the gateway's permissions and workspace, not yours; anyone signed in as the administrator can start it. See Signing in and finding your way. - The previous console draws no terminal screen. It offers a terminal endpoint only — see The live terminal.
- ⚠ The pseudo-terminal is not proven on hosts that do not provide
/dev/ptmx. A terminal on such a host cannot open, and says "the terminal could not be opened".