libraries

tcmts.design.libraries · #koyd2zhi · md file

Libraries from another codebase: a registry with no registry

A design, partly built. How a codebase depends on part of another, as Unison's lib does, and how people would find libraries without anyone running a registry. Built so far (tcmts.docs.libraries): step 1 of the path below, closure fetch, lib mounting with tcm lib add / rm, read-only libraries, and tcm lib upgrade as one patch; step 2, several sources per library (tcm lib sources); and from step 3, what a library reaches, said when it's added and upgraded. From step 4, discovery, announcing a release as a codes.tcm.release record (tcm release --announce, tcmts.docs.releases), and adding one by its announcement (tcm lib add @alice.dev/geo@1.2.0, upgraded by the announcer's DID; the records' signatures aren't checked yet); the hub (tcm hub, tcmts.hub): its ingest (Jetstream v2, codes.tcm.*; with an API key, its archive first), its mirror job (each release checked against its author's signed record with com.atproto.sync.getRecord, its code mounted by DID) and its pages; and what people say (tcm lib attest, tcm lib trust): reviews, advisories and yanks, a collection each, shown by tcm lib and the hub; and signing in to the hub (AT Protocol OAuth from the visitor's browser), with "People I follow", "Libraries I use" and stars (codes.tcm.star, one per person per library); and tcm lib search, asking a hub.

What a registry does, and what content addressing already gives

A registry like npm does four jobs. Content addressing pulls them apart:

Job npm tcm
Storage the registry anyone who has the hashes. Every codebase that has a library can serve it, as tcm serve already does.
Integrity the registry's checksums free. Sync re-renders, re-typechecks and re-hashes what it receives, so a source can't send different code under a hash.
Naming the registry decides the author claims it: a signed record that "geo 1.2" is namespace hash #x.
Trust npm, and every maintainer of everything you choose whose claims to believe, and pin them.

Storage and integrity need no trust. The only claim left is "which hash is geo 1.2", and that's a signed record, not a registry. Nobody has to publish: a release already names a point in a codebase's history, and any codebase tcm serve serves is already a source.

What a library is

tcm lib add https://alice.dev geo@1.2.0     # by release, from a source
tcm lib add origin #k3x9… --as geo           # by hash, from any source that has it
tcm lib add @alice.dev/geo@1.2.0                # by announced release (see discovery)
tcm lib sources geo add https://mirror.example   # another place to fetch it
tcm lib upgrade geo                              # to the newest release, as one patch

What tcm has, and what this adds

Need tcm today Missing
Fetching only what's missing, by hash push/pull, checked by re-hashing fetching a namespace's closure, not a branch's history
Named versions releases, synced and immutable resolving a release on a remote to its namespace hash
Knowing whether an upgrade breaks you semver checks of the API showing them before an upgrade
Upgrading update and patches: dependents re-derived, failures to todo "geo moved from #x to #y" as one patch over lib.geo
Knowing what code does before running it effects analysis summarising a library's effects when it's added
Finding code type search in a codebase searching what's announced, across codebases

The pieces

  1. Closure fetch. Fetch what a namespace hash reaches (names, definitions, libraries), not the history behind it. The missing, get and put routes already work by hash; this adds a walk from a namespace.
  2. lib namespaces. Mounted read-only, named as the library names them, shown as lib.geo.area. Apps and tests treat them like any code: cached by hash, so a library's tests that passed anywhere don't run again.
  3. Upgrades as patches. tcm lib upgrade geo maps each definition of the old version to its new one, by name and kind, and applies the mapping as one update. What still typechecks follows; what doesn't goes to todo, on the old version. The semver report comes first ("2 breaking changes: area takes a Shape now").
  4. Trust signals when adding. Effects, computed from the code ("geo reaches nothing outside itself; geo.fetchTiles reaches the network"); attestations from others; the licence, read from its manifest.
  5. Discovery over AT Protocol, below.

Discovery

Records

Authors announce releases as AT Protocol records in their own repositories. The repository signs them, so an author's identity is their DID, and pinning a library's DID the first time it's added replaces pinning keys.

Announcing is optional: tcm release --announce writes the record. Unannounced releases still work by URL or hash.

The hub

A hub is a tcm app anyone can run. It ingests the records, mirrors the code they name, and shows it.

Every hub is also a source: what it mirrors, it serves.

The site

The forge (tcmts.share) renders a codebase it's given, so the hub shows mirrored code with the forge's pages (CodePage, NamespaceView, DefinitionView, ReleasesPage, SearchPage) and adds:

The userinput example's network code (polite fetching, OAuth sign-in) is where reading the network and announcing start from.

How it differs

Open questions

A path there

  1. Local first: closure fetch, lib mounting and upgrades as patches, from another codebase or a tcm serve remote, by release or hash. Useful alone: sharing between your own codebases, and split repositories that stay one model. It needs no trust layer, since a hash is its own proof.
  2. Sources and mirrors: several per library, fetched from whichever answers.
  3. Trust signals: effects summaries when adding, attestations.
  4. Discovery: codes.tcm.* records (under tcm.codes), tcm release --announce, tcm lib add @handle/geo, the hub and its site.