← All posts

0sh: a shell where authority is declared, not assumed

A shell script. Five functions. The third function calls a utility that hits an API. You find out in production — the call succeeded and mutated something it shouldn't have. You trace it back: the script had the API key in its environment. It was set in a terminal session three weeks ago to test something. Nobody cleaned it up. It survived across sessions via shell history or an init file. The utility inherited it. The script 'worked'. In bash, inheritance is the default, and default is the problem.

This failure is ordinary. It happens constantly. And every shell since sh has the same underlying cause: authority is ambient and inherited, not declared and scoped.

How shells make this structurally worse

Every variable in the parent environment is visible to every child in bash. PATH is ambient. Secrets you set are ambient. Functions can reach any variable in scope without declaring they need it. There's no way to look at a function and know what external state it depends on — you have to read the entire call tree.

The standard mitigations: use subshells to limit scope, unset sensitive vars after use, use `local` in functions, be careful. This is discipline-as-security. It works until someone forgets, or until a helper script you didn't write does something unexpected with an env var you forgot was there.

The problem isn't that bash is poorly implemented. It's that bash's security model is: trust the programmer. That's not a model. That's the absence of one.

0sh: authority is declared, not inherited

0sh is being built around a different primitive: every execution scope declares what it needs. A function that calls an API declares it needs the API key. A block that writes a file declares it needs write access to that path. If something isn't declared, it isn't available — regardless of what the parent scope contains.

Authority flows down explicitly. Nothing bleeds up from a previous session. Nothing inherits from a parent process that happened to have a variable set. The script is its own declaration of what it touches.

PortableText [components.type] is missing "code"

0sh is in early design — the parser is in progress and the contract syntax is being finalised. This is what it's building toward.

The invariant

Every shell since sh has treated authority as inherited by default. 0sh treats it as declared by default. That's not a feature added to an existing shell. It's a different model of what a script is: a declaration of what it touches, not a sequence of commands with whatever happens to be in scope.