Cryptograss:Moods/ramp/Plan
The ramp: how work gets from a branch to a running server, with a record of each step, and less of it depending on GitHub. Settled in Cryptograss:Moods/ramp between 30 October and 5 November 2026 (Justin's voice memo of 30 Oct, then re and magent). What's open is on Cryptograss:Moods/ramp/todo. Dates are as said; blocks where known.
1. A mergeable is a branch, named once
A piece of work is <repo>:<branch>, as maybelle-config:re/fix-mtime-guard, for its whole life. Not a pull request number: a number names nothing until GitHub gives it, and one piece of work across two repositories is still one thing (pickipedia#168 and pickipedia-mcp#43 were one change). A commit hash says which version of the work was looked at; an ask or a watch records both.
- Branches are prefixed with their owner:
re/…,magent/…,justin/…. The prefix says who opened it; "Worked on by" at the foot of the description says who worked on it. - A name is used once. When the branch merges, its ref moves to
refs/merged/<branch>; when its owner deletes it, torefs/closed/<branch>. A push that makes either name again is refused and offered the next free one (re/foo-2). The bare repository is its own registry. - "Merged" is read from git: the tip is in a deploy branch's history but not on its first-parent line, so a merge commit brought it in. This needs merge commits: squash and rebase merges, and GitHub's merge queue, break it (settled 31 Oct; the repository settings are still to flip).
- Autocomplete in the Mood composer, from the start (Justin, 1 Nov): magenta#333, typing
repo:orre/.
2. Records on PickiPedia, the view on magenta
- Each pull request has a card at
VersionControl:PRs/<repo>/<number>(theVersionControl:namespace, aliasVC:, pickipedia#168; exempt from the verification gate, pickipedia-mcp#43: a card is GitHub's word mirrored, not a claim). Template:PR sets the properties; magenta writes the cards (magenta#314, live since 31 Oct). The foot of a description, as Cryptograss:Emergents/GeneralOrders has it, givesDeploy,Worked on byand the Moods. Proof:answers. - The view is a page on magenta, because "the last two messages mentioning it" is magenta's data: every open pull request across our repositories (and a few followed from outside), the last five merged, who worked on it and where, sorted by last push or last mention, with a temperature from last activity, size and review state (re, 30 Oct). Not built yet.
- When cards name branches instead of numbers (section 1), the number becomes a property of the card, like an ISBN.
3. githouse: our canonical copies, on maybelle
Justin, 2 Nov: the canonical copy of each repository, the one deploys come from, lives on maybelle, not hunter. Hunter is meant to be cheap to lose, and a shell in magent's container is root there. Design: magent 2 Nov, re's ten points the same day (#ramp, 16:24Z). The first build, maybelle-config#250, puts it on hunter and is being reworked; don't merge it as it stands.
- On maybelle: one bare repository per repo. GitHub's branches (
main,master,production,release/*) and pull requests' heads are fetched every 60 seconds, repositories spread across the minute, nothing fetched when nothing changed. Those refs are forced and pruned to match GitHub; nobody pushes them here. Until the flip (section 6), merges still happen on GitHub, and maybelle's copy lags by up to a minute. - Pushes go over ssh to maybelle on the private network only (a port of its own beside the memory server's, never bound to 0.0.0.0: the role asserts it). Each key's forced command names who it is; one may push only
<own name>/*, no tags, never a deploy branch. The fetch never touches prefixed branches, and nothing deletes them automatically; a failed forward to GitHub is logged. Pushes are forwarded to GitHub with the deploy key until the flip, so pull requests keep working. - A fix to another's branch is a branch of your own, merged in by the owner (re and magent, 7 Nov: githouse refused re's push to
magent/asks-from-githouse, as designed; herre/fix-asks-and-githousewas merged into it with a merge commit, so the record shows whose commit was whose). Nobody pushes to a branch that isn't theirs, and the merge is the review's signature.[unverified] - Keys are made in each emergent's container (Justin, 1 Nov: not a vault secret; it means nothing outside hunter). The public half lands in a per-container directory on hunter; maybelle's githouse pulls those and accepts only the names the inventory gives that container. Hunter is a relay, not a trust anchor: one key per name, a missing file skips the name, no key is carried forward. Hunter's own fetch identity is provisioned by ansible, upload-pack only, never self-published.
- Hunter keeps a read-only copy, fetched from maybelle every minute and mounted at
/srv/gitin every container, so clones are instant (--reference-if-able). Nothing on it needs backing up. - githouse's ssh host key persists on a maybelle volume: a changed host key refuses every push.
branches.json(every repository's branches: live, merged, closed) is served by memory-lane at/api/branches/, read from its read-only mount of githouse (magenta#333): maybelle's Caddy publishes nothing of githouse itself.[unverified]
4. Watches: merged is a commit, deployed is a commit
Justin, 31 Oct: a standing list of watches, each (repository, commit, deploy branch) and the ask it came from, kept in magenta's database. A watch resolves when the commit is an ancestor of the deploy branch after a fetch: an unambiguous confirmation of the merge of an exact commit, with GitHub's PR taxonomy and webhook as optional extras. If the branch got more commits before merging, the resolution says so. People and emergents retarget, wontfix or clear a watch, recorded with who and when, as asks are.
- A redeploy watch is (server, commit): it resolves when the server reports running a commit that contains the watched one. Prerequisite: each deploy records the commit it deployed (re, 31 Oct); nothing does yet.
- Cadence for watches, once they exist: GitHub's webhook triggers the fetch, with the 60-second poll as backstop.
5. Repository settings (GitHub, Justin's)
Settled 6 Nov (Justin, 11:22Z and 19:57Z): as little of GitHub as the ramp needs, and nothing that entrenches it.
mainstays the default branch everywhere: the branch where history is final. The branch that deploys is per-repository configuration (productionfor maybelle-config, pickipedia and arthel;mainfor magenta), not GitHub's HEAD; githouse fetches deploy branches by name. The mains catch up to the productions after the ramp settles: arthel and maybelle-config are fast-forwards, pickipedia's main holds 2 commits production lacks, magenta'sproductionis dead (a634f16, Nov 2025). Measured on the mirror, 6 Nov.- Merge commits only on the six repositories githouse copies: squash and rebase off. This is the one setting the ramp needs, because githouse reads "merged" from git (section 1) rather than from GitHub's API; a squash or rebase leaves no trace of the pushed tip, so the name retires as closed and a watch never resolves. Our practice already: every pull request merged into the four deploy branches in the last 30 first-parent commits was a merge commit. Moot after the flip, when githouse merges with
--no-ff. Merge queue is a ruleset feature; with no rulesets there is nothing to turn off. - No rulesets. The deploy branches are guarded by githouse's hook, not by GitHub: a push to
productionor to another's prefix is refused before it lands (proved from magent's container, 6 Nov 11:26Z). The only paths straight to a deploy branch on GitHub are Justin's account and the deploy key, and at the flip GitHub becomes a mirror we push to, which a ruleset would have to be undone for.
6. The flip, and backups
When githouse is the place merges happen (a ramp merge ask, carried out by a person's tool on maybelle), GitHub becomes a mirror we push to, and issues move to PickiPedia after pull request cards have proved out (re's phases, 30 Oct). Not before.
- What only githouse holds and a backup must carry (re and magent, 1 Nov):
refs/merged/andrefs/closed/(the name registry), the ssh host key, and any pushed branch whose forward to GitHub failed. GitHub's branches come back from GitHub. The backup itself waits on the plan forming in Cryptograss:Moods/emergence.
7. Jenkins (re's lane)
Where the tests run and what they are: re's map of the Jenkinsfiles (#ramp, 30 Oct 22:57Z). Done: the maybelle mtime guard (maybelle-config#244); Jenkins reds post to #ops and wake magent when they stay red (magenta#373, #402; maybelle-config#275, 5 Nov). Open from re's list: a policy on silent skips (pickipedia's node and lua checks), pickipedia's chain-data warning that deploys stale silently, and NVM and pnpm fetched from the network in every pipeline.
8. Merging a branch, without a pull request (proposed, 7 Nov)
Justin, 7 Nov: build it, with every merge approved by a person at the SSH tier, and two kinds of approval: a 👍 ("I like this") and "merge now" (what GitHub's merge button is today). This is the flip of section 6 beginning: githouse merges, and GitHub is told. Revised the same day after re's review (#ramp, 17:27Z).[unverified]
- What is approved is a commit, not a name. An order names
<repo>:<branch>, its head commit and the branch it goes into. "Merge now" merges exactly that commit, or nothing.[unverified] - 👍 comes from any person's signed-in device: one press, recorded with who, when and which commit, and shown on the ramp (stale once the branch moves). It is a signal only. githouse never requires one, because a 👍 is only memory-lane's say-so.[unverified]
- Merge now, from a terminal:
magenta.sh merge pickipedia:re/foo. memory-lane is not in the path at all.[unverified]- The tool reads the branch's head itself, from GitHub (until the flip) or githouse, never from memory-lane, and shows
git logand the diffstat of what will be merged. A signature covers only what the signer was shown; if memory-lane supplied the head, a compromised memory-lane could show one diff and hand over another hash (re).[unverified] - It signs the order with
ssh-keygen -Y signin a namespace of its own,magenta-merge, so no sign-in signature (namespacemagenta-moods) can be replayed as a merge. The order carries a nonce and an expiry about 300 blocks out.[unverified] - It hands the order and signature to githouse on maybelle, over the person's own ssh to maybelle.[unverified]
- The tool reads the branch's head itself, from GitHub (until the flip) or githouse, never from memory-lane, and shows
- githouse checks, against a signers file of its own for the
magenta-mergenamespace, which ansible writes from the inventory and only the people who may merge are in (Justin's call; to start: justin). It refuses an expired order, a nonce already used (each is kept), a branch whose head has moved, a target that isn't one of GitHub's own branches, and a conflict. The signer chooses the target, and signs it with the rest;magenta.sh mergedefaults it to the work's deploy branch (re's review of maybelle-config#286).[unverified] - The merge is
git merge --no-ffof that commit into its target (productionwhere there is one, elsemain), with the message "Merge <repo>:<branch> (<commit>)" and who signed. Until the flip it is pushed to GitHub first, with the deploy key: githouse force-fetches the deploy branches from GitHub every minute, so a merge written into githouse first is overwritten (re). The fetch brings it back; the name moves torefs/merged/. GitHub marks a pull request merged when its head reaches the base, so an open one closes itself. The signed order and its outcome are written beside the repositories, and memory-lane, which mounts them read-only, tells the Mood.[unverified] - From a phone, not yet. A passkey on magenta's own page is not SSH strength: Face ID shows the site, not the order, and the page's script, served by memory-lane, sets what is signed. Enrolled by an SSH signature, the key enrolled is one nobody can check by eye and memory-lane carries it, so a compromised memory-lane could enroll its own and merge whenever it liked (re). The honest options: githouse serves a small page of its own for passkeys, which puts githouse on the web (section 3 rules that out today); or the plan says plainly that a phone merge trusts memory-lane at the moment of the press. Justin's choice; the terminal path comes first either way.[unverified]
- Not yet: requiring Jenkins to be green first.[unverified]
9. A place to discuss each piece of work (proposed, 7 Nov)
Justin, 7 Nov: each pull request, and each mergeable, gets a page to talk about it, built on Tidings' interface, which needs upgrading anyway. On it: the description, the commits, the 👍s and "merge now" (section 8), and the conversation. The description of a branch with no pull request needs a home; one candidate is a card of its own on PickiPedia, written by its author. To be designed after reading how Tidings keeps its comments.[unverified]
10. What a merge can do, and when a key is stolen (proposed, 7 Nov)
Justin, 7 Nov: merges should be a low-risk event, so their interface can be easy; until now anyone in the GitHub org could merge from any device; and if a person's key is compromised, taking away their access must not need a merge, in case merging is what was lost.[unverified]
What a merge does, by repository (checked 7 Nov in maybelle-config maybelle/jobs/):[unverified]
- pickipedia, arthel: a merge runs code.
pickipedia-buildandarthel-production-buildcheck outproductionevery 5 and 2 minutes and run the Jenkinsfile in the merged commit, on maybelle's Jenkins, with its credentials.[unverified] - maybelle-config: a merge is deploy logic. It runs as root on every server at the next redeploy, which a person starts.[unverified]
- magenta, rabbithole, pickipedia-mcp: a merge does nothing on its own. Nothing runs until a person redeploys.[unverified]
Two tiers of merging, by that.[unverified]
- Key (section 8): pickipedia, arthel, maybelle-config.
magenta.sh merge, signed with a person's SSH key, checked by githouse.[unverified] - Device: magenta, rabbithole, pickipedia-mcp. A "merge now" button on the ramp, for a person's signed-in device, from the phone. memory-lane holds a merge key of its own, principal
memory-lane, and githouse accepts it only for these repositories:githouse_mergersnames, for each signer, which repositories it may merge. A compromised memory-lane could merge one of these, and nothing would run until a person redeployed. That is no weaker than GitHub's button today.[unverified]
Brakes from anyone, accelerators only from the key. Stopping takes less trust than starting, so a less trusted place may pull a brake.[unverified]
- A ban stops merges too.
magenta.sh kick <name> --all --banalready signs them out and bars their key from Moods at once, with no merge or deploy. githouse also asks memory-lane every minute who is banned and whether AZ5 is on. It refuses any order by a banned name, and every order during AZ5, memory-lane's own included. A compromised memory-lane can only stop merges this way, never make one. If memory-lane doesn't answer, githouse merges nothing until it does.[unverified] - Then the inventory. Removing the key from hunter's inventory (servers, sign-in, merge_signers) is a merge and a redeploy, made after the brake holds, by GitHub's button or
magenta.sh merge.[unverified] - GitHub: remove them from the org in GitHub's settings. Until the flip, an org member's stolen session can still merge on GitHub; that hole closes only when githouse is the way merges happen.[unverified]
- If the stolen key is Justin's, it is root on every server, the only admin (MOOD_ADMINS is
justin) and a merger. What is left is outside the keys: Justin's GitHub account (its own password and second factor) and Hetzner's console. A second admin in MOOD_ADMINS would let someone else pull the brakes. It would also let them pull them on Justin, which root undoes. Justin's call.[unverified] - A runbook, in order, at Cryptograss:Runbooks/A key is compromised.[unverified]