Fork channel

Create a new channel as a copy of main.

Rename channel

Rename main to:

Delete channel

Delete main? This cannot be undone.

mode name
drwxr-xr-x get-started/
drwxr-xr-x internals/
drwxr-xr-x macos/
drwxr-xr-x operations/
drwxr-xr-x philosophy/
-rw-r--r-- README.md
-rw-r--r-- doctest.py
-rw-r--r-- sections.json
README

Elpe documentation

Elpe is a universal package installer and build system: it consumes unmodified packages from conventional distributions (Ubuntu .deb, Homebrew bottles, PyPI wheels, npm tarballs, crates, …) and builds your code against them in strict, reproducible isolation — the immutable-store ideas of Nix without bootstrapping a whole distribution. Build recipes are typed OCaml programs; the backend that runs them is Rust.

At a glance — two frontends feed a federation of peer daemons. Every node runs the same backend-agnostic server (Elpe::serve) paired with one isolation backend; the daemon a frontend dials places each build across the compilation farm and streams the content-addressed output back. “Coordinator” is a role, not a separate tier — there is no single “backend” box:

```diagram id=farm-topology caption=The elpe CLI dials exactly one node of a fully-meshed peer federation; that node coordinates and places the build. Every machine runs the same backend-agnostic server (Elpe::serve) — there is one node type, and “coordinator” is just the role the dialed node plays. Peers share a BLAKE3 content-addressed store, action cache, and copy-closure over every edge. Builds are portable by default, so any peer can emit any target; Apple targets are cross-built against the Apple SDK fetched once over SSH from the Mac and then resident in the store, so the Linux farm builds them without touching a Mac. Bazel is not a frontend: it is one builder a recipe can call (Bazel.from_targets runs bazel aquery and replays each action as a native derivation), like the Cargo/npm/PyPI builders. FRONTEND elpe CLI — OCaml recipe (build.ml / shell.ml) builders a recipe can call: Cargo · npm · PyPI · Bazel · … │ gRPC · dials any ONE node (that node then coordinates) ▼ ──────────────────────────────────────────────────────────────────────────── PEER One node type — every node runs the SAME server (Elpe::serve). FEDERATION Five peers, fully meshed (a clique): every edge carries Register/ Heartbeat + shared BLAKE3 store · action cache · copy-closure.

                    ┌──────────────┐
                    │ Linux node   │   ← the dialed node
                    │ x86_64-linux │     (coordinator now)
                    └──────────────┘
                    ╱   ╲   ╳   ╱   ╲          every peer links
          ┌──────────────┐   ┌──────────────┐  to every other
          │ Linux node   │───│ Linux node   │
          │ x86_64-linux │ ╳ │ aarch64-linux│
          └──────────────┘   └──────────────┘
                │   ╲   ╳   ╱   │
          ┌──────────────┐   ┌──────────────┐
          │ Linux VM     │───│ Mac          │  Apple SDK ──┐
          │ x86_64-linux │   │ aarch64-darwin│  over SSH    │
          └──────────────┘   └──────────────┘  → store ────┘

──────────────────────────────────────────────────────────────────────────── Portable by default → any peer emits ANY target (native or cross): TARGETS x86_64-linux aarch64-linux aarch64-darwin aarch64-apple-ios (macOS) (iPhone) └── cross-built vs the SSH-fetched Apple SDK, no Mac in the loop ──┘


The pieces, at a glance:

- [`elpe shell`](get-started/shell.md) — ephemeral dev shells derived from a recipe.
- [`elpe build`](get-started/build.md) — build an artifact, addressed by content in the store.
- A recipe is a `build.ml` / `shell.ml` (or `project.elpe`) — a typed OCaml program.
- Packages come straight from Ubuntu/Homebrew/PyPI/npm/crates.io, consumed unmodified.
- [Whole bootable systems](images-packaging/images.md) are just an `Image.*` build, installed and updated with `elpe-install`/`elpe-update`/`elpe switch`.
- Builds distribute over the [compilation farm](operations/farm.md).
- The lock is an [SBOM with a verifiable SWHID per dependency and a Software Heritage kill-switch](operations/software-heritage.md).
- Bazel is just another builder inside a recipe: `Bazel.from_targets` runs `bazel aquery` and replays each action as a native derivation (`elpe/lib/bazel.ml`) — Elpe owns the build, keeping repro/audit/store-link.

## Guides

1. [**`elpe shell`** — ephemeral dev shells](get-started/shell.md), Linux and macOS.
2. [**`elpe build`** — build a package](get-started/build.md), the simple cases.
3. [**Authoring derivations**](authoring-recipes/derivations.md) for the supported ecosystems —
   Rust/Cargo, Python/PyPI, npm/pnpm, Bazel, Debian, opam — and lockfiles.
4. [**Images and the installer**](images-packaging/images.md) — build a `qcow2`, and a bootable
   **ElpeOS** live installer that lays a real, self-updating system onto a disk.
5. [**The compilation farm**](operations/farm.md) — coordinator/node federation, macOS
   nodes, CPU placement, and an instrumented node with Prometheus + Loki.
6. [**Design**](internals/design.md) — the crates, the OCaml library, the embedded VM, and
   the Rust↔OCaml boundary.
7. [**macOS / Apple Silicon**](macos/darwin.md) — the Darwin infrastructure: the test
   Mac, the SSH-fetched Apple SDK, the Virtualization.framework isolation VM, and
   deployment. Consuming Homebrew is [darwin-homebrew.md](macos/darwin-homebrew.md).

### Supply chain & interop

8. [**Software Heritage** — SWHID-in-SBOM & the kill-switch](operations/software-heritage.md):
   the lock *is* a CycloneDX SBOM, every entry carries an intrinsic, verifiable
   SWHID (ISO/IEC 18670), and a dead upstream is transparently recovered from the
   Software Heritage archive. See also the [deb kill-switch](operations/deb-killswitch.md)
   (recover Debian *source* and rebuild the `.deb`).

## Read the docs in a browser (`serve.sh`)

These Markdown guides also render as a small, self-contained website — a retro
"90s web" look with modern-browser niceties (flex nav, dark mode, accessible
focus, a table of contents). One command, all inside an `elpe shell` that
provisions `pandoc` + `python3`, so there is nothing to install:

```console
ELPE=/home/pe/Projets/elpe/target/release/elpe doc/serve.sh   # → http://localhost:8080/

PORT=9000 doc/serve.sh picks another port. The recipe is doc/serve.ml, the renderer/driver is doc/serve.sh, and the stylesheet is doc/serve.css.

Runnable examples (doctest.py)

Every example in these docs that is meant to work is a real, executable artifact, à la Rust’s doctests. A small harness — doc/doctest.py — extracts the tagged code blocks, wraps each into a complete recipe (the equivalent of rustdoc synthesising a main), and runs it against a real elpe binary. A doc that rots therefore fails the harness.

# from the repo root, with a built elpe on hand:
ELPE=/home/pe/Projets/elpe/target/release/elpe doc/doctest.py
# or a single guide:
ELPE=…/elpe doc/doctest.py doc/get-started/build.md

Without $ELPE the harness still parses, wraps, and validates every block (catching malformed recipes and missing preambles) but reports execution as skip. Pass --require-elpe to make a missing binary a hard failure (CI).

How a block is marked

The harness keys off the fenced-block info string (the text after the opening ```). Two kinds of block run.

Elpe recipes — an ocaml block carrying the elpe directive:

```ocaml elpe
let () =
  build
    (object
       inherit std_derivation
       method name = "demo"
       method! build_inputs = Lwt.return [ ubuntu "coreutils" ]
       method! src = Lwt.return (local_src ".")
     end
      :> derivation)
```

The harness prepends the standard preamble (open Elpe / open Lwt.Syntax) unless the block already opens Elpe, so snippets stay focused on the derivation. If a block contains no build/build_lwt call, the whole block is treated as a single expression of type derivation and wrapped in let () = build ( … :> derivation) — that is the “adds a main()” convenience. The temp file it writes is never named elpe.ml (that would make the recipe’s own module shadow the Elpe library and break open Elpe).

Directives after elpe (comma- or space-separated):

directiveeffect
elpe=build (default)run elpe build on the assembled recipe
elpe=shellrun elpe shell (use with --export-style snippets)
elpe=lockrun elpe lock
no_runassemble and write the recipe, but do not invoke the backend — for recipes whose build needs a VM/network the doctest host lacks; still proves the recipe compiles-as-assembled
cwd=<path>run from this directory (for local_src/native-lockfile recipes that need a project tree), relative to the repo root
ignoreskip entirely (pure illustration / pseudo-code)

Shell transcripts — a console (or sh) block carrying test:

```console test cwd=examples/rust-hello
elpe build build.ml
```

Lines run under bash -euo pipefail; a leading $ prompt is stripped; cwd=<path> sets the working directory. Without test a console block is illustrative and never executed.