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 listed secrets;
  • /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 it needs.

It may write:

  • /run/gt/out/: outputs for the effects that need it (summary.md is 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>.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.

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:

gt auth login work gt.example.org

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.

NAME

What to call this login (work, home, …)

URL

The host's web address, e.g. gt.example.org

--no-browser

Don't open a browser here: paste a code instead

gt auth list

gt auth list

The 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)

NAME

The login (default: the active one)

--global

In 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.

--repo

The repo (default: the one this checkout's remotes point at)

--ref

A branch, tag or commit (default: the checked-out branch, else the repo's HEAD)

-f, --follow

Keep 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)

JOB

builds/<name>, effects/<name>, or just the name

--repo

The repo (default: the one this checkout's remotes point at)

--ref

A 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

EFFECT

The approval's name

-y, --yes

Don't ask: approve

-m, --comment

A comment to go with the decision

--repo

The repo (default: the one this checkout's remotes point at)

--ref

A 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)

EFFECT

The approval's name

-y, --yes

Don't ask: deny

-m, --comment

A comment to go with the decision

--repo

The repo (default: the one this checkout's remotes point at)

--ref

A 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)

JOB

builds/<name>, effects/<name>, or just the name

--repo

The repo (default: the one this checkout's remotes point at)

--ref

A 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

EFFECT

The effect's name

--kind

Which of its triggers, if it has both (schedule, webhook)

--repo

The repo (default: the one this checkout's remotes point at)

--ref

A branch, tag or commit (default: the checked-out branch, else the repo's HEAD)

gt repo

gt repo list

gt repo list

The repos you can see, with how CI went at their HEAD

gt artifact

gt artifact list

gt artifact list [<ARTIFACT>] [--repo <REPO>] [--ref <REF>]

The artifacts of a ref, or one artifact's files

ARTIFACT
--repo

The repo (default: the one this checkout's remotes point at)

--ref

A 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)

ARTIFACT
FILE
-o, --output

Where to write it (- for stdout; default: its name, here)

--repo

The repo (default: the one this checkout's remotes point at)

--ref

A 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 /.

PATH
BODY

A 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, --global

Every repo you can see, not just this one

--repo

The 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

REF

A branch, tag or commit to open

-b, --browse

Its files

-r, --refs

All branches and tags

-l, --log

Its commits

--repo

The 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)

PathWhatIf lost
repos/<name>.gitThe bare repos: the source of truthThe code

/var/lib/gt: state (back up)

PathWhatIf lost
db/ci.dbCI: refs seen, evaluations, jobs, effect runs and approvals, what's published, mirror pushes and pulls, schedules, repo meta, the mirrors' SSH key pairsAll 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.dbWeb login sessionsEveryone is logged out
keys/<repo>.ageEach repo's age identity for its secrets, encrypted to the SSH host keySecrets can't be decrypted until re-encrypted to the new recipients
cache-keyThe binary cache's signing keyClients must trust the new public key
url-keySigns content URLsNothing (links in open pages expire)
gcroots/Keeps what's published from Nix's garbage collectionRebuilt from ci.db
mirror/Known hosts of the mirrors' remotesLearned again

/var/cache/gt: disposable

PathWhat
db/logs.dbLogs (builds, effects, mirrors), effect results and summaries
index/Search indexes
export/What the public side serves
hooks/registry.jsonWebhooks the hook receiver accepts
effects-work/Effects' working copies
nix-cache/Nix's own cache for the services (fetched flake inputs)

Elsewhere

PathWhat
/etc/ssh/ssh_host_ed25519_keyThe 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-hooksSockets between the services, by audience
/nix/storeWhat was built