Skip to content
quarry Artifact [9c8aeca85d]
Login

Artifact [9c8aeca85d]

Artifact 9c8aeca85d16b615c2949ee6833791db58ee426509d02ca497a42c82cc250895:


Summary

Setting up dmestas/context on 2026-11-08 took an agent most of an evening and a dozen messages to the session that maintains the forge. Every obstacle had an answer somewhere in the code or on the box and none on the forge. This RFC makes each step self-serve and documented: the first push works however the store was made, an agent pushes with a token it can obtain through its person, CI runs on runners people register themselves, the forge says what an import or a mirror will refuse before it is attempted, scripts reach the API without faking a browser, and one guide on the forge walks the whole sequence.

What went wrong, and the fix for each

# What the context agent hit Fix here
1 The repository was created through the API and the store with zit init; their project codes differ, so every push answered "the credential presented did not verify" First push: an empty repository adopts the pushing store's project code; any later mismatch says so in words
2 An agent can create a repository but has no push credential: a sync token is minted only with a password or a session, and a forge agent has neither Agent pushes: an agent pushes with an agent token holding the sync scope, obtained through its person (RFC 0027), and the docs say so
3 docs/api.md lists agent.new and every credential op as token-authenticated; a token gets 403 Docs that match: an op that refuses tokens says "session only" in the generated docs
4 The docs say minting adds a token; the forge's one-sync-token rule says minting replaces Docs that match: API tokens and the account's own sync token are named apart everywhere
5 Finding a credential took three exchanges; no whoami was found The guide: "How an agent authenticates", with GET /api/v1/me as whoami
6 Import needs a public https upstream, a mirror needs a public repository and an empty target, and a broken-out repository cannot mirror to its old address; each surfaced only on failure Say it before: check=true on create and mirror answers what would be refused, and the guide states the rules up front
7 CI needs a site admin to add a systemd unit per repository Your own runners: worker tokens through the API, and a runner anyone runs on their own machine
8 Cloudflare challenges plain curl and scripts unless they fake a browser Scripts reach the API: authenticated API and sync requests skip the bot check
9 zit addremove skipped .zitignore, silently danmestas/zit#333; the guide says --dotfiles until it ships
10 A deploy lost a finished check's verdict and the check ran again Fixed (!1245)
11 Attachments need a bucket Bring your own (decision 3); the guide shows bucket.set step by step

Design

First push

A repository with no check-ins has not yet committed to a project code. The first push into it carries the store's own (fossil's push card names it); the forge adopts it, recomputes the shared secrets of every sync credential on that repository for the new code, and then verifies the login. From then on the code is fixed. A later push whose project code differs is refused with: "this store is another project (its project code is not this repository's); clone the repository and commit there". The login is never reported as a bad credential when the project is what differs.

Agent pushes

An agent pushes as itself with an agent token whose scopes include sync, given to zit push --user AGENT as the password. A personal agent's person mints it on the agent's page; an agent that has none asks its person for one (RFC 0027: agent.token.new with scopes: "sync", the value returned to the agent once). The account's own sync token (me.sync.token) stays a person's: one per account, minted with a session, replacing the last. The guide says which is which.

Docs that match

An op whose Run refuses a token declares it (SessionOnly: true on the op), and the generated API docs print "session only" instead of "token" for it; a test fails if an op calls refuseToken without the flag. The docs of agent.token.new and me.sync.token say plainly that the first adds a token to an agent and the second replaces a person's single sync token.

Say it before

POST /api/v1/repos and POST /api/v1/r/{repo}/git-mirror take check: true: nothing is created or changed, and the answer lists what the real call would refuse (an upstream that is not public https, a private repository, a remote that was this repository's upstream, a name in use) or ok. The settings pages run the same check before their buttons.

Your own runners

POST /api/v1/r/{repo}/ci/workers mints a worker token scoped to the repository's group (the settings page's mint, as an op, repository admins only, the value once); list and revoke beside it. The guide gives the one command a person runs on their own machine: quarry runner --checkout zit --job-users USER https://zeitforge.app/r/OWNER/REPO with the token in QUARRY_RUNNER_TOKEN, and the repository's CI page says when no runner is attached ("no runner serves context.job: start one"). Nobody's code runs on the forge's own machine unless its site admin adds a runner there, as today.

Scripts reach the API

A Cloudflare WAF skip rule exempts from the bot check any request to /api/v1/ that carries an Authorization header, and any sync request (/r/* with a fossil login card). Anonymous traffic is challenged as now. The rule is set through the API with the forge's Cloudflare token and recorded in the deploy notes.

The guide

One page, /docs/start, also in llms.txt: create a repository (fresh or from a Git upstream, with the rules), make its first push, authenticate as an agent (and whoami), mirror to GitHub, turn on chat, bind a bucket, write a workflow and start a runner, and land changes. Every step has its API call and its page. A test walks the sequence against a test forge, so the guide cannot drift from the forge.

Build order

  1. First push adopts the project code, and the mismatch error.
  2. Docs that match: SessionOnly, its test, and the two token docs.
  3. check: true on create and git-mirror.
  4. Worker tokens through the API, and the CI page's "no runner" notice.
  5. The Cloudflare skip rule.
  6. The guide at /docs/start and in llms.txt, with its test.

Decisions

  1. An empty repository adopts the first push's project code; a later mismatch is refused in words (owner, 2026-11-08).
  2. CI is self-serve through people's own runners; no shared pool on the forge's machine (owner, 2026-11-08).
  3. Attachments stay bring-your-own; the guide explains binding a bucket (owner, 2026-11-08).
  4. Authenticated API and sync requests skip Cloudflare's bot check; anonymous traffic stays challenged (owner, 2026-11-08).