cc-libs/docs/adrs/adr-0010-ccpm-package-manager.md

5.7 KiB

ADR 0010: ccpm Package Manager

Status

Accepted

Date

2026-06-08

Context

The previous install flow (a LIST_FILES table inside install.lua and a single flat manifest.json read by wget) was all-or-nothing. There was no way to install just networking or just the UI, and no way to add or remove pieces of the OS after the initial install. The project also outgrew the "Trap's ComputerCraft APIs" framing into a small in-game OS — TrapOS — with a name, a version, a boot banner, and a persisted beta channel; that motion is preserved here even though the original install mechanism is fully replaced.

We want a package manager, ccpm ("ComputerCraft Package Manager"), installed first as a standalone user-facing step. After that, a machine can ccpm update, ccpm install trapos, ccpm install trapos-net, ccpm uninstall trapos-ui, and manage where packages come from. TrapOS itself is installed through a trapos meta-package; the wget run .../install-ccpm.lua bootstrap exists only to install ccpm.

Decision

Packages are descriptors over the existing tree

Source files stay where they are (apis/, programs/, servers/, startup/); their install targets remain the same absolute CC paths, so require paths and the dev mounts are unchanged. A package is a descriptor that references those files: packages/<name>/ccpm.json with { name, version, description, dependencies, files, autostart }. packages/index.json lists the packages a registry offers (for ccpm search). There is no ccpm.json at the repo root.

The split is finer-grained than the install examples imply:

package contents deps
trapos-core ccpm, libccpm, eventloop, upgrade, events
trapos-test libtest, runtest trapos-core
trapos-boot motd, servers (startup) trapos-core
trapos-net net, router, ping, ping-server trapos-core
trapos-ui libtui, tuidemo trapos-core
trapos-ai AI client for opencode serve trapos-core
trapos full TrapOS meta-package trapos-boot, trapos-net, trapos-ui, trapos-test, trapos-ai

Two files for ccpm, "manifest" reserved for the OS

To avoid colliding with the OS manifest.json, ccpm never uses the word "manifest". Local state lives under /trapos:

  • ccpm.json — ordered registry list { registries = { { name, type, branch } } }. type is github (resolves to raw.githubusercontent.com/<name>/<branch>/) or http/https (the name is a base URL).
  • ccpm.lock.json — installed packages { packages = { <name> = { version, registry, files, dependencies, autostart } } }, used by ls, uninstall, and reinstall.
  • ccpm.cache.json — packages advertised by configured registries, written by ccpm update from each registry's packages/index.json, used by ccpm search, ccpm available, and ccpm upgrade.

apis/libccpm.lua is the testable core (a factory; http/stateDir/installRoot are injectable for tests). programs/ccpm.lua is a thin CLI over it.

The bootstrap installs only ccpm

install-ccpm.lua resolves only the trapos-core package descriptor (pulling any future dependencies), downloads its files, and writes:

  • /trapos/manifest.json — the aggregated { name, version, branch, files, autostart } still consumed by startup/motd.lua and startup/servers.lua after boot packages are installed. This is the surviving piece of the previous manifest-driven install: it is no longer the install source of truth (each package's ccpm.json is), but it is still the local system state used at boot for the colored TrapOS v<version> banner and to read the autostart list. branch is the persisted beta opt-in (a single confirmed --beta install switches subsequent ccpm upgrade runs to next with no flag needed; --stable is the symmetric opt-out);
  • /trapos/ccpm.lock.json — so right after a fresh install ccpm install trapos-core correctly reports "already installed";
  • /trapos/ccpm.json — seeding/refreshing the default guillaumearm/cc-libs registry to track the install branch.

The install path is:

  • wget run .../install-ccpm.lua — install ccpm (trapos-core) and seed the default registry.
  • ccpm update — refresh the local package cache.
  • ccpm install trapos — install the full OS. During beta, trapos includes trapos-test by default.

On a subsequent upgrade, programs/upgrade.lua delegates to ccpm upgrade, which upgrades installed packages using /trapos/ccpm.cache.json. Users run ccpm update first to refresh available versions.

Consequences

  • The repo gains a packages/ descriptor tree; the flat source layout is untouched.
  • just trapos (formerly just craftos; see ADR-0005) no longer derives mounts from manifest.json .files (it is now .packages); it mounts a fixed list of top-level dirs instead. just test was already on fixed mounts and is unaffected.
  • ccpm logic is covered by tests/ccpm.lua (URL resolution, dependency ordering, cycle/missing detection, already-installed, registry CRUD, cache update, available status, upgrade, uninstall dependency guard) with an injected http stub — no network in tests.

Future Work

  • Version ranges (today a single pinned version per package).
  • http/https registries beyond a plain base URL (auth, caching).