CI/CD Setup
vanixiets builds on nixbot running on magnetite, lands changes through the gitea-mq merge queue, and deploys through hercules-ci-style effects.
The GitHub Actions release and docs-deploy workflows are archived under .github/deprecated/.
The rule: secrets are only held by code from main
Section titled “The rule: secrets are only held by code from main”Effects are the only CI programs that receive deploy secrets (the Cloudflare API token, the GitHub release token, and the R2 token for browser evidence).
onPush effects run only for pushes to main, and onEvent effects, including build_finished, are always evaluated from main, so the code that holds a secret has always already been reviewed and landed.
Secrets are declared per trigger, so a pull request trigger receives only the secrets it declares: the release-packages pull request trigger declares none.
An effect contains no behaviour of its own. Each effect is a data entry naming a program, and every behaviour lives in that program, which a hermetic rehearsal check runs against stubs. There is no separate dry-run implementation of an effect; pre-merge confidence comes from those rehearsals.
Gating
Section titled “Gating”nixbot.toml at the repository root sets:
attribute = "checks"effects_on_pull_requests = falsenixbot reads the effects gating keys from the default branch, never from the ref being built, so a pull request cannot grant itself effects.
With effects_on_pull_requests = false and effects_branches left at its empty default:
- a push to
mainruns the onPush effects; - no pull request runs its own onPush effects, whether it comes from a fork or from a branch in this repository;
- a push to any other branch, including a merge-queue batch branch, runs no effects.
The comments in nixbot.toml cite the nixbot functions that implement each rule.
What pull requests and merge-queue batches run
Section titled “What pull requests and merge-queue batches run”A pull request or batch build evaluates and builds checks, scoped by evalSystems in modules/nixos/nixbot.nix to checks.x86_64-linux and checks.aarch64-darwin; aarch64-linux is never evaluated.
The darwin checks, the darwin-<host> machine checks included, are best-effort (modules/checks/nixbot-best-effort-darwin.nix): magnetite dispatches them to stibnite, which builds them on battery or AC at Background QoS.
A failed darwin attribute is an ignored failure, shown on the build, excluded from its aggregate status, and retried on the next build.
nixbot cannot tell an absent Mac from a genuine darwin regression, so local just check-fast on a Mac remains the gate for darwin.
A darwin evaluation error still fails the build.
The marking changes no drvPath, so the binary cache is unaffected.
For each onPush effect gated off on that ref, nixbot builds the effect’s dependencies as a check and still posts the nixbot/effects status.
A required nixbot/effects context is therefore satisfied without running anything that reads a secret.
The required contexts are nixbot/nix-eval, nixbot/nix-build, and nixbot/effects.
Rehearsals
Section titled “Rehearsals”Each effect lists the rehearsal checks for its program among its inputs, so every pull request and batch builds them as part of the required nixbot/effects gate:
checks.<system>.deploy-docs-rehearsalruns everydeploy-docsmode (production,preview,pull-request,pull-request-closed,versions, anddeployments) against a stub wrangler that also executes each invocation with the real pinned wrangler and the identical argv:deploywith--dry-run, andpreview,preview delete,versions list, anddeployments listagainst a loopback fake Cloudflare API, so an argv wrangler would reject fails the check. It covers the superseded production run; the untrusted--payloadhardening, including symlink refusal and a payload build command that never runs; the pull request trust rule (a same-repository bot pull request and fork pull requests with a writer as actor or author preview, an untrusted fork is skipped asneutral, and a missingisForkfails);succeeded,skipped_local, failed, and missing docs attributes, a malformed event, a failed deploy, and thedocs-previewcheck-run lifecycle; a pull request found closed or superseded before the upload, which is skipped without any Cloudflare or nixbot API request, one closed during the upload, whose Preview is withdrawn, and a failed pull request state lookup; teardown of an existing Preview, of one that never existed, and a failed delete;--limittruncation of the listings; and missing secrets failing before any network call;checks.<system>.release-rehearsalrunsrelease-packages --revwith the production semantic-release plugin list against a local git fixture and a stub GitHub API, including the floating major and minor tags fromsemantic-release-major-tag, a superseded rev that main has moved past, and a diverged rev outside main’s history; it also runsrelease-packages planon fixture pull requests through the installation-token path, covering a minor, a major, and no bump, a pull request that edits the release configuration (forecast from main’s configuration, its plugin never run), a closed or superseded pull request, which is skipped without cloning, a failed pull request state lookup, a merge conflict, a head mismatch, a missing forge token, and a release PAT in the environment that is never used, and it asserts that a forecast leaves the fixture remote’s tags and refs untouched and creates no release;checks.<system>.publish-evidence-rehearsalrunspublish-evidence eventandpublish-evidence mainagainst a loopback nixbot API, a chroot store, a stub comment endpoint, and a stub S3 endpoint that verifies the temporary credentials’ JWT claims and SigV4 signature, refuses writes under a read-only credential, and refuses keys outside each credential’s prefix. It covers report selection and validation; amainupload tottl-90dwith the receipt last and create-only, and a latermainbuild with the same report that uploads nothing;mainmode’s lookup failures (a commit with no build, a missingNIXBOT_API_URL, and a missing or non-main--rev); an unaffected pull request whose reportmainalready published, which uploads and comments nothing; an affected pull request uploaded tottl-30dwith a comment and marker, its retry, and a second build with the same report; the same affected run delivered as apull_requestevent; a reused build whose comment names the head it was reused for and whose marker records it, and a reused build that returns tomain’s report and supersedes the comment; a rejected event kind such aspull_request_closed; a refused comment, a corrupted remote receipt, and a failed upload; a pull request that returns tomain’s report and supersedes its comment, also without an API token and with a malformed marker; credential confinement, including that no pull request run writes intottl-90d; a build without a pull request; a failing baseline probe; missing R2 secrets; and the parent secret’s absence from every request.
The docs effect lists deploy-docs-rehearsal, release-packages lists release-rehearsal, and browser-evidence lists publish-evidence-rehearsal.
The rehearsals run the real programs without a token or network access, so a change that breaks the release, deploy, preview, or teardown path fails before it reaches main.
checks.<system>.effects-interpreter covers the one piece of code between nixbot and a program: the script the interpreter generates for each effect trigger.
Docs previews
Section titled “Docs previews”The docs effect’s pull request trigger deploys each pull request’s docs as a Cloudflare Preview, and its pull-request-closed trigger deletes that Preview; see docs below.
The preview reports as the docs-preview check run, which is not a required context and never blocks a merge.
Release plan
Section titled “Release plan”The release-packages effect’s pull request trigger forecasts, per package, what semantic-release would publish if the pull request landed, and reports it as the release-plan check run on the head commit; see release-packages below.
It is not a required context and never blocks a merge.
Fork pull requests
Section titled “Fork pull requests”nixbot’s prApproval (configured in modules/nixos/nixbot.nix) is enabled and holds pull requests from authors outside the OWNER, MEMBER, and COLLABORATOR associations until a maintainer approves CI for them.
Pull requests whose head branch lives in this repository are always trusted, bots included, since pushing that branch already required write access.
A held pull request is approved with its check run’s button.
Landing
Section titled “Landing”gitea-mq tests batches of queued pull requests and lands a batch by fast-forwarding main to the exact tested commit.
The resulting push to main reuses the batch’s build, and nixbot then runs the effects for that commit.
A batch forms once the queue has settled: 15 seconds with no new enqueue (GITEA_MQ_BATCH_SETTLE, a local patch in modules/nixos/gitea-mq-batch-settle.patch), at once when 20 pull requests are queued (batchMax), and at most 2 minutes after the oldest queued pull request (GITEA_MQ_BATCH_SETTLE_MAX).
So pull requests enqueued together, such as one gh-queue-open-prs run, share one build instead of the first one forming a batch of its own.
A pull request that does not merge cleanly onto the batch is ejected while the batch is built, and gitea-mq removes its auto-merge; it re-enters the queue only when enqueued again.
Batches can land in quick succession, so an effect may start after main has already moved past the commit it was built for.
The production deploy and the release handle this the same way: a lock orders runs across builds, and the program exits 0 without acting when its --rev is no longer main’s head, leaving the work to the run for the newer commit.
The browser-evidence effect does not skip a superseded commit: evidence for each landed commit is kept under its own build’s keys, and its lock only serialises runs.
Effects
Section titled “Effects”Effects are data.
modules/effects/vanixiets/effects.nix declares them as entries of the flake option vanixiets.effects.<name>, each naming its program, its rehearsals, and its triggers.
An entry has at least one of the triggers main, pullRequest, pullRequestClosed, and buildFinished, and each trigger sets the program’s literal arguments, the secrets it reads, its lock, and whether it receives nixbot’s forge token.
One interpreter, modules/effects/vanixiets/registry.nix, turns each trigger into a nixbot effect: main into herculesCI.onPush.default.outputs.effects.<name>, pullRequest into herculesCI.onEvent.pull_request.<name>, pullRequestClosed into herculesCI.onEvent.pull_request_closed.<name>, and buildFinished into herculesCI.onEvent.build_finished.<name>.
A buildFinished trigger also carries nixbot’s delivery conditions (when), such as the build statuses it is delivered for; these select builds rather than authorize anyone, and the trust gate for pull request builds is nixbot’s CI approval (prApproval in modules/nixos/nixbot.nix), which holds a pull request from outside the repository until a maintainer approves it.
The script it generates for each trigger does three things, in order:
- for
maintriggers, runs the fail-closed guard (mainOnlyGuardfrommodules/lib/effect-run-context.nix), which asks nixbot for an identity token and refuses to run unless the token says the event is a push torefs/heads/main: any other run is skipped with exit 0 before a secret is read, and a missing or unreadable token fails the effect; - exports each secret the trigger declares, failing with a specific error on the first one that is absent, null, or empty;
- execs the program with the trigger’s arguments, followed by
--rev <commit>formaintriggers.
checks.<system>.effects-interpreter renders synthetic entries with all three trigger kinds through the same function and runs the generated scripts against stubs, asserting the guard’s skip on a non-main run, the exported secrets, that a trigger never receives another trigger’s secrets, the failure on a missing secret, the forge token of each trigger, and the exact argv the program receives, including --rev.
Programs that report a GitHub check run share one program for it, github-check-run (modules/apps/ci/github-check-run.{nix,sh}, also apps.github-check-run).
github-check-run create --repo <owner/name> --name <check> --head-sha <sha> creates an in_progress check run and prints its id; github-check-run complete --repo <owner/name> --id <id> --conclusion <success|neutral|failure> --title <title> --summary <summary> [--details-url <url>] completes it.
It authenticates with GITHUB_FORGE_TOKEN and fails on any non-2xx response; deploy-docs pull-request and release-packages plan both call it, and both complete their check run from an exit trap that never masks the program’s exit status.
nixbot does not guarantee the order in which it delivers pull request events, and each event’s payload describes the pull request as it was when the event was delivered, not when the effect runs.
A fast merge can deliver pull_request_closed before the pull_request event for the pull request’s last green head, so both pull request modes check the pull request’s live state before acting, with the shared github-pull-request program (modules/apps/ci/github-pull-request.{nix,sh}, also apps.github-pull-request).
github-pull-request state --repo <owner/name> --number <n> --head-sha <sha> reads the pull request from the GitHub API with GITHUB_FORGE_TOKEN and prints one word: current when it is open and its head is <sha>, closed when it is closed or merged, and superseded when it is open at a different head; an API or argument failure exits 1.
A mode that finds its pull request anything but current completes its check run as neutral and exits 0 without acting.
The docs effect runs the deploy-docs program on all three triggers:
| Trigger | nixbot effect | Program | Lock |
|---|---|---|---|
main | herculesCI.onPush.default.outputs.effects.docs | deploy-docs production --rev <commit> | deploy-docs |
pullRequest | herculesCI.onEvent.pull_request.docs | deploy-docs pull-request | docs-preview-{pr} |
pullRequestClosed | herculesCI.onEvent.pull_request_closed.docs | deploy-docs pull-request-closed | docs-preview-{pr} |
deploy-docs production deploys the nix-built vanixiets-docs payload with wrangler deploy to 100% of production traffic.
It skips with exit 0, logging DEPLOY-DOCS-ACTION: superseded, when main’s head is no longer its commit.
The two pull request triggers are onEvent effects: nixbot evaluates them from main, never from the pull request, so pull-request code never holds their secrets.
On a pull request event, deploy-docs pull-request:
- creates an
in_progressGitHub check run nameddocs-previewon the pull request’s head commit withgithub-check-run, using nixbot’s forge token; - asks
github-pull-request statewhether the pull request is still open at the event’s head commit; when it isclosedorsuperseded, it completes the check run asneutral(“Docs preview skipped”, “pull request #is ; no preview”), logs DEPLOY-DOCS-PREVIEW: skipped (<state>), and exits 0 without fetching or deploying anything, and a failed state lookup fails the run; - decides whether to trust the pull request from the event’s
pullRequest.isFork,actor.permission, andpullRequest.author.permission: a pull request whose head branch is in this repository is trusted, bots included, since pushing that branch already required write access; a fork is trusted only when the actor or the author haswriteoradmin, so a maintainer previews a fork by adding any label to it; an untrusted fork completes the check run asneutral(“Docs preview skipped”, naming the reason and that remedy) and exits 0 without fetching or deploying anything, and an event without a booleanisForkfails; - fetches the pull request’s nixbot build from nixbot’s API, without evaluating any pull-request code, and requires the build to have succeeded and its
checks.x86_64-linux.package-vanixiets-docsattribute to besucceededorskipped_local(nixbot reports a cached output asskipped_local); - deploys that payload with
wrangler preview --name pr-<number> --worker-name infra-docs --ignore-base-configas the Cloudflare Previewpr-<number>of theinfra-docsWorker, from a synthesized assets-only wrangler config with an emptypreviewsblock,upload_source_maps: false, and no build command, bindings, or routes, so neither the payload nor the dashboard’s Preview base config adds anything to it; - checks the pull request’s state again: if it closed during the upload, it deletes the Preview
pr-<number>exactly aspull-request-closeddoes, logsDEPLOY-DOCS-PREVIEW: withdrawn (closed during upload), and completes the check run asneutral, failing if the delete fails; otherwise it completes the check run assuccesswith the Preview URL,https://pr-<number>-infra-docs.sciexp.workers.dev, as its details link, keeping the Preview when a newer head has superseded this one, since that head’s run updates the same Preview.
Any failure completes the check run as failure with the error in its summary.
The program applies the trust rule itself because bots report no permission even when their head branch is in this repository, and a nixbot condition cannot express “same repository or a writer”. Each run for a pull request updates the same Preview, whose URL always serves its latest deployment.
When the pull request is closed or merged, deploy-docs pull-request-closed deletes the Preview with wrangler preview delete --name pr-<number> --skip-confirmation --worker-name infra-docs and logs DEPLOY-DOCS-PREVIEW: deleted (pr-<number>).
A pull request that never got a Preview, such as a skipped fork or a failed build, logs DEPLOY-DOCS-PREVIEW: absent (pr-<number>) and exits 0.
Both pull request triggers share lock = "docs-preview-{pr}", so teardown waits for an upload still in flight for the same pull request.
The lock serialises the two triggers but does not order them: when teardown runs first, the later pull-request run finds the pull request closed and skips, and when the pull request closes while an upload is in flight, that run withdraws the Preview it just uploaded.
Cloudflare applies these properties to Previews:
- a Worker holds at most 100 Previews on the Free plan and 500 on paid plans, and a Preview holds at most 100 deployments; at either limit Cloudflare deletes the least recently deployed Preview or the oldest deployment to make room;
- Preview URLs are public, and their
workers.devURLs sendX-Robots-Tag: noindex; - deletion can lag: a Preview’s URL has been observed to keep serving for hours after
wrangler preview deletereported success (cloudflare/developer-platform#73).
The previews are informational: a failed, skipped, or undeleted preview never blocks a merge.
deploy-docs owns its interface: production --rev <sha>, preview --rev <sha> --name <name> [--payload <dir>] (both with an optional --deployed-by <name>), pull-request, pull-request-closed, versions [--limit <n>], deployments [--limit <n>], and tail.
It sanitizes a Preview name to at most 40 lowercase letters, digits, and hyphens, reads only CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID from the environment (plus nixbot’s event variables and forge token in the pull request modes), and fails before any network call when a secret is missing; deploy-docs --help prints the usage.
release-packages
Section titled “release-packages”The release-packages effect runs the release-packages program on two triggers:
| Trigger | nixbot effect | Program | Secrets | Lock |
|---|---|---|---|---|
main | herculesCI.onPush.default.outputs.effects.release-packages | release-packages --rev <commit> | GITHUB_TOKEN | release-packages |
pullRequest | herculesCI.onEvent.pull_request.release-packages | release-packages plan | none (forge token only) | release-plan-{pr} |
release-packages --rev <commit> clones the repository, checks out the commit as main, discovers packages with list-packages-json, and runs the release program for each, which runs semantic-release with the production plugins.
It skips with exit 0 and logs RELEASE-PACKAGES-ACTION: superseded when main has moved past its commit, and refuses a commit outside main’s history.
list-packages-json lists only packages whose package.json declares a non-null release key, so a workspace package opts in to releases explicitly; a package.json that fails to parse fails the run, naming the file.
release-packages plan runs on every pull request once its head has built green, forks included once CI is approved for them, and reports the release-plan check run on the head commit:
- it creates the check run with
github-check-run, using nixbot’s forge token; - it asks
github-pull-request statewhether the pull request is still open at the event’s head commit; when it isclosedorsuperseded, it completes the check run asneutral(“Release plan skipped”, “pull request #is ; no forecast”), logs RELEASE-PLAN: skipped (<state>), and exits 0 without cloning, and a failed state lookup fails the run; - it clones the public repository without a token, fetches
refs/pull/<number>/head, and requires it to equal the head commit in nixbot’s event; - it simulates the merge into
mainwithgit merge-tree, failing on a conflict, and checks out the merge commit asmainwith the index and working tree reset tomain’s tree, so semantic-release analyses the pull request’s commits while its configuration and plugins come frommain’s files; - it runs the
releaseprogram for each package with semantic-release’s--dry-run, with nixbot’s installation token asGITHUB_TOKEN, and with git’s URLs for the repository rewritten to a local bare clone, so semantic-release’s push-permission check and every other git network operation reach that clone and nothing is ever pushed; - it logs
RELEASE-PLAN: <package> <last|none> -> <next|no release>per package and completes the check run assuccesswith a package, last, next, and bump table, or asfailurewith the reason.
semantic-release logs Published release <version> on <channel> channel even under --dry-run (semantic-release 25.0.9 index.js, line 221), but the forecast publishes nothing; the RELEASE-PLAN lines and the check run are its output.
The release PAT is never given to this trigger and plan mode never reads it. The forecast is informational and never blocks a merge. See Semantic Release Preview for reading the forecast and previewing a release locally.
browser-evidence
Section titled “browser-evidence”The browser-evidence effect runs the publish-evidence program, which publishes the docs browser report nixbot already built (checks.x86_64-linux.package-vanixiets-docs-test-e2e-report) as screenshots and JSON metadata:
| Trigger | nixbot effect | Program | Lock |
|---|---|---|---|
main | herculesCI.onPush.default.outputs.effects.browser-evidence | publish-evidence main --upload --rev <commit> | browser-evidence |
pullRequest | herculesCI.onEvent.pull_request.browser-evidence | publish-evidence event --upload | browser-evidence |
buildFinished | herculesCI.onEvent.build_finished.browser-evidence | publish-evidence event --upload | browser-evidence |
All three triggers share the browser-evidence lock, so no two runs overlap on a pull request’s comment and marker.
A per-pull-request {pr} lock is not usable here: nixbot also delivers build_finished for builds without a pull request, and it skips an event whose lock names {pr} when the event has no pull request.
nixbot delivers pull_request after every pull request build that settles succeeded, whether it built afresh or reused an earlier build of an identical tree, and it delivers build_finished only for a newly finished build; the buildFinished trigger is restricted to failed builds (when.status = [ "failed" ]), since a failing suite still leaves a report.
Each settled pull request build therefore gets exactly one run:
| Pull request build | Succeeded | Failed |
|---|---|---|
| fresh | pullRequest | buildFinished |
| reused | pullRequest | none |
A reused failed build gets no run, so its head has no comment of its own; the earlier build’s comment remains.
For a reused build the comment names the build’s revision and adds (reused for head <head>, same tree).
A label event also delivers pull_request for the pull request’s latest build, and that repeat run finds its upload and comment already in place.
Neither onEvent trigger sets when.permission, which nixbot satisfies with the actor’s or pull request author’s permission: bots such as Renovate report none, so the condition skipped their pull requests.
The trust gate is nixbot’s CI approval instead, and the program never evaluates or executes pull request code.
A landing on main reuses the merge-queue build and nixbot sends no build_finished for a reused build, so the main trigger looks the landed commit up in nixbot’s builds API instead.
Neither mode evaluates or builds anything: both realise the recorded report output, validate it with the program’s own validator, and upload only run.json, completion.json, the referenced PNG screenshots, and a receipt.json (schema version 3) that identifies the report by attribute and output path and names no build.
Objects go to the R2 bucket sciexp under projects/vanixiets/browser-evidence/<tier>/v1/<obs>/, where <obs> is derived from the report’s attribute and Nix output path only; every build with the same report writes the same keys, each tier receives a report at most once, and an identical existing receipt logs PUBLISH-EVIDENCE: unchanged.
main and failed builds without a pull request use the ttl-90d tier; bucket lifecycle rules in modules/terranix/cloudflare.nix delete each tier’s objects by age.
A pull request build first checks whether main has already published this exact report, by probing for its receipt under ttl-90d.
If so, the pull request is unaffected: the program uploads nothing, posts no comment, and logs PUBLISH-EVIDENCE: unaffected (report <obs> already published from main).
Otherwise it uploads to ttl-30d and posts one comment through nixbot’s pr-comment API with the verdict, the build, links to the screenshots and receipt, the retention, and a note that no identical report has been published from main in the last 90 days; it edits that same comment on later builds.
A report that differs from every report published from main in the last 90 days is treated as affected, for example when main’s evidence has expired or its publish run failed.
The report’s output path is input-addressed, so a dependency bump in the site’s closure is affected even when its screenshots are pixel-identical to main’s.
vanixiets-docs-deps is built from the whole workspace bun.lock, so a dependency change in another workspace package, such as @vanixiets/evidence-worker, currently counts as affecting the docs report too; per-application dependency derivations would remove that.
Another application would add its own report derivation, one per (project, kind), judged independently; the program’s single report attribute is the point to extend.
Because pr-comment can only upsert, the program records its comment in a marker object under ttl-30d/pr/; a later unaffected build that finds a current marker replaces the comment with a superseded note and logs PUBLISH-EVIDENCE: superseded #<number>.
When two builds of one pull request finish out of order, the one that finishes last decides the comment.
The program never uses the R2 token for requests: each run signs temporary credentials valid for 15 minutes, read-write on its own tier’s prefix and, for a pull request, read-only on projects/vanixiets/browser-evidence/ttl-90d/, then unsets the token, so a pull request run cannot write into main’s tier.
The Worker sciexp-evidence (packages/evidence-worker/, deployed with wrangler) serves the evidence read-only at https://evidence.vanixiets.net/vanixiets/browser-evidence/<tier>/v1/<obs>/<file>, the object key after projects/: GET and HEAD only, .png and .json only, no directory listing, with nosniff and a sandboxing content security policy.
vanixiets.net is dedicated to untrusted CI content and hosts no authentication, sessions, or cookies.
A failed upload or comment fails the effect run.
nixbot runs event effects from the default branch and posts no forge status for them (nixbot/nixbot/event_effects.py), so a pull request that changes this effect or its program is first exercised after it lands, and a failed pullRequest or buildFinished run never blocks a merge; a failed main run fails nixbot/effects on the main push.
Posting the comment requires nixbot’s GitHub App to have the repository permission Pull requests: Read and write.
nixbot caches installation tokens for 48 minutes (nixbot/nixbot/forge/github.py:153-154), so a permission change on the App takes effect once the cached token expires or after nixbot restarts.
See Docs browser evidence for reading the evidence.
Secrets
Section titled “Secrets”Effect secrets are declared once, in flake.lib.vanixietsEffectSecrets (modules/effects/vanixiets/secrets.nix).
The vanixiets-effects-secrets clan vars generator’s prompts and the secrets JSON it composes are generated from that schema; the generator runs on magnetite and nixbot receives the result as a systemd credential.
Each trigger’s secrets is typed as an enum of the schema’s names, and the interpreter derives each trigger’s secretsMap from it, so a trigger can read exactly the secrets it declares and no other trigger’s.
checks.x86_64-linux.nixbot-wiring asserts that the union of every trigger’s secrets across all entries equals the schema, so no secret is prompted for without being used and none is used without being prompted for.
| Secret | Used by |
|---|---|
CLOUDFLARE_API_TOKEN | docs: main, pullRequest, and pullRequestClosed |
CLOUDFLARE_ACCOUNT_ID | docs: main, pullRequest, and pullRequestClosed; browser-evidence: main, pullRequest, and buildFinished |
GITHUB_TOKEN (fine-grained PAT, read and write) | release-packages: main only |
R2_EVIDENCE_ACCESS_KEY_ID, R2_EVIDENCE_SECRET_ACCESS_KEY (R2 token, Object Read & Write on sciexp only) | browser-evidence: main, pullRequest, and buildFinished |
The pullRequest triggers of docs and release-packages also set forgeToken = true, which gives them nixbot’s per-run GitHub App installation token as GITHUB_FORGE_TOKEN for their docs-preview and release-plan check runs; that token is not an entry of the schema.
The browser-evidence program also reads NIXBOT_API_URL and NIXBOT_API_TOKEN, which nixbot sets for each effect run, to query its build API and post the pull request comment; neither is an entry of the schema.
To rotate a value, regenerate the generator’s prompts with clan vars generate --regenerate and redeploy magnetite.
Running the same programs locally
Section titled “Running the same programs locally”Local runs use secrets/shared.yaml through sops exec-env for Cloudflare credentials, and every docs recipe runs deploy-docs rather than wrangler directly, so local runs share the wrangler invocation the rehearsal checks:
# Deploy HEAD as the Cloudflare Preview named after the current branch (or: just docs-deploy-preview <name>)just docs-deploy-preview
# Deploy HEAD to production (normally done by the docs effect);# exits 0 without deploying unless HEAD is main's head on GitHubjust docs-deploy-production
# Inspect Cloudflare state (newest first, default limit 10)just docs-deploymentsjust docs-versionsjust docs-tail
# Preview a release with semantic-release's own dry run (needs GITHUB_TOKEN)just release-package docs trueBuild what CI builds:
nix build .#checks.x86_64-linux.some-checkjust check-fastjust checkReferences
Section titled “References”- nixbot: https://github.com/Mic92/nixbot
- Cloudflare Workers: https://developers.cloudflare.com/workers/
- Wrangler CLI: https://developers.cloudflare.com/workers/wrangler/
- semantic-release: https://semantic-release.gitbook.io/