What the console does not protect against
The limits, stated as limits — including the two things the terminal does not promise and the one named cookie exception.
The Secure cookie rule — a named exception, not a shrug
The rule, in full:
| Connection | Secure | Why |
|---|---|---|
HTTPS (or behind a proxy sending X-Forwarded-Proto: https) | on | The normal case. |
Plain HTTP from a loopback address (127.0.0.1, ::1) | off | The fleet serves plain HTTP on localhost by standing owner decision. A Secure cookie is discarded by every browser on such a connection, so login 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 login 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, a Secure cookie, and a page that says so.
HttpOnly and SameSite=Lax are unconditional. Path=/console keeps the session cookie
away from /dashboard, /mcp and every other surface.
The login page always states which of the three rows it is in. You never have to guess, and a silently insecure cookie is never what you got.
What this does not protect against
Written here rather than left to be discovered:
- One admin, no roles. There is exactly one master admin. Everyone who logs in is that admin. Multi-user and RBAC are a different arc.
- A restart clears the attempt counters and all sessions. Whoever can restart your server is not slowed by the attempt limit.
- The attempt limit keys on the client IP. An attacker with many source addresses is not stopped by it. The memory-hard KDF — 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 engine 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 the same reasoning is what §5 now rests on. The fix is a trusted-proxy policy the operator declares, so the header is read only where an operator has said a proxy exists; that key does not exist yet. Until it does, put the console behind loopback (an SSH tunnel, §4.4) or terminate TLS on the server itself rather than in front of it. - CSRF: the ACT routes carry a token, the login form does not.
POST /console/actandPOST /console/act/previewrequire a session-bound synchronizer token — an HMAC over your session id under the process's session key, rendered into every act form and verified in constant time. Another origin cannot read it off the page, so a cookie alone is not enough to fire an act.SameSite=Laxon the cookie stays as the outer layer, but it is a browser's promise and invisible to the server, which is why it is not the mechanism. The login form still has no token: it changes no record state, and a cross-site POST to it can at worst log you in as yourself. - A restart invalidates every act token, because it invalidates every session. Reload the queue after a restart; a stale form posts a token bound to a session that is gone.
- This is not browser OAuth. Full OAuth for the console — code + PKCE + callback + logout against an identity provider — is a separate piece of work. This master-admin credential is what that work will sit beside, not something it replaces.
/dashboardis not behind this session. It is a different surface with its own history, and it is changed on its own.
Two things this page does not promise
-
There is no terminal SCREEN in the console yet. This is the endpoint. A browser needs a terminal emulator to render what comes out of it, and that is a separate piece of work.
-
The pseudo-terminal itself is unproven on hosts without device nodes. The engine's own test harness is refused
/dev/ptmx, so the pty-level suite skips there and says so. To measure it where device nodes exist:That variable turns the skip into a failure, so a host that should have a pseudo-terminal cannot report a quiet green.
The session sweep of §6 step 4 is the exception: its own controls (
a control in the product’s own suiteand its siblings) reproduce a descendant in its own process group withsetsid/setpgiddirectly and need no pseudo-terminal, so that mechanism is measured everywhere. So is the timing of the sweep against a shell that has only just been killed:a control in the product’s own suiteruns the close sequence forty times over a plainsetsidleader, anda control in the product’s own suiteforces the case where the state never arrives and requires the bound to be reached and named.What still needs
/dev/ptmxis the end-to-end shape — a real shell, real job control,Session.Close— which isa control in the product’s own suiteanda control in the product’s own suite.