Architecture Overview¶
Noether has four layers. Each layer depends only on the one below it.
graph TB
L4["L4 — Agent Interface<br/>ACLI CLI · Composition Agent · Semantic Index"]
L3["L3 — Composition Engine<br/>Type Checker · Planner · Executor · Trace"]
L2["L2 — Stage Store<br/>Content-Addressed Registry · Lifecycle"]
L1["L1 — Nix Execution Layer<br/>Hermetic Sandbox · Binary Cache"]
L4 --> L3 --> L2 --> L1 L1 — Nix Execution Layer¶
Stages run inside Nix-managed environments. Each stage declares its language runtime (Python 3.11, Node 20, Bash) and Nix provides a runtime with the exact dependencies pinned to the Nix store hash.
This is a reproducibility boundary, not an isolation boundary: the subprocess inherits the host user's privileges, filesystem access, and network. Stages can os.system(...), read ~/.ssh/id_ed25519, and make arbitrary HTTP calls. Don't execute stages you did not write unless you have independent isolation (bwrap, firejail, nsjail, a container, a throwaway VM).
What you do get: - The same StageId produces the same output on any machine that has Nix. - No "works on my machine" — dependencies are content-addressed. - No ambient environment leaks — stages cannot access the host filesystem or network unless their effects declare it.
L2 — Stage Store¶
The store is a content-addressed registry of stages. A StageId is the SHA-256 hash of the stage's StageSignature (input type, output type, effects, implementation hash). Metadata (description, examples, cost hints) does not affect the identity.
Stages have a lifecycle: Draft → Active → Deprecated → Tombstone. Only Active stages participate in semantic search. Lifecycle transitions are validated — you cannot un-tombstone a stage.
L3 — Composition Engine¶
Given a Lagrange JSON graph, the engine:
- Type-checks every edge using structural subtyping (
is_subtype_of). - Plans a linear
ExecutionPlanwith dependency tracking and parallelisation groups. - Executes the plan, routing data between stages.
- Traces every stage input/output for reproducibility and debugging.
The CompositionId is the SHA-256 of the graph JSON — the same graph always gets the same ID.
L4 — Agent Interface¶
The only public API is the ACLI-compliant CLI. All output is structured JSON. There are no human-readable error strings mixed into stdout.
The Composition Agent translates plain-English problem descriptions into Lagrange graphs:
- Searches the semantic index for the top-20 candidate stages.
- Builds a prompt with candidates + type system documentation + operator reference.
- Calls the LLM, parses the JSON response, type-checks it.
- Retries up to 3 times on type error, including the error message in the retry prompt.
Crate structure¶
crates/
├── noether-core/ # Type system, effects, stage schema, hashing, stdlib
├── noether-store/ # StageStore trait + MemoryStore + lifecycle
├── noether-engine/ # Graph format, type checker, planner, executor, index, LLM
└── noether-cli/ # ACLI CLI — the only public interface
Each crate is independently testable. noether-core has zero external dependencies beyond serde and sha2.