Docs
Flake interface
What a repo's flake tells gt: its description (shown in the repo list and under the repo's name), and the outputs meta and ci:
{
description = "…";
outputs = { self, nixpkgs, ... }: {
meta = { … };
ci = { ref, rev, ... }: { checks = …; artifacts = …; caches = …; effects = …; };
};
}
meta
Read from HEAD's flake only. Each field stands alone: one that fails to evaluate isn't shown, and the others still are.
meta.icon
- Type
- null or absolute path
- Default
null- Example
./icon.svg
The repo's icon: one PNG, WebP or SVG file, by its name's extension; anything else is rejected.
meta.public
- Type
- null or absolute path
- Default
null- Example
./public
For metadata-only repos: a directory of files browsable in place of the code by
everyone who can't read it (its README.md is the landing page).
Built only for those repos.
ci
Read for every pushed ref, from that commit's flake.
ci
- Type
- function { ref, rev, ... } → attribute set
- Default
_: { }- Example
{ ref, rev, ... }: { checks = self.checks.x86_64-linux; effects = lib.optionalAttrs (ref == "refs/heads/main") { … }; }
What CI does for a ref: a function of the ref (its full name, e.g.
refs/heads/main) and the commit (rev), evaluated for every
pushed ref. Read from the commit's own flake.
ci.artifacts
- Type
- attribute set of (submodule)
- Default
{ }
Outputs for downloading (images, archives, firmware, reports): a
single file, or a directory whose files are listed and downloaded
one by one. One that references other store paths is rejected,
unless selfContained is off.
ci.caches
- Type
- attribute set of (submodule)
- Default
{ }
Outputs for Nix: their closures are served by the binary cache.
ci.checks
- Type
- attribute set of package
- Default
{ }- Example
self.checks.x86_64-linux
Derivations (for any system) to build: they succeed or fail, nothing more. Anything but a derivation is rejected.
ci.effects
- Type
- attribute set of (submodule)
- Default
{ }
Impure jobs: deployments, updates, notifications.
ci.secretsFile
- Type
- null or absolute path
- Default
null- Example
./secrets.yaml
One sops-encrypted file for all of the CI, encrypted to the repo's
age key (gtd keys repo-recipient <repo> on the host). Effects
receive the keys they list in secrets.
ci.artifacts
Files to download, by name.
ci.artifacts.<name>.label
- Type
- null or string
- Default
null- Example
"Firmware (UF2)"
Shown instead of the attribute name.
ci.artifacts.<name>.path
- Type
- package
The store path (a derivation's output) to publish.
ci.artifacts.<name>.public
- Type
- boolean
- Default
false
Offered to everyone who can see the repo's CI, not only readers. No effect on private repos.
ci.artifacts.<name>.selfContained
- Type
- boolean
- Default
true
Whether it must not reference other store paths (anything else is
rejected): what a published script or report should be. Off, it
may (e.g. a symlinkJoin), and the files its symlinks lead to in
its closure are listed and served with it.
ci.caches
Store paths for Nix, by name.
ci.caches.<name>.buildDeps
- Type
- boolean
- Default
false
Also serve what building it built here: the outputs of every
derivation in its build closure that this host built itself
(toolchains, generators, …), not those it downloaded. Sources and
.drv files are never served.
ci.caches.<name>.label
- Type
- null or string
- Default
null- Example
"Firmware (UF2)"
Shown instead of the attribute name.
ci.caches.<name>.path
- Type
- package
The store path (a derivation's output) to publish.
ci.caches.<name>.public
- Type
- boolean
- Default
false
Offered to everyone who can see the repo's CI, not only readers. No effect on private repos.
ci.effects
Effects, by name.
ci.effects.<name>.approval
- Type
- null or (submodule)
- Default
null
Instead of drv: an approval step, which a writer of the repo
approves or denies. It passes the outputs of its needs on
unchanged, and must have needs.
ci.effects.<name>.approval.expires
- Type
- positive integer, meaning >0, or string
- Default
"7d"- Example
"2h"
How long it waits for a decision before it expires (and
what needs it is skipped): seconds, or a number with s,
m, h, d or w.
ci.effects.<name>.approval.message
- Type
- null or string
- Default
null- Example
"Apply these infrastructure changes?"
What the approver is asked.
ci.effects.<name>.description
- Type
- null or string
- Default
null- Example
"Production deployment"
Shown next to its name.
ci.effects.<name>.drv
- Type
- null or package
- Default
null- Example
pkgs.writeShellScript "deploy" "…"
What runs: a derivation whose executable (its meta.mainProgram,
or the file itself) is run in a sandbox, in a checkout of the
commit, with:
/run/gt/secrets/<key path>: the listedsecrets;/run/gt/trigger.json: what started it (push, schedule or webhook), the ref, the commit and the chain that led here;/run/gt/webhook/: a webhook call's body and headers (untrusted input);/run/gt/in/<effect>/: the outputs of the effects itneeds.
It may write:
/run/gt/out/: outputs for the effects that need it (summary.mdis shown on the approvals that follow, each under the name of the effect that wrote it);/run/gt/result.json: a small structured result;/run/gt/skip: marks the run as skipped (its content: why).
Either drv or approval.
ci.effects.<name>.fastForward
- Type
- boolean
- Default
false
It gets a clone with history, the branch checked out, and may
commit: after a successful run, if its branch is a strict
descendant of where it started and the branch hasn't moved since,
the branch is advanced to it (the repo must allow it too:
ci.allowFastForward on the host). That counts as a push, so CI
runs on it. Only its own branch, never tags.
ci.effects.<name>.link
- Type
- null or string
- Default
null- Example
"https://example.org"
What it deploys, linked from it.
ci.effects.<name>.needs
- Type
- list of string
- Default
[ ]- Example
[ "plan" ]
Effects it waits for, in the same run on the same commit, and
whose outputs it receives (/run/gt/in/<effect>/). If one fails,
this one is blocked (doesn't run); if one is skipped (or an
approval is denied or expires), so is this one.
ci.effects.<name>.on
- Type
- null or (submodule)
- Default
{ push = true; }
When it runs. Unset: on push; set, only on what it lists. Effects
with needs run with what they need instead, and have no on.
ci.effects.<name>.on.push
- Type
- boolean
- Default
false
Runs when a ref it is declared on is updated, on the new commit (branches and tags).
ci.effects.<name>.on.schedule
- Type
- list of (submodule)
- Default
[ ]
Runs on the branch's tip at these times. Branches only; missed times aren't made up for.
ci.effects.<name>.on.schedule.*.cron
- Type
- string
- Example
"0 3 * * 1"
Five cron fields (minute resolution).
ci.effects.<name>.on.schedule.*.tz
- Type
- string
- Default
"UTC"- Example
"Europe/Berlin"
An IANA time zone.
ci.effects.<name>.on.webhook
- Type
- boolean
- Default
false
Runs when POST <web URL>/<repo>/-/hooks/<effect>/<branch>
is called with a token whose hash the host has for it
(repos.<repo>.webhookTokens.<effect>). Branches only.
ci.effects.<name>.secrets
- Type
- list of string
- Default
[ ]- Example
[ "deploy/ssh_key" ]
Keys of secretsFile it receives, as /-separated paths into the
file's nested keys.
ci.effects.<name>.waitForFastForwards
- Type
- boolean
- Default
false
On a push, wait until none of the branch's fast-forwarding effects is queued or running: if they changed nothing, run on the pushed commit; if they fast-forwarded, run once on the new tip instead.
Host configuration
NixOS module options, under services.gt (the flake's nixosModules.default).
General
cacheHostName
- Type
- string
- Default
"cache.${config.services.gt.hostName}"
Host name of the Nix binary cache.
caddy.enable
- Type
- boolean
- Default
true
Configure Caddy as the reverse proxy.
contentHostName
- Type
- string
- Default
"content.${config.services.gt.hostName}"
Separate host name for untrusted content (raw files, artifacts).
defaultBranch
- Type
- string
- Default
"main"
Branch HEAD points to in newly created repos.
docs
- Type
- null or package
- Default
null
The option docs served at /-/docs (the flake's docs package). Unset: no docs.
enable
- Type
- boolean
- Default
false- Example
true
Whether to enable gt, a small Nix-native code host.
frontend
- Type
- package
The bundled frontend assets.
hostKey
- Type
- absolute path
- Default
"/etc/ssh/ssh_host_ed25519_key"
SSH host key (ed25519): root of trust for per-repo age keys.
hostName
- Type
- string
- Example
"gt.example.org"
Host name of the web UI.
logo
- Type
- null or absolute path
- Default
null- Example
./logo.svg
The site's logo in the page top (SVG, PNG or WebP). Unset: a plain placeholder.
package
- Type
- package
The gt package (both binaries).
publicPort
- Type
- null or 16 bit unsigned integer; between 0 and 65535 (both inclusive)
- Default
null- Example
8080
Port in the public URLs, when clients reach Caddy on a non-default port (e.g. through a port forward).
sshUrl
- Type
- string
- Default
"ssh://git@${config.services.gt.hostName}"
Base of SSH clone URLs shown in the UI, as git@host:repo.git when it has no port other than 22.
tls
- Type
- boolean
- Default
true
Serve over HTTPS (Caddy's automatic TLS). Disable for *.localhost.
valkey.enable
- Type
- boolean
- Default
false- Example
true
Whether to enable a local Valkey page cache for public pages.
effects
effects.ipAddressAllow
- Type
- list of string
- Default
[ ]
systemd IPAddressAllow= (takes precedence), e.g. your DNS resolver.
effects.ipAddressDeny
- Type
- list of string
- Default
[ "localhost" "link-local" "multicast" "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16" ]
systemd IPAddressDeny= for effect units.
effects.runtimeMaxSeconds
- Type
- signed integer
- Default
21600
How long an effect may run before it's stopped (and fails).
eval
eval.ipAddressAllow
- Type
- list of string
- Default
[ "127.0.0.53" "127.0.0.54" ]
systemd IPAddressAllow= (takes precedence): systemd-resolved's stub resolver by default; add another local resolver here.
eval.ipAddressDeny
- Type
- list of string
- Default
[ "localhost" "link-local" "multicast" ]
systemd IPAddressDeny= for the scheduler, where flakes are evaluated: their fetches get the internet, not the host's own services.
images
images.ipAddressAllow
- Type
- list of string
- Default
[ "127.0.0.53" "127.0.0.54" ]
systemd IPAddressAllow= (takes precedence) for the image proxy: its DNS resolver (systemd-resolved's stub by default).
images.ipAddressDeny
- Type
- list of string
- Default
[ "localhost" "link-local" "multicast" "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16" "100.64.0.0/10" "fc00::/7" ]
systemd IPAddressDeny= for the image proxy, which fetches documents' external images for viewers: the internet, never the host or its networks (it refuses those addresses itself too).
limits
Hard limits, each its default unless set. None can be turned off (no value means "no limit"): to lift one, set it high.
limits.buildWorkers
- Type
- positive integer, meaning >0
- Default
8
Builds at once.
limits.chainMax
- Type
- positive integer, meaning >0
- Default
4
Links in a chain of fast-forwards (the push, then each effect whose fast-forward led to the next): beyond it, push effects are skipped, so effects can't keep each other going.
limits.effectOutputBytes
- Type
- positive integer, meaning >0
- Default
33554432
Bytes an effect may write to /run/gt/out/; more fails it.
limits.effectOutputFiles
- Type
- positive integer, meaning >0
- Default
1000
Files an effect may write to /run/gt/out/; more fails it.
limits.effectWorkers
- Type
- positive integer, meaning >0
- Default
4
Effects running at once.
limits.evalCpuSeconds
- Type
- positive integer, meaning >0
- Default
300
CPU time one evaluation may use; more fails it (and it's stopped after twice that, plus 30 seconds, of wall time).
limits.evalMemoryBytes
- Type
- positive integer, meaning >0
- Default
4294967296
Memory one evaluation may use; more fails it.
limits.evalWorkers
- Type
- positive integer, meaning >0
- Default
4
Evaluations at once.
limits.heavyTasks
- Type
- positive integer, meaning >0
- Default
2
Heavy work at once on the machine, of all its processes: commit caches, search indexes, histories put in order ahead of visitors. One slot is kept for the short kind, so a search index taking hours (nixpkgs') doesn't hold up the rest.
limits.searchResults
- Type
- positive integer, meaning >0
- Default
100
Results of one search, per kind (code, commits, diffs).
limits.webhookBodyBytes
- Type
- positive integer, meaning >0
- Default
1048576
Bytes of a webhook call's body; a larger call is refused.
repos
repos
- Type
- attribute set of (submodule)
- Default
{ }- Example
{ "chris/*" = { accessFrom = [ "chris/*" ]; read = [ "alice" ]; write = [ "chris" ]; }; "chris/site" = { read = [ "web-server" ]; visibility = "public"; }; }
Repositories, by name, or by pattern for many: * stands for one
or more characters within a path segment. Every entry that applies
to a repo counts: readers, writers and accessFrom add up; other
settings come from the most specific one setting them (the repo's
own, then the pattern with the most fixed characters). Writers of a
pattern create repos by pushing to a new matching name; a repo
shows up once it has commits.
repos.<name>.accessFrom
- Type
- list of string
- Default
[ ]- Example
[ "chris/*" ]
Repos (names or patterns) whose CI may read these repos, e.g. as
private flake inputs. "*".accessFrom = [ "*" ]: every repo's CI
may read every repo.
repos.<name>.ci.allowFastForward
- Type
- null or boolean
- Default
null
Allow effects with fastForward = true to advance branches.
repos.<name>.ci.enable
- Type
- null or boolean
- Default
null
Evaluate the flake's ci and run builds and effects. Unset: as a less specific pattern says, else off.
repos.<name>.head
- Type
- null or string
- Default
null- Example
"master"
The branch HEAD points to (master, or refs/heads/master): the
default branch clones check out, and what the overview, log and
CI follow. Set when the branch exists (as the config changes, and
on pushes). Unset: as a less specific entry says, else where it
points (a repo created by a push points to a branch it pushed).
repos.<name>.mirrors.pull
- Type
- attribute set of (submodule)
- Default
{ }
Remotes this repo is pulled from, by name: like pushes, their changes run CI and go on to the push mirrors. A repo that only pulls needs nobody to push; it's created on its first pull. Only on a repo's own entry, not on patterns.
repos.<name>.mirrors.pull.<name>.branch
- Type
- null or string
- Default
null- Example
"main"
The one upstream branch to pull. Unset: all branches and tags.
repos.<name>.mirrors.pull.<name>.force
- Type
- boolean
- Default
false
Also take updates that aren't fast-forwards (and moved tags), replacing what's here.
repos.<name>.mirrors.pull.<name>.interval
- Type
- null or (positive integer, meaning >0)
- Default
900
Seconds between pulls. Null: only when the webhook is called (and once when gt starts).
repos.<name>.mirrors.pull.<name>.localBranch
- Type
- null or string
- Default
null- Example
"upstream"
With branch: the local branch it updates. Unset: the same name.
repos.<name>.mirrors.pull.<name>.prune
- Type
- boolean
- Default
false
With all branches: delete branches and tags that aren't upstream (pushed ones too), keeping the repo an exact copy.
repos.<name>.mirrors.pull.<name>.url
- Type
- string
- Example
"git@github.com:chris/kb.git"
Where to pull from. Over SSH, with a key of its own (shown to readers on the overview: add it as a deploy key there).
repos.<name>.mirrors.pull.<name>.web
- Type
- null or string
- Default
null- Example
"https://github.com/chris/kb"
Where people see it: linked from the overview, for everyone.
repos.<name>.mirrors.pull.<name>.webhookTokens
- Type
- attribute set of string
- Default
{ }- Example
{ github = "sha256:…"; }
Token name -> sha256:<hex> (see gtd keys token). A POST to
<web URL>/<repo>/-/pull/<name> with one of them (as a
Bearer token or ?token=) pulls right away.
repos.<name>.mirrors.push
- Type
- attribute set of (submodule)
- Default
{ }
Remotes every change is pushed on to (branches and tags, exactly), by name. Each gets an SSH key of its own (shown with its pushes to readers of the repo: add it as a deploy key there); the private half stays on the host, encrypted to the host key.
repos.<name>.mirrors.push.<name>.url
- Type
- string
- Example
"git@github.com:chris/kb.git"
Where to push.
repos.<name>.mirrors.push.<name>.web
- Type
- null or string
- Default
null- Example
"https://github.com/chris/kb"
Where people see it: linked from the overview, for everyone.
repos.<name>.privateStore.gc.rootAge
- Type
- null or (positive integer, meaning >0)
- Default
null- Example
2592000
Seconds what's published (artifacts, caches, meta) is kept
after it was last published; publishing it again, by a later
commit building the same thing, starts over. Unset: as a less
specific entry says, else for good. Ignored for the host's store.
repos.<name>.privateStore.gc.schedule
- Type
- null or (submodule)
- Default
null- Example
{ cron = "0 4 * * 0"; }
When to collect a private store's garbage (it waits until nobody is using the store). Unset: as a less specific entry says, else never. Ignored for the host's store.
repos.<name>.privateStore.gc.schedule.cron
- Type
- string
- Example
"0 4 * * *"
When, as 5 cron fields.
repos.<name>.privateStore.gc.schedule.tz
- Type
- string
- Default
"UTC"
The time zone of cron.
repos.<name>.privateStore.substituteFromHost
- Type
- null or boolean
- Default
null
Substitute from the host's store: what any repo using it built or fetched, so private stores don't build it all again. Unset: as a less specific entry says, else off.
repos.<name>.read
- Type
- list of string
- Default
[ ]
Users who may read (fetch, browse, logs).
repos.<name>.store
- Type
- null or one of "host", "match", "repo"
- Default
null- Example
"match"
Which Nix store the repo's evaluations, builds and effects use:
host, the host's /nix/store, shared with every repo using it;
match, on a pattern, one private store for all the repos it
matches; repo, a private store of the repo's own. Others' CI
can't read a private store, and its paths are served by a binary
cache of its own (<cache URL>/store/<name>/-). Unset: as a less
specific entry says, else stores.default.
repos.<name>.substituters
- Type
- list of (submodule)
- Example
[ { publicKeys = [ "cache.example.org-1:…" ]; url = "https://cache.example.org"; } ]
What a private store substitutes from, in place of the host's
nix.conf substituters. cache.nixos.org is defined at the
normal priority, so a list set here adds to it; lib.mkForce [ ]:
none, nothing to ask but privateStore.substituteFromHost. Each
one is more to check for every path. Set apart from store, and
resolved for the store's name like privateStore: an entry left
at cache.nixos.org alone takes a less specific entry's. Ignored
for the host's store.
repos.<name>.substituters.*.credentialsFile
- Type
- null or string
- Default
null- Example
"/run/secrets/cache-example"
A file outside the store holding user:password, sent as
basic auth to the URL's host. Read by the store manager
(root) when it starts the store's daemon.
repos.<name>.substituters.*.publicKeys
- Type
- list of string
- Default
[ ]- Example
[ "cache.example.org-1:…" ]
Keys it signs with, trusted on top of the host's.
repos.<name>.substituters.*.url
- Type
- string
- Example
"https://cache.example.org"
The binary cache's URL.
repos.<name>.visibility
- Type
- null or one of "private", "metadata-unlisted", "metadata-public", "unlisted", "public"
- Default
null
Who can see what (see DESIGN.md, access matrix). Unset: as a less specific pattern says, else private.
repos.<name>.webhookTokens
- Type
- attribute set of attribute set of string
- Default
{ }- Example
{ rebuild-docs = { github = "sha256:…"; }; }
effect -> token name -> sha256:<hex> (see gtd keys token).
repos.<name>.write
- Type
- list of string
- Default
[ ]
Users who may do anything: push (forced too), delete, decide approvals, and for a pattern, create matching repos by pushing.
retention
retention.age
- Type
- null or signed integer
- Default
7776000
Drop logs older than this many seconds.
retention.keep
- Type
- unsigned integer, meaning >=0
- Default
1
Protect logs of tags and of commits within keep-1 steps of branch tips.
retention.size
- Type
- null or signed integer
- Default
null
Drop the oldest logs once the log DB exceeds this many bytes.
stores
stores.default
- Type
- one of "repo", "host"
- Default
"repo"
The store of repos no entry sets store for: one of their own, or
the host's /nix/store, shared (one cache for everything, so
nothing is built twice, but anyone whose CI runs there can read
what's in it).
stores.idleSeconds
- Type
- positive integer, meaning >0
- Default
300
A private store's daemon nobody used for this long is stopped.
stores.maxRunning
- Type
- positive integer, meaning >0
- Default
4
Private stores' daemons running at once; work for another store waits its turn.
users
users
- Type
- attribute set of (submodule)
- Default
{ }
Users. Names are shared by SSH, the web and the cache.
users.<name>.cacheTokens
- Type
- list of string
- Default
[ ]- Example
[ "sha256:…" ]
Tokens (their sha256:<hex> hashes, from gtd keys token) for
the authenticated binary cache and artifact downloads: the
basic-auth password, with the user's name as the user. They
carry the user's access: caches and artifacts of the repos
they can read.
users.<name>.passwordHash
- Type
- null or string
- Default
null
The hash itself, from gtd passwd. It ends up readable to
everyone on the host: prefer passwordHashFile.
users.<name>.passwordHashFile
- Type
- null or absolute path
- Default
null- Example
"/run/secrets/gt-chris"
A file (outside the store) holding the hash of the user's web
password, from gtd passwd. Without a password a user can't
log in on the web (SSH keys still work).
users.<name>.sshKeys
- Type
- list of string
- Default
[ ]- Example
[ "ssh-ed25519 AAAA… chris@laptop" ]
Public keys the user logs in with over SSH (git as git@<host>).
Command line
gt, the command line (the gt package of gt's flake), talks to the host as you: CI live, job logs, approvals, retries, repos, search, artifacts. Log in through the browser once per host:
In a checkout of a repo on that host, commands are about that repo (its remotes say which) and the checked-out branch; elsewhere, --repo names it. With logins on several hosts, --auth <name> picks one (else the remotes, else the active login).
For git over HTTPS, gt auth setup-git makes git ask gt for credentials: no passwords to type.
gt auth
gt auth login
gt auth login <NAME> <URL> [--no-browser]Log in to a host through the browser, and make it the active login.
Opens the host's page to authorize gt (log in there first if need be), which hands gt a token for this machine. Where the browser can't reach this machine, --no-browser shows a link to open anywhere and asks for the code it gives.
NAMEWhat to call this login (
work,home, …)URLThe host's web address, e.g. gt.example.org
--no-browserDon't open a browser here: paste a code instead
gt auth list
gt auth listThe logins, which is active, and whether each still works
gt auth show
gt auth show [<NAME>]Who a login is and where, and its token (default: the active one)
NAME
gt auth remove
gt auth remove [<NAME>]Log out: revoke the login's token on its host and forget it
NAME
gt auth set
gt auth set <NAME>Make a login the active one
NAME
gt auth setup-git
gt auth setup-git [<NAME>] [--global]Let git use gt's login for HTTPS remotes on its host (in this repo, or for all of yours with --global)
NAMEThe login (default: the active one)
--globalIn your git config, not this repo's
gt ci
gt ci watch
gt ci watch [--repo <REPO>] [--ref <REF>] [--follow]Follow the CI run of a ref live, drawn as on the web.
Until nothing runs any more (or for good, with --follow); follows the ref to new commits. Exits non-zero when the run failed.
--repoThe repo (default: the one this checkout's remotes point at)
--refA branch, tag or commit (default: the checked-out branch, else the repo's HEAD)
-f, --followKeep watching (and following the ref) when the run is done
gt ci show
gt ci show <JOB> [--repo <REPO>] [--ref <REF>]A job: what it is, how it went, and its log (live while it runs)
JOBbuilds/<name>,effects/<name>, or just the name--repoThe repo (default: the one this checkout's remotes point at)
--refA branch, tag or commit (default: the checked-out branch, else the repo's HEAD)
gt ci approve
gt ci approve <EFFECT> [--yes] [--comment <COMMENT>] [--repo <REPO>] [--ref <REF>]Approve an approval step, after showing what it's about
EFFECTThe approval's name
-y, --yesDon't ask: approve
-m, --commentA comment to go with the decision
--repoThe repo (default: the one this checkout's remotes point at)
--refA branch, tag or commit (default: the checked-out branch, else the repo's HEAD)
gt ci deny
gt ci deny <EFFECT> [--yes] [--comment <COMMENT>] [--repo <REPO>] [--ref <REF>]Deny an approval step (what follows it is skipped)
EFFECTThe approval's name
-y, --yesDon't ask: deny
-m, --commentA comment to go with the decision
--repoThe repo (default: the one this checkout's remotes point at)
--refA branch, tag or commit (default: the checked-out branch, else the repo's HEAD)
gt ci retry
gt ci retry <JOB> [--repo <REPO>] [--ref <REF>]Run a failed job again (where that makes sense: the latest of it)
JOBbuilds/<name>,effects/<name>, or just the name--repoThe repo (default: the one this checkout's remotes point at)
--refA branch, tag or commit (default: the checked-out branch, else the repo's HEAD)
gt ci trigger
gt ci trigger <EFFECT> [--kind <KIND>] [--repo <REPO>] [--ref <REF>]Run a scheduled or webhook effect now, on the branch
EFFECTThe effect's name
--kindWhich of its triggers, if it has both (schedule, webhook)
--repoThe repo (default: the one this checkout's remotes point at)
--refA branch, tag or commit (default: the checked-out branch, else the repo's HEAD)
gt repo
gt repo list
gt repo listThe repos you can see, with how CI went at their HEAD
gt search
gt search
gt search <QUERY> [--global] [--repo <REPO>]Search code, commits and changes (in this repo, or everywhere with --global)
QUERYWhat to look for, in the web's search syntax (
from:alice,path:src,lang:rust, …)-g, --globalSearch every repo, not just this one
--repoThe repo to search (default: this checkout's)
gt artifact
gt artifact list
gt artifact list [<ARTIFACT>] [--repo <REPO>] [--ref <REF>]The artifacts of a ref, or one artifact's files
ARTIFACT--repoThe repo (default: the one this checkout's remotes point at)
--refA branch, tag or commit (default: the checked-out branch, else the repo's HEAD)
gt artifact get
gt artifact get <ARTIFACT> [<FILE>] [--output <OUTPUT>] [--repo <REPO>] [--ref <REF>]Download an artifact's file (its only file if there's one)
ARTIFACTFILE-o, --outputWhere to write it (
-for stdout; default: its name, here)--repoThe repo (default: the one this checkout's remotes point at)
--refA branch, tag or commit (default: the checked-out branch, else the repo's HEAD)
gt api
gt api
gt api <PATH> [<BODY>]Call the JSON API: GET, or POST with a JSON body. Prints JSON.
The path is under /-/api/ (me, repos, repos/<repo>/refs, …) unless it starts with /.
PATHBODYA JSON body: makes it a POST
gt stream
gt stream
gt stream [--global] [--repo <REPO>]Follow the host's change events, one JSON object a line
-g, --globalEvery repo you can see, not just this one
--repoThe repo (default: this checkout's)
gt web
gt web
gt web [<REF>] [--browse] [--refs] [--log] [--repo <REPO>]Open the repo (or a ref of it) in the browser; outside a repo, the index
REFA branch, tag or commit to open
-b, --browseIts files
-r, --refsAll branches and tags
-l, --logIts commits
--repoThe repo (default: this checkout's)
Files on the host
Where the module keeps things, and what losing each costs.
/var/lib/git: the repos (back up)
| Path | What | If lost |
|---|---|---|
repos/<name>.git | The bare repos: the source of truth | The code |
/var/lib/gt: state (back up)
| Path | What | If lost |
|---|---|---|
db/ci.db | CI: refs seen, evaluations, jobs, effect runs and approvals, what's published, mirror pushes and pulls, schedules, repo meta, the mirrors' SSH key pairs | All CI history and what's published; mirrors get new keys (add them at the remotes again). Recreated empty: each repo's tip is built again, its push effects skipped as not pushed |
db/auth.db | Web login sessions | Everyone is logged out |
keys/<repo>.age | Each repo's age identity for its secrets, encrypted to the SSH host key | Secrets can't be decrypted until re-encrypted to the new recipients |
cache-key | The binary cache's signing key | Clients must trust the new public key |
url-key | Signs content URLs | Nothing (links in open pages expire) |
gcroots/ | Keeps what's published from Nix's garbage collection | Rebuilt from ci.db |
mirror/ | Known hosts of the mirrors' remotes | Learned again |
/var/cache/gt: disposable
| Path | What |
|---|---|
db/logs.db | Logs (builds, effects, mirrors), effect results and summaries |
index/ | Search indexes |
export/ | What the public side serves |
hooks/registry.json | Webhooks the hook receiver accepts |
effects-work/ | Effects' working copies |
nix-cache/ | Nix's own cache for the services (fetched flake inputs) |
Elsewhere
| Path | What |
|---|---|
/etc/ssh/ssh_host_ed25519_key | The SSH host key: the root of trust (it decrypts the repos' keys). Back it up |
/run/gt, /run/gt-hub, /run/gt-public, /run/gt-hooks | Sockets between the services, by audience |
/nix/store | What was built |