01 / systems / 05 of 05
A command-allowlisted shell for trading hosts (prototype)
Operators need a shell on trading hosts, and full sudo is too much. This is a sandbox prototype of a shell that runs only approved commands, keeps a tamper-evident audit log and can lock a user out of every host within five seconds. It has not gone to production.
Why it mattered
Giving operators sudo on a trading host means trusting every command they type, and keeping a log that the same sudo can edit. We wanted operators to have a fixed list of commands, an audit trail nobody can quietly rewrite, and a way to cut one person off everywhere at once.
It started as a research task into how restricted operator shells are usually built, and became a prototype for our own hosts. I built it in a Docker sandbox with mock credentials over two days in October 2026: 27 commits and about 61 Python files.
Two rules that could not bend
The second rule cuts across the rest of the design. Approvals, certificates and logging each add a dependency, and kill had to keep working with any of them down. Every safety feature needed a path around it for that one action.
- Disabling a user must end their sessions on every host and block new logins within 5 seconds.
- An emergency kill is one person's decision. It never waits on an approval, and it must not depend on the CA or the logs.
The design
Operators log in with short-lived SSH certificates from an internal CA, plus a password and a TOTP code. A short-lived certificate expires on its own, so a forgotten grant does not last forever. For the cases where waiting for expiry is too slow, an OpenSSH key revocation list blocks a disabled user on every host.
Commands come from a signed catalogue and policy. The shell runs only what the catalogue lists, and the sudoers rules on each host are generated from the same policy instead of being written by hand. Risky commands need a second person to approve them. Kill never does.
Every action goes into a hash-chained audit log. Each entry carries the hash of the one before it, so editing or deleting an entry breaks the chain from that point on. An offline verifier checks the chain without needing the live system.
A file guard watches the policy, the revocation list and the sshd configuration with inotify and compares hashes. If one of those files changes, it puts the known-good copy back in under a second. There is also a break-glass path for when the normal one is down, with written runbooks.
Stack: Python 3.12 for the shell, the policy engine and a host agent; OpenSSH certificates and KRL; generated sudoers; inotify; Docker Compose with 8 services; a bash test runner.
How the 5-second disable works
The 5-second figure is a requirement checked in the sandbox: from disabling a user to every session ended and new logins blocked on every sandbox host.
Testing and review
The sandbox has 105 acceptance tests. Each one has passed at least once on the final build. One clean run of the whole suite on the final tree is still to do, and until that happens I do not say the suite passes.
The repository also holds security, compliance and owner reviews, runbooks and an 11-chapter user handbook. The prototype was accepted at the end of its second phase. It has not gone to production, and a list of production gates is still open.
What I'd change
First, the clean full run. Tests that each passed once across several builds have not yet shown they pass together on one tree, and that is the claim a production review will ask for.