# ppm — design notes

This document is the long-form companion to `AGENTS.md`. It records *why*
the design looks the way it does, so future maintainers (human or AI) can
push back against pressure to change invariants without context.

## Goals

1. **Boring.** A package manager should be the least interesting piece of
   your toolchain. No surprises, no novel UX, no clever defaults.
2. **Honest.** Every artifact is content-addressed and signed. If `ppm`
   tells you a package is installed, you can verify it byte-for-byte.
3. **Atomic.** A power loss or `SIGKILL` mid-operation must never leave
   the system half-upgraded. Either the new generation is the active one,
   or the old one still is.
4. **Reproducible.** Same inputs, same outputs. Same `ppm.toml` and same
   registry commit must yield the same `ppm.lock` and the same on-disk
   tree, on any machine.
5. **Distro-friendly.** `./configure && make && make install` works. No
   bundled libraries at runtime. Standard FHS paths.

## Non-goals

- Multi-version coexistence of a single package. If your application needs
  two versions of `libfoo`, that is a sign that one of them should be
  vendored or that the API should be stabilised. ppm will not paper over it.
- Binary patching, partial updates, or delta downloads. Tarballs are whole.
- A custom transport. Tarballs ride HTTPS; the index rides git.
- Windows or MSVC support.

## Threat model

- **Untrusted registry mirror.** Anyone may host a mirror of the registry
  git repo. Trust is rooted in Ed25519 signer keys pinned by the registry
  itself (in `signers/`) plus a user-configured trust anchor.
- **Untrusted tarball host.** Tarballs may be served from arbitrary URLs;
  integrity is established by SHA-512 from the registry manifest.
- **Hostile package author.** Cannot retroactively modify a published
  manifest because the registry is a git repo with signed commits, and
  each manifest carries its own detached Ed25519 signature.
- **Out of scope:** local root compromise, kernel bugs, hardware attacks.

## Why git for the registry

- Free hosting on any forge.
- Built-in immutability per commit.
- Easy to mirror and audit.
- The diff between two registry commits *is* the changelog.
- We can pin to a commit hash in the lockfile, which closes the
  "TOCTOU on the index" hole that classic package managers leave open.

## Why PubGrub

- Deterministic and complete.
- Produces *good error messages* on unsatisfiable inputs.
- Already battle-tested in `pub` (Dart) and `uv` (Python).
- Naive backtracking is correct but slow, and worse, opaque when it fails.

## Why SHA-512 over SHA-256

- Faster on 64-bit hardware.
- More headroom against future cryptanalysis.
- libsodium ships it as a first-class primitive.

The cost is a slightly longer on-disk path. Not enough to matter.

## Why a one-version rule

The diamond-dependency problem is unsolvable in the general case without
either (a) duplicating libraries with all the symbol-conflict pain that
implies, or (b) refusing the install and letting the human resolve it.
ppm picks (b), loudly. A clear error message beats a silent miscompile.

## Atomicity sketch

```
                    +---------------------+
   ppm install ---> | begin txn           |
                    | write journal entry |
                    | stage extract dirs  |   (under tmp/<id>/)
                    | fsync all of above  |
                    | rename(2) to final  |   (sha512/<aa>/<full>)
                    | flip profile link   |   (renameat2 swap)
                    | delete journal      |
                    +---------------------+
                              |
                              v
                       crashed somewhere?
                              |
                              v
                    +---------------------+
   next ppm  -----> | ppm_txn_recover     |
                    | replay or rollback  |
                    +---------------------+
```

## Open questions

- Sandboxed builds (`bwrap`?) — needed only when ppm starts compiling from
  source. MVP is binary-only.
- Cross-compilation manifests — out of scope for v0.x.
- Multi-arch tarballs — handled at the registry layer with per-arch
  source URLs in the manifest. Sketch only, not yet implemented.
