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 } } }.typeisgithub(resolves toraw.githubusercontent.com/<name>/<branch>/) orhttp/https(thenameis a base URL).ccpm.lock.json— installed packages{ packages = { <name> = { version, registry, files, dependencies, autostart } } }, used byls,uninstall, andreinstall.ccpm.cache.json— packages advertised by configured registries, written byccpm updatefrom each registry'spackages/index.json, used byccpm search,ccpm available, andccpm 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 bystartup/motd.luaandstartup/servers.luaafter 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'sccpm.jsonis), but it is still the local system state used at boot for the coloredTrapOS v<version>banner and to read theautostartlist.branchis the persisted beta opt-in (a single confirmed--betainstall switches subsequentccpm upgraderuns tonextwith no flag needed;--stableis the symmetric opt-out);/trapos/ccpm.lock.json— so right after a fresh installccpm install trapos-corecorrectly reports "already installed";/trapos/ccpm.json— seeding/refreshing the defaultguillaumearm/cc-libsregistry to track the install branch.
The install path is:
wget run .../install-ccpm.lua— installccpm(trapos-core) and seed the default registry.ccpm update— refresh the local package cache.ccpm install trapos— install the full OS. During beta,traposincludestrapos-testby 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(formerlyjust craftos; see ADR-0005) no longer derives mounts frommanifest.json .files(it is now.packages); it mounts a fixed list of top-level dirs instead.just testwas 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 injectedhttpstub — 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).