Skip to content
quarry Documentation
Login

Documentation

Summary

A horizon clone holds a repository from a check-in C forward. zit's horizon draft defines two cuts. The content horizon keeps every structural artifact and elides the file blobs no held check-in references; it needs nothing from quarry beyond fronting /xfer, because the zit subprocess serves it. The structural horizon also omits the manifests below C and stands a seed, a private control artifact asserting the tag state at C, in their place; it needs an origin that mints seeds, labels the hashes that cross the horizon, and answers merge-base. quarry is that origin, and this RFC says how: the seed and the labels are derived from the index plane the same way every page is, the merge-base answer comes from the same ancestry index, and all three pass the §11 disposability test because they are pure functions of store state.

The history a horizon clone does not hold is still on quarry. The RFC names the API that answers for it (annotate, path history, diff and file at a check-in, already owed under principle 5), adds one read endpoint quarry lacks (an artifact by hash with immutable caching), and settles which consumer gets which tool: build-and-test runners already fetch archives and keep doing so; persistent workers keep a warm clone; cold agent environments get the horizon clone, content first.

Motivation

RFC 0001 made quarry the always-on side of zit. The store is the truth and quarry derives everything else, so a clone that leaves history on quarry loses nothing a person cannot get back by asking quarry. That is what makes a shallow clone safe to offer here when it is not safe to offer against an arbitrary Fossil peer: there is a dense, indexed, always-reachable copy, and every page quarry renders is already an answer about history the client need not hold.

The consumer that forces the question is RFC 0021's agent: a short-lived environment on a dagnats worker that must clone, commit and push, with no warm cache and a network between it and the store. RFC 0009's runners do not have this problem, because premerge removed the clone from the test path: a runner fetches an archive of a tree and never holds a repository. Agents cannot use that trick. They write.

zit's horizon draft keeps every artifact's real name and the existing derivation. What it asks of an origin is small and index-shaped, and quarry already has the indexes.

Guide-level explanation

An agent's environment starts with:

$ zit clone --horizon https://quarry.example/r/proj

quarry's sync front sees the horizon pragma, computes the held set from its ancestry index, and the zit subprocess sends every structural artifact and the blobs of the held check-ins. The clone opens, derives branch state exactly, commits, pushes. The push is an ordinary push.

With --horizon=structural, quarry additionally sends the seed for C and labels every parent hash that lies below C. The clone's timeline ends at a horizon edge rather than a missing parent. When the agent runs zit annotate on a path older than C, zit prints the horizon and the quarry URL that answers for it; zit merge of a branch whose base is below C asks quarry for the base and deepens to it before merging.

On the repository's page nothing changes. A horizon clone's check-ins are the same check-ins, under the same hashes.

Reference-level explanation

1. The content horizon costs quarry nothing new

RFC 0001 §7's front passes pull traffic to the per-repo zit serve child untouched, and the child computes the held set and the keep set from the store. quarry's only obligations are two it already meets: front the request, and keep the child's store dense. The capability pragma horizon-served content is the child's, not quarry's.

One front-side rule is new. The §7 gate decodes the request container to inspect login and push cards. It MUST pass pragma horizon* cards through untouched, including to a child whose version predates them, which ignores them by the sync protocol's rule.

2. The structural horizon: three answers from the index plane

The structural horizon needs an origin to mint seeds, label boundary hashes, and answer merge-base. Each is a function of the store's state and nothing else, so each is derived data under RFC 0001 §1 and must pass §11: delete the data directory, re-ingest, get the same answers.

Where they are computed. In quarry, from the per-repo SQLite index that ingest (§4) already maintains for branch state, leaves and ancestry, not in the zit child. Two reasons. The child is one-shot and stateless, and an ancestry walk per request against a large store is the work §4's index exists to avoid. And the seed asserts the tag state quarry displays; minting it from the same derivation keeps the clone's view and the page in agreement by construction.

How they reach the wire. The front already decodes the request. For a request carrying horizon-structural it computes the three answers, encodes them as the reply pragmas the zit draft specifies (horizon-below, horizon-seed with a private file card, horizon-mergebase), and splices them into the child's reply before re-encoding. The child continues to decide which manifests and blobs to send; quarry adds only what the child cannot know. This is the one place the front writes into a reply body, and it is confined to pragma cards and one private file card.

The seed. A control artifact whose T cards apply to C every tag in force at C: the branch tag with its value, every other propagating tag, closed if set, and C's own non-propagating tags, each with the value the index holds. D card at minting time, no earlier than the latest tag event summarized and strictly later than any seed it replaces. U card is the repository's forge agent login (RFC 0021, forge agents), so a seed is never attributed to a person. Clear-signed with the forge agent's key when the repository has one configured.

A seed is minted on request, compared by hash to the one the client offers, sent only when it differs, and never stored. There is no seed table. A repository with a thousand horizon clones at a thousand different C's costs quarry a thousand index reads per sync round and nothing between rounds.

Boundary labels. For each manifest the child's reply carries, every P card naming a check-in not in the held set is a boundary hash. The front emits horizon-below for each, in the reply that carries the manifest. The held set is the zit draft's leaves rule by default.

Merge base. horizon-mergebase A B is answered from the ancestry index with the same bounded walk the timeline graph uses. No common ancestor answers -.

Refusing a seed. A pushed artifact that is a control artifact applying to a check-in exactly the tag set the index holds for it, with a U card naming the forge agent, is a seed that leaked. The front refuses the push with the sync protocol's error card and the text seed is private to the clone that holds it.

3. The push-time cancel check

A commit from a structural horizon that creates a branch writes a cancel card for the parent branch's symbolic tag, derived from the seed. On every push the front already inspects manifests (RFC 0007's branch ownership). It additionally checks each -sym-* cancel card on a branch-creating manifest against the index's branch for the parent. On disagreement it refuses with stale seed: parent is on <branch>, not <name>; re-sync to re-seed. This is the one route by which a clone's derived state enters an artifact, and checking it here keeps a wrong seed from ever reaching the store.

4. The history below the horizon: the API

The zit draft's horizon-served pragma carries a URL. quarry's is /api/v1/r/<repo>/history, a namespace over query ops that exist or are owed under principle 5:

Op Route Status
annotate GET /api/v1/r/{repo}/annotate/{checkin}/{path} exists as a page op; API route to confirm
path history GET /api/v1/r/{repo}/finfo/{path}?from={checkin} owed
diff GET /api/v1/r/{repo}/diff/{a}/{b} exists
file at check-in GET /api/v1/r/{repo}/file/{checkin}/{path} exists
merge base GET /api/v1/r/{repo}/mergebase/{a}/{b} new; the same answer as the pragma
artifact by hash GET /api/v1/r/{repo}/artifact/{hash} new

All are registered ops in the §9 registry, so they appear in the generated OpenAPI and pass the §11 snapshot. None is a new capability; the horizon is the reason to finish the list.

Artifact by hash is the one genuinely new read. It returns the artifact's bytes, Content-Type: application/octet-stream, with Cache-Control: public, max-age=31536000, immutable, because an artifact's content is its name. It is what a horizon clone's targeted demand fetch and an edge cache both want: one blob, no sync round. Authorization is the repository's read capability as for every read op. A private artifact (the sync protocol's private marker) is never served here.

5. Which consumer gets which tool

Consumer Tool Why
Build-and-test runner (RFC 0009) /archive/<checkin> and /archive/pair: Already the design: one tree, no clone, no credential. This RFC changes nothing for it.
Persistent worker a dense clone kept warm per host, synced forward, each job on a copy-on-write copy of the .zit file Pays the clone once per host. Outside quarry's control; written here so the horizon is not sold to it.
Cold agent environment (RFC 0021) horizon clone, content first Must commit and push; has no warm copy; talks only to quarry.
A person at a keyboard dense RFC 0001 principle 7 names zit clone as the exit, and a default clone that is not a full copy weakens that promise.

The agent-account token minted for an environment (RFC 0021, run-scoped tokens) carries no horizon. The clone command does. Making the horizon a property of the token was considered and rejected: a clone shape is the client's choice, and encoding it in a credential would make the credential mean two things.

6. Disposability (§11), applied

Three new derived answers, one test each in the §11 corpus run: for a fixture store and a chosen C, snapshot the seed's T cards, the boundary set, and a merge-base answer; delete the data directory; re-ingest; the three must be byte-identical except the seed's D card, which is minting time. The D card is excluded from the comparison and asserted monotonic instead.

7. Build order

Gated on the measurement. Nothing before step 4 of the zit draft's Appendix A is quarry work.

Rung What Waits on
1 Front passes horizon* pragmas through untouched zit serving the content horizon
2 /artifact/{hash} with immutable caching; /mergebase/{a}/{b}; the finfo route nothing
3 horizon-served URL pointed at the history namespace rung 2
4 Seed minting, boundary labels, merge-base pragma splice in the front the measurement saying structural pays; zit's structural horizon
5 Push-time cancel check; seed refusal rung 4
6 §11 rows for the three answers rung 4

Drawbacks

Rationale and alternatives

Prior art

Unresolved questions

Future possibilities