· runawaydevil , security , software
A Shell That Is Not a Shell
The prompt at the bottom of this system looks like bash. It completes paths
with Tab, walks history with the arrows, understands Ctrl+W, and prints
no such file or directory in the right tone of voice.
It cannot run anything. There is no interpreter behind it at all.
What actually happens when you press Enter
The line is split into words, honouring quotes. The first word is looked up in a table. If it is there, that entry's function runs with the remaining words. If it is not there, nothing happens and you get a boxed notice.
That is the entire mechanism. No eval, no Function constructor, no
dynamic dispatch on a string, no fallthrough to anything. Type
guest@runawaydevil:~ $ rm -rf /
and the parser produces four words, fails to find rm, and says so. |, >,
&& and $(...) are not operators here — they are characters that end up
inside a word and get looked up like any other.
The part that is easy to get wrong
The temptation, when building something like this, is a small convenience: one command that takes an expression, or a debug hatch, or a plugin hook that takes a callback name. Each is harmless alone and each one turns a table into an interpreter.
So the rule is written as a test:
for (const name of ['eval','exec','sh','bash','fetch','curl','wget','sudo'])
expect(findCommand(name)).toBeNull()
node exists, but it prints node status — the BBS sense of the word. The test
asserts that too, because a name that looks dangerous deserves to be pinned
down rather than argued about.
There is no filesystem either
~/journal/2026-08-21-eighty-by-twenty-five.md is not a path. It is a key in
a tree of objects built when the site was compiled, from Markdown files in a
repository. ls walks it, cat prints its contents, grep searches them.
Nothing has a writer. .. cannot climb past the root, because there is no
root to climb past — resolution is string arithmetic over an array.
Which means the usual questions about a web shell do not apply. There is no container to escape, no PTY to hijack, no socket to hold open, no host process that exists. The whole system is static files and a program that runs in your browser, and the worst thing a visitor can do to it is press keys.
Why keep the shape then
Because the shape is good. A directory listing is a better index than a
navigation bar: it has dates, sizes, and an obvious way to look inside. grep
is a better search box than a search box. And muscle memory is real — people
who have typed ls ten thousand times should get what they expect.
The commands, the filesystem and the pager know nothing about the DOM. They consume key events and produce ANSI. Point that stream at a socket instead of a renderer and the same system answers a telnet call. That is not implemented and may never be, but the seam is real and it is marked.