Software passes through a chain of tools before it runs. One resolves dependencies, another builds them, another assembles an environment, another describes a machine, another deploys it. Each tool rebuilds a version of the same graph under its own identities, caches, and effect rules, and information is lost at every handoff. Pith is an attempt at the mechanism those tools could share, and at the point where that mechanism has to stop and hand the rest to a domain library.
Design notebook · Problem · Principles · Milestones · Decisions
Every value is identified by its content and every computation by what it was
derived from, so a second build is a question about identity rather than about
timestamps. Two C sources over a shared header, from
crates/xylem/tests/two_source_build.rs:
Editing one source leaves the other source's header scan and compile answered from the durable index; the link runs only because the object it consumes moved. The last row is the one that needed a database: close the engine, open a new one over the same store, and the build hydrates from the attempt the first engine recorded instead of from a copy of it.
Pure computation and external work share one graph without being given the same
semantics. Sealed categories in
pith-core decide what may be cached, what
must be declared, and what the scheduler may do with it:
| cacheable | today | ||
|---|---|---|---|
Pure |
computes from immutable values, terminating by construction | indefinitely | runs |
Action |
bounded external work with declared inputs, outputs, platform, and capabilities | when the executor honored the contract | runs, confined |
Observation |
reads external state, recording source, revision, and freshness | across an attested revision | runs |
Mutation |
changes external state | no | designed |
Opaque |
unmodeled work behind a fixed-output boundary; the escape hatch | no | designed |
The categories are sealed: adding one is a kernel change argued in a decision record, not something a library can reach for.
An action's contract is checked rather than trusted. The Linux executor stages declared inputs and the toolchain closure into a scratch root, runs the child under a Landlock ruleset and a seccomp allowlist measured from real compilers, and captures only the declared outputs. The child cannot reach an undeclared header at all, so it cannot go quietly stale.
Values, rules, dependencies, effects, identities, content, and provenance belong to the kernel. Builds, packages, services, and systems do not. A domain library is two registration calls:
impl ExampleEngine for Engine {
fn register_example_domain(&mut self) {
self.register_action_rule(RenderAction::rule(), RenderAction);
self.register_rule(RenderRule::rule(), RenderRule);
}
}Incremental reuse, cross-process hydration, and contract inspection follow from
those two calls and nothing else. xylem (builds), phloem (packages and
environments), and stele (system composition) are clients on exactly these
terms. They take their names from the tissues around a stem's pith, and the
kernel has no built-in notion of a package, service, machine, or deployment.
The claim is checked by a test:
crates/example-domain is a library the kernel knows
nothing about, depending on no other domain and named nowhere else in the
workspace, and it collects the same properties the first-party domains are
measured on.
A working prototype, not a tool for general use. There is a source language and
a command line. A project is one file, pith.pi: a header declaring its
domain/name subject, its inputs (today path dependencies), its workspace
membership, and the files it includes, followed by the declarations
themselves. The first command that resolves a project's inputs writes a lock
beside the file. The remaining module-system work (registries, version
resolution, argument passing) is what M-14 and
M-17 still owe. What exists is one vertical
slice:
- typed rule selection, incremental evaluation, and equality-based pruning when a recomputed dependency lands on the value it already had
- content-addressed blobs and trees, engine state in SQLite, and state adapters held to each other by a generated conformance suite
- confined local execution on Linux, with a syscall allowlist measured from the tools themselves
- builds under real toolchains with discovered header dependencies; package resolution, locks, and admitted binary substitution; development environments over a lock; and an immutable Linux tree composed from files, users, units, and boot configuration
- multi-module programs:
examples/local-workspacedeclares two projects, one inputting the other by path, and runs a pure entry that computes once and hydrates in the next process
Observation identity and freshness now run in the kernel. An observation rule derives a typed subject, an observer returns a value and revision, and a pure consumer is reused or hydrated only after the same observer attests the same revision. The thin prototype observes a file's modification time; Linux system activation remains the first domain consumer.
docs/planning/milestones.md is the boundary
between demonstrated and proposed. Each milestone records what was measured,
what it still owes, and which of its own claims later rounds overtook.
Most documents describe a direction rather than released behavior, and their status separates accepted decisions from proposals, research, and living references.
| Problem, scope | what this is for, and what it refuses to become |
| Design overview | the four layers and the kernel boundary |
| Principles | the constraints the architecture answers to |
| Research | Nix, Bazel, Terraform, CUE, and others |
| Decisions | numbered records, each ending in a measurement |
A record usually opens with a primary source and closes with a test.
The development environment is pinned with Nix.
nix develop
just testjust lists the available commands. just check runs formatting and static
analysis, just ci runs the full local suite. The Linux executor and system
fixtures compile to zero tests elsewhere, so a green run on another platform has
not exercised those paths.
Contributions are guided by CONTRIBUTING, including the project's authorship and AI-tool terms.
Pith is licensed under Apache 2.0. Third-party notices are collected in NOTICE.