← All posts

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

Every shell I've used treats authority as implicit. The current working directory is ambient. Environment variables bleed across boundaries. Secrets injected at the start of a script are still visible in the last command. When something goes wrong, you reconstruct what happened from logs that were never designed to be read by a machine.

0sh is an attempt to build a shell where this is not the case. It's in early design — the contracts are being frozen and the parser is being built. This post is about what it's trying to become.

The core idea: execution as evidence

The more accurate framing is: 0sh is a constrained execution language whose primary output is evidence.

Most shells output text. 0sh outputs an execution graph. Every command in a script produces a typed node in that graph. Every data flow between commands is an explicit edge. The graph is a first-class artifact — inspectable before execution, verifiable after, and structured enough for agents to consume deterministically.

The goal is that after a 0sh script runs, you can answer: what did it do, what did it read, what did it write, what secrets did it touch, and what was the causal chain? Not from reconstructed logs, but from the execution graph itself.

No ambient authority

The authority model is the most opinionated part of the design. There is no ambient current working directory. There is no environment bleed. Every effect a script may have is declared before it runs.

The five effect classes — declared at plan time, not discovered at runtime
EffectWhat it covers
reads_filesystemReads from files or directories — requires an explicit scope
writes_filesystemCreates or modifies files — scope-bounded, temporary
uses_secretsAccesses secret material via tsafe — secret.resolved → injected → revoked → destroyed
network_accessMakes network calls — declared per-host allowlist
emits_eventsProduces lifecycle or audit events — always on for CellOS integration

File access requires an explicit scope block that names the path. Access cannot escape the declared scope — ../ is rejected. The scope is revoked when the block exits. Secrets follow the same pattern: resolved, injected, revoked, memory zeroized.

PortableText [components.type] is missing "code"

You can inspect what a script will do before it does it. The plan command shows the execution graph with all effect declarations for every node. This is intended to make 0sh safe for agent-driven automation — an agent can read the plan, verify the declared authority, and decide whether to proceed.

Language heritage

0sh is a synthesis. The design draws deliberately from existing languages, keeping what works and redesigning what doesn't.

What 0sh borrows — and what it rejects
LanguageWhat to keepWhat to reject
PowerShellObject-first pipeline thinking, operational ergonomicsVerbosity and ceremony-heavy constructs
zshCommand-line fluidity, composability, quick interactive workflowsHidden state and implicit expansion surprises
RustExplicitness at boundaries, strong correctness defaults, safety mindsetSyntactic overhead for routine scripting tasks
GoStraightforward concurrency model, practical operational tooling styleLimitations that reduce expressiveness in script contexts
PythonReadability, expressive scripting flow, low-friction automationAmbiguity that harms determinism and auditability

The synthesis rule: if a borrowed feature makes common ops workflows clearer and safer, keep it. If it increases ceremony or ambiguity, redesign it.

Three levels of progressive disclosure

The language should feel good at three levels without changing core semantics.

Progressive disclosure levels
LevelWho it's forWhat it looks like
Quick scriptingDaily interactive useMinimal ceremony, intuitive defaults, short feedback loops — feels like a fast shell
Robust automationScripts and pipelinesTyped values where they prevent real mistakes, reusable functions, deterministic plans
High-assurance executionCI, agent workflows, compliance contextsPolicy-aware constraints, strict authority declarations, audit-grade execution graph

The same language at all three levels. The difference is how much of the authority and lifecycle machinery you engage with.

Where it fits in the stack

0sh is being built as the execution language for CellOS — the typed authoring and orchestration surface for cells, plans, and operator workflows. The authority model is designed to compose directly with CellOS cell specs: a 0sh script running inside a cell inherits the cell's declared authority and cannot exceed it.

How 0sh fits the governance stack
LayerRole
tauditScans pipeline YAML — before 0sh, when you're still using standard CI workflows
tsafeSecret broker — 0sh resolves secrets through tsafe; lifecycle events track every access
CellOSExecution host — 0sh scripts run inside cells; cell authority bounds what the script can do
0shThe authoring language — typed, authority-aware, evidence-producing

The long-term aim is that writing a 0sh script inside a CellOS cell is the normal way to express automation that needs to be auditable, agent-safe, and least-authority by default. Not a special mode — the default.

What exists today

The contracts are being frozen: AST schema, IR schema, execution graph node and edge types, lifecycle states, event emission points. The parser is being built. The design is in active development.

This is early. The post is about the design goals, not a shipping product. But the contracts-first approach means the shape of the language is being locked down before the implementation — which is the right order for something that needs to be machine-readable and stable.

Language tenets

  • PortableText [components.type] is missing "span" — fast startup, tight feedback loops, no ceremony tax on simple tasks
  • PortableText [components.type] is missing "span" — the same constructs work at every level of the stack
  • PortableText [components.type] is missing "span" — no ambient access, no hidden state mutation for authority-sensitive operations
  • PortableText [components.type] is missing "span" — source ranges, intent-aware suggestions, stable formatting and linting
  • PortableText [components.type] is missing "span" — the execution graph is the product, text output is secondary