Skip to content
pith-languagePublic

About

Github mirror for https://codeberg.org/pith/pith

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Pith, a computation kernel for build, package, environment, and system tooling

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

One graph, and what it lets you skip

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:

A cold build plans five action computations, editing one source plans three, nothing changed plans none and is reused, and a new process over the same store plans none and is hydrated.

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.

Effects are where the kernel draws its line

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.

Domains are libraries, ours included

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.

What runs today

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-workspace declares 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.

Reading the notebook

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.

Working in the repository

The development environment is pinned with Nix.

nix develop
just test

just 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.

About

Github mirror for https://codeberg.org/pith/pith

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Used by

Contributors

Languages