Skip to content

[ops] Serve every deployment from one command, surviving reboot and crash — and the origin problem behind a live/paper switcher #756

Description

@eaitbrahim

Operator request, from running v0.14.0: one command that serves every deployment, a way to move between them in the browser, and both surviving a reboot or a crash.

What exists today

keel serve is one process, one --db/--config pair, one port (DEFAULT_PORT = 8765). modeBadge states the invariant: "one process serves one --db/--config pair". Starting the live console is:

./keel-live serve

The deployment has four of these, not two — keel-live (config.live-sandbox.yaml + keel-live.db), keel-paper (config.paperforward.yaml, default keel.db), keel-paperhourly (config.paper-hourly.yaml + keel-paperhourly.db), and config.paper-equities.yaml. Any design here should assume N, not 2.

launchd plists exist for the agent cycles (com.keel.live, com.keel.paperforward, com.keel.paper-hourly, com.keel.paper-equities) and for nothing else. There is no plist for serve, so the console is manual today and dies with its terminal.

The blocker, which is not the obvious one

Two servers means two ports, and a different port is a different origin. That breaks the switcher in a way a link cannot fix:

  • new_session_token() mints a token per process, and security.py is explicit that "nothing is persisted on this side of the wire."
  • The session cookie is per-origin, so :8766 never sees :8765's cookie.
  • A link from the live console to the paper console therefore arrives unauthenticated and lands on the 403 "refused" view.

So "switch between modes" is not a nav item. It requires one of:

  1. A token the operator can carry — pasting the other server's URL, which is what the refused view already offers. Works, but it is the manual step this request exists to remove.
  2. Cross-origin token issuance — precisely keel link prerequisite: the remote-exposure security pass — bind opt-in, tunnel Host validation, token issuance off loopback #648's subject, and it must not be invented here.
  3. One process serving several deployments — see below.

Why "one process, several databases" is the tempting answer and the dangerous one

It removes the port and token problem entirely: one origin, one token, a real switcher. It also puts the live database and the paper databases behind one process and one token, and makes "which deployment is this row from" a runtime variable rather than a process-level fact.

The current design leans on that fact hard. modeBadge's tooltip names the pair because the pair is fixed. A rendering bug in a multi-deployment server could show live figures under a paper label — the exact class of false claim _session_banner refuses to make, and on the one surface where the stakes are real money.

Not ruling it out. Recording that it changes the blast radius of a UI bug from "wrong number" to "wrong account", and that it needs the argument made explicitly rather than arrived at because it was convenient.

Auto-start is the separable, easy half

RunAtLoad + KeepAlive in a com.keel.serve.*.plist covers boot and crash restart, and needs none of the above resolved. Notes for whoever builds it:

  • One plist per deployment, mirroring the existing four, each pinning its own --port.
  • How does the operator get the URL? Under launchd nobody reads stdout; the token goes to StandardOutPath. So the printed URL — the only way in — becomes a line in a log file. That is workable (grep the log) and it is a genuinely worse experience than today's terminal. It is also the strongest argument for a keel console / keel open command that reads the running server's URL, which does not exist.
  • A restarted process mints a new token, invalidating every open tab. KeepAlive therefore makes the An installed PWA cannot authorise itself after a serve restart — start_url carries no token #634 tail routine rather than rare: after any crash-restart, an installed PWA with an evicted worker cache has no way back in. That is Device pairing for the console: a verifier on keel's disk, the secret on the device — blocked on #648 #656's case, and KeepAlive makes it common enough to matter.
  • Ports must be pinned per deployment and recorded, or two servers race for 8765 and the loser exits.

Suggested split

Acceptance

  • One command starts every deployment's console; one stops them
  • Consoles come back after reboot and after a crash, with no operator action
  • Each console's port is pinned and recorded; no two race for a port
  • An operator can obtain a running console's URL without reading a log file by hand
  • modeBadge still names the exact --db/--config being served, per console
  • No control that changes a deployment's mode, profile, or equity state appears in any console

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions