Saphan StudioDocs
The console

Signing in

Setting the credential the first time, signing in day to day, and rotating it.

First login — setting the credential

Until you do this, /console serves nothing. It does not serve itself open: every console route answers 503 and names this section. An unprovisioned console is a closed console.

1.1 Set the credential. The verb asks you for the password, the way passwd does:

$ saphan console admin set --user marcin --workspace /path/to/workspace
new console password:
retype new console password:
console admin: credential set for user "marcin"

Type it and press Enter. Nothing appears as you type — echo is off. You are asked twice and the two are compared: if they differ, the verb says so and writes nothing, and any credential already on file is left exactly as it was.

Minimum length is 12 characters. The verb prints the username, the file, and the KDF parameters — never the password and never the digest.

The password is never an argument. There is no --password flag and there will not be one: argv is readable by every process on the machine through ps, and it is written to your shell history. The password reaches the engine from your keyboard, or — for scripts — from stdin (§1.1b), and from nowhere else.

1.1a — if you press Ctrl-C at the password prompt, your typing may stay invisible. Echo is off while the verb is asking. It turns echo back on when it finishes, including when it refuses — but a Ctrl-C at the prompt kills the process before it gets there, and the terminal keeps the setting. The symptom is unmistakable and harmless: your next commands run normally, you just cannot see what you are typing. One command fixes it:

stty sane

Type it blind and press Enter. (If that terminal is already unusable, closing it and opening a new one has the same effect — the setting belongs to the terminal, not to your account.) Nothing was written to the credential file: a cancelled prompt leaves whatever was on file exactly as it was.

1.1b — scripts and CI. When stdin is not a terminal there is nobody to ask, so the whole of stdin is read as the password (one trailing newline is trimmed). That is the right way to do this unattended:

printf '%s' "$CONSOLE_PASSWORD" | \
  saphan console admin set --user marcin --workspace /path/to/workspace

This path is unchanged and is not going away. It is the fallback, not the door.

--allow-tty-echo was removed. It existed for one reason: the binary had no no-echo reader, so it refused an interactive terminal outright and that flag was the escape hatch — "read it from the terminal anyway, with the characters visible on screen". The verb now has a real no-echo reader, so the flag's only remaining effect would be to put a password on a shared screen. It never affected a pipe. If a script passes it, remove it; the same command without it does the same thing.

1.1c — if the verb says it cannot turn your terminal's echo off, it refuses. On a platform this engine does not ship binaries for, or on a terminal whose settings it cannot read, you get this instead of a prompt (first line wrapped here to fit the page):

console admin set: STDIN IS A TERMINAL WHOSE ECHO THIS BUILD CANNOT TURN OFF — nothing was
read and nothing was written.
Asking you to type a password here would put it on the screen and into this
terminal's scrollback, so the verb refuses instead of reading it in the clear.
→ feed the password in through a pipe, which never puts it on the screen either:
    read -rs -p 'new console password: ' P && printf '%s' "$P" | saphan console admin set --user <name>; unset P

That refusal is the mechanism working, not a fault to work around. The verb will not ask you to type a password onto a screen where it would be visible and would stay in your scrollback. It asks two separate questions about your stdin — is anyone typing? and can I turn the echo off? — and when the answers are yes and no, it stops rather than guessing. Use the pipe route from §1.1b; it never puts the password on the screen either:

read -rs -p 'new console password: ' P && \
  printf '%s' "$P" | saphan console admin set --user marcin --workspace /path/to/workspace
unset P

The shipped binaries are darwin/arm64, linux/amd64 and linux/arm64, and the prompt works on all three; you will normally meet this refusal only in a build from source on another kernel.

1.2 Confirm what was written:

saphan console admin status --workspace /path/to/workspace

Expect console admin: SET, your username, the file at 0600, and the argon2id parameters.

1.3 Start the server:

saphan server --workspace /path/to/workspace

If a server was ALREADY running while you did §1.1, RESTART it. It resolves the credential once per process, at the first console request it serves — so a server that has already answered "console: closed — no master-admin credential has been set" goes on answering exactly that after you set one. stop, then start again:

saphan server stop --workspace /path/to/workspace
saphan server --workspace /path/to/workspace

This is the same fact as §3.2, met on the way in rather than on the way round. The verb prints it at the moment you set the credential, and the closed page in the browser prints it too, so you meet it wherever you are standing. It is pinned by a control in the product’s own suite.

1.4 Open http://127.0.0.1:7654/console in a browser. You will be redirected to /console/login — that redirect is the mechanism working. Enter the username and password. You land on the queue.

1.5 Log out with the log out button at the foot of the queue page. It is a POST, and it invalidates the session on the server, not just in your browser.

Logging in day to day

2.1 Go to /console. With a live session you land on the queue directly.

2.2 Without one — no cookie, an expired one, one from before a restart — you are redirected to the login form, with where you were going carried along, so logging in puts you back there.

2.3 A failed login says exactly this, and nothing else:

Wrong username or password.

That message is identical for a wrong password and for a username that does not exist, by design — and both cost the same amount of work, so the timing does not reveal the answer either. The login route is not a way to discover who your admin is.

2.4 After 5 failed attempts from one address, the next attempt is refused with 429 and a Retry-After, for the remainder of the 15-minute window. A correct password does not clear a tripped lockout — only time does. Wait it out, or restart the server if you are the one locked out and cannot wait.

Rotating the credential

Rotate when you suspect exposure, when someone who knew the password leaves, or on whatever schedule your policy sets.

READ THIS BEFORE YOU START. ROTATION IS TWO STEPS, AND THE SECOND ONE IS THE ROTATION. Writing the new password (§3.1) changes a file on disk. A server that is already running does not read that file again. Until you restart it (§3.2), the old password still logs in and the new one is refused. If you are rotating because a password is compromised, then between §3.1 and §3.2 the compromised password is still live — and nothing on the console will tell you so.

3.1 Set the new password — the same verb as first login, run against an existing credential. It asks, twice, exactly as it did the first time:

$ saphan console admin set --user marcin --workspace /path/to/workspace
new console password:
retype new console password:
console admin: credential ROTATED for user "marcin"

The output says ROTATED, and it repeats the restart requirement on the spot. The old digest is replaced in the file. That is all this step does.

A mistyped retype is safe. If the two entries differ, the verb refuses, writes nothing, and the OLD password stays on file and keeps working. You are not left half rotated with no way to tell — run it again.

3.2 RESTART THE SERVER. THIS IS THE STEP THAT ROTATES THE PASSWORD. Do not treat it as tidying up after §3.1:

saphan server stop --workspace /path/to/workspace
saphan server --workspace /path/to/workspace

Two things happen here, and both of them are the point:

  • The server reads the credential file. It read it once, when the console first served a request, and held what it read for the life of the process. A running server does not see the new password. Until this restart: the OLD password still logs in. The NEW password is refused with the ordinary "Wrong username or password." — which is exactly what a wrong new password looks like, so the surface cannot tell you which of the two you are looking at.
  • Every open session ends. The session table is per-process too, and it holds rows issued before the rotation. A restart is the only thing that closes them.

A rotation that stops after §3.1 is not half a rotation. It is no rotation at all — the file changed and nothing else did.

saphan server reload is NOT this step. It exists, it succeeds, and it does not touch the console gate — the credential, the session table and the attempt counters are resolved once per process, and reload does not start a new one. An operator who reaches for it instead of a stop/start gets a command that reports success and rotates nothing. Stop the process and start it again.

3.3 Confirm — first the file, then the login:

saphan console admin status --workspace /path/to/workspace

Then open /console in a browser and log in with the NEW password. If the new password works, the restart took. If the OLD password still works, §3.2 did not happen — the process you are talking to is the one that was running before, and it is still holding the credential it read at start-up.

Why the server does not just reload it. The credential, the session table and the attempt counters are all resolved once per process, together. Reloading only the credential would make the console say something false in a more dangerous way: the new password would work while the sessions the old one issued — including one held by whoever you are rotating against — stayed open, and the surface would look rotated. The restart is what makes all three true at once. If that ever changes, §3.2 changes with it; the behaviour above is pinned by a test (a control in the product’s own suite) so the two cannot drift apart in silence.

On this page