.gitbay/wiki/Users.org

a7c9352033c319587613cb6825035e8d99f5871a
gitbay/.gitbay/wiki/Users.org rendered · source · history · blame · raw

721 lines · 33568 bytes

  1#+title: gitbay user guide
  2
  3Everything here works from stock OpenSSH — replace =gitbay= with
  4=ssh git@<host>= in any command. The CLI adds convenience (instance
  5profiles, repo inference, =$EDITOR=), nothing more. Two things differ
  6over bare ssh:
  7
  8- ssh joins its arguments with spaces and the server tokenizes the
  9  result, so a value with a space in it needs a second layer of quotes:
 10  =--description "'a widget'"=, not =--description "a widget"=, which
 11  arrives as two arguments.
 12- =--help= is not understood. =ssh git@<host> help= lists every command
 13  the server knows, and =help <noun>= (=help repo=, =help mr=) prints
 14  the usage of each command under that noun.
 15
 16* Installing the CLI
 17
 18#+begin_src sh
 19go install gitbay.org/gitbay/cmd/gitbay@latest   # any platform with Go
 20
 21brew tap krz/tap https://gitbay.org/krz/homebrew-tap.git
 22brew install krz/tap/gitbay                      # Homebrew (macOS/Linux)
 23#+end_src
 24
 25Or build from source: =go build ./cmd/gitbay= in a clone of
 26=https://gitbay.org/krz/gitbay.git=.
 27
 28* Getting an account
 29
 30How you join depends on the instance's registration mode. On instances
 31with web accounts enabled, =/register= offers the same signup as a
 32browser form (paste your SSH public key); everything below works from
 33the terminal alone:
 34
 35- closed :: an admin creates your account on the host and registers your
 36  first SSH key. Nothing for you to do but hand over your public key.
 37- invite :: you receive a single-use code by email. With the SSH key you
 38  want to use:
 39  #+begin_src sh
 40  ssh git@<host> register --username you --invite <code>
 41  #+end_src
 42  Your account is active immediately; the invited address is your
 43  verified email.
 44- open ::
 45  #+begin_src sh
 46  ssh git@<host> register --username you --email you@example.org
 47  #+end_src
 48  A verification code arrives by mail. Until you run
 49  =ssh git@<host> email verify <code>=, the account is pending: you can
 50  run =whoami= and the email commands, and nothing else — no git, no
 51  repos.
 52
 53* SSH keys
 54
 55New to SSH keys? [[SSH-keys]] makes one in two commands.
 56
 57Your key is your identity; there are no passwords anywhere. The SSH
 58username is always =git= — the key alone determines who you are.
 59
 60#+begin_src sh
 61gitbay auth keys list
 62gitbay auth keys add --scope git < ~/.ssh/ci_key.pub   # key on stdin
 63gitbay auth keys add --label laptop < ~/.ssh/id_ed25519.pub
 64gitbay auth keys label SHA256:... "work laptop"
 65gitbay auth keys remove SHA256:...
 66#+end_src
 67
 68A key's label is the comment on its =authorized_keys= line unless
 69=--label= gives one; =keys label= renames a key, and with no text
 70clears the name. Labels are one line of up to 64 bytes.
 71
 72Scopes: =full= (default; git plus every control command), =git= (git
 73transport only — right for automation keys, which then cannot touch
 74issues, settings, or your account), or =runner= (the CI runner's
 75protocol plus read-only git, for the key a =gitbay-runner= host holds;
 76see [[Admin]]).
 77
 78A key belongs to exactly one account instance-wide. Registering a key
 79someone else already holds is refused without telling you whose it is.
 80
 81* Verified commits
 82
 83The commit badge is driven by the *author* email and the signing key:
 84=verified= means the signature is valid, the key is registered to an
 85account, and the author email is a verified address on that account.
 86
 87For OpenPGP signing (git's default):
 88#+begin_src sh
 89gpg --armor --export you@example.org | gitbay auth pgp add
 90#+end_src
 91
 92For SSH signing (=git config gpg.format ssh=): sign with any key
 93registered on your account; your verified addresses act as the principal
 94set. No separate registration step.
 95
 96Add and verify additional addresses with =email add <address>= /
 97=email verify <code>= (requires the instance to have SMTP; otherwise an
 98admin can assert an address for you). =email list= shows them. =email
 99remove <address>= drops one, with two refusals: the primary stays until
100=email primary <address>= names another verified address, and the last
101verified address stays, since activation, login links and commit
102identity all resolve through verified addresses. Removing a verified
103address re-evaluates signature states, so a commit authored from it
104shows as =signed_email_mismatch= afterwards. A removed address is free
105for any account to claim.
106
107The states you will see, in decreasing order of trust: =verified=,
108=signed_unknown_key= (valid signature, key not registered here — register
109it and history upgrades retroactively), =signed_email_mismatch= (real
110key, author line claims someone else), =signed_key_expired= /
111=signed_key_revoked=, =bad_signature=, =unsigned=. Server-created commits
112(web edits, merge commits) are always =unsigned= — the server holds no
113signing key on principle.
114
115=repo bookmark <owner/name>= saves a repository to come back to and
116=repo unbookmark= drops it; =repo bookmarks= lists yours, each with how
117many people have bookmarked it. The web has the control on the
118repository header, the count in the facts bar, and the list at
119=/bookmarks=. Bookmarks are public and counted; pins are private and
120drive the rail. Read access is all a bookmark needs.
121
122A job's result is a property of the commit's *tree*, not its sha: a
123rebase onto a base that touched nothing the branch did gives every
124commit a new sha and the same tree, and a job that already passed for
125that tree is not run again — the new commit gets the earlier result as
126its =ci/<job>= status, naming the build it came from. Scheduled and tag
127jobs are never reused this way; their trigger is the clock or the tag.
128
129* Profiles
130
131A profile is what =/{owner}= shows: a one-line description, a website, a
132set of links, long-form about text, the repositories you can see, org
133membership, and a year of activity. Users and orgs have the same fields.
134Repository rows carry the listing metadata too — topics, license,
135default branch, last commit — so a client renders a profile listing the
136way =/{owner}= does. The web dispatches this command rather than
137assembling the page itself.
138
139#+begin_src sh
140gitbay profile show               # your own
141gitbay profile show alice
142gitbay profile set --description "builds small tools" --website https://alice.example
143#+end_src
144
145About text is markdown by default, or org-mode. It takes inline text or
146stdin, so it can live in a file you keep:
147
148#+begin_src sh
149gitbay profile set --about "I maintain a few small tools."
150gitbay profile set --file - --about-format org < about.org
151#+end_src
152
153Up to five links, each =label|url= or a bare url, http(s) only. Passing
154=--link= replaces the whole set; a single empty one clears it:
155
156#+begin_src sh
157gitbay profile set --link "Mastodon|https://fosstodon.example/@alice" \
158                   --link https://alice.example/now
159gitbay profile set --link ""      # clear
160#+end_src
161
162Every field follows the same rule as the rest of the CLI: a flag you
163leave out is untouched, and ='' clears the one you name. Org profiles
164work the same way and need org admin:
165
166#+begin_src sh
167gitbay org profile krz --description "software and experiments" --about-format org --file - < krz.org
168gitbay org profile krz            # no flags shows it
169#+end_src
170
171The web renders profiles but has no form for editing one, so the CLI is
172the only interface today. =profile set= is not =SSHOnly=, so the JSON API
173runs it like any other write command.
174
175* Repositories
176
177#+begin_src sh
178gitbay repo create you/project [--private]
179gitbay repo clone you/project
180gitbay repo list
181gitbay repo show you/project
182gitbay repo log you/project --limit 20      # commits with signature states
183gitbay repo fork other/project [--name mine]
184gitbay repo rename you/project tool          # clone URLs change
185gitbay repo delete you/project --yes
186#+end_src
187
188=repo show= also reports your own state on the repository — =watch=
189(=watching= or =muted=; =repo unwatch= clears either) and =bookmarked= —
190and =fork_of= when it is a
191fork whose parent you can read, so a client draws a toggle rather than
192two blind buttons. Absent means none.
193
194Pushing is SSH-only. Public repositories are anonymously readable over
195HTTPS (and =git://= where enabled); private repositories exist only over
196SSH and answer "not found" to everyone without access.
197
198Access and settings (owner or =admin= grant):
199#+begin_src sh
200gitbay repo access grant you/project alice write    # read | write | admin
201gitbay repo access revoke you/project alice
202gitbay repo access list you/project                 # everyone who can reach it, role, and via what
203gitbay repo settings protect you/project main       # no force-push, no delete
204gitbay repo settings require-mr you/project on      # protected branches: merge requests only
205gitbay repo settings protect-tag you/project 'v*'   # matching tags: created once, never moved or deleted
206gitbay repo settings default-branch you/project trunk # HEAD, and what the web shows
207gitbay repo settings require-signed you/project on  # every commit must verify
208gitbay repo settings git-daemon you/project on      # expose over git://
209gitbay repo topics add you/project cli forge        # free-form tags, shown on the web
210gitbay repo search forge                            # find repos by name/description/topic
211gitbay repo grep you/project "some string"          # literal git grep over the default branch
212gitbay repo pin you/project                         # pin to your web dashboard
213gitbay repo unpin you/project
214gitbay repo archive you/project                     # read-only: pushes and issue/MR
215gitbay repo unarchive you/project                   #   writes refused, browsing intact
216#+end_src
217
218Import from another forge — git data first, then optionally the issue
219and PR history from GitHub or any Forgejo instance such as Codeberg
220(issues keep state/labels/comments; PRs land as closed or merged MRs
221with their discussion; originals are attributed inline since foreign
222authors have no local account; re-running resumes where it stopped):
223#+begin_src sh
224gitbay repo import you/mirror --from https://github.com/you/repo.git \
225    [--private] [--token-stdin]        # token on stdin, never in the URL
226gitbay repo import-issues you/mirror --from you/repo --token-stdin
227gitbay repo import-issues you/mirror --from you/repo \
228    --api-base https://codeberg.org/api/v1    # Forgejo: the site's /api/v1
229#+end_src
230
231Sourcehut has no API of that shape; =repo import= takes its git data
232and the todo.sr.ht tracker is not read.
233
234Moving between gitbay instances (no lock-in): run on the TARGET, with
235your key registered on both sides. Profile, repos with settings,
236issues, MRs, and comments replay with attribution; git data mirrors
237client-side through your own key. Keys never transfer and emails
238arrive unverified — trust is per-instance. Re-running resumes.
239Push-blocking policies (require-signed, protected branches) are
240deferred and printed for you to re-apply after the data lands.
241=gitbay auth export= alone doubles as a user-level backup.
242
243#+begin_src sh
244gitbay migrate --from old-instance.example [--from-port 22]
245#+end_src
246
247Mirroring keeps a foreign remote in sync during a gradual migration
248(repo admin; https remotes; the token is stored server-side for the
249recurring sync and never echoed back):
250
251#+begin_src sh
252gitbay repo mirror add you/project https://github.com/you/project.git \
253    --direction push --token-stdin    # propagate after every local push
254gitbay repo mirror add you/copy https://github.com/them/theirs.git \
255    --direction pull                  # follow upstream; local pushes refused
256gitbay repo mirror list               # sync status and last error, per mirror
257gitbay repo mirror sync / remove <id>
258#+end_src
259
260Markdown and org files render on the web when opened, the way a README
261does on the repository page, with relative links resolved against the
262file's directory; =source= in the file's action bar (or =?view=source=)
263shows the text instead. Other files show the text with highlighting.
264
265* Organizations
266
267Orgs share the owner namespace with users and own repositories at
268=org/repo=. By default members get write on all org repos; org admins
269get repo admin, create repos under the org, and manage membership. An
270org's existence and member list are public; its teams are visible to
271members, and anyone else asking is refused rather than told the org
272does not exist.
273
274#+begin_src sh
275gitbay org create krz
276gitbay org members add krz alice [--role admin]
277gitbay org show krz
278gitbay org rename krz newname     # clone URLs change
279gitbay org delete krz --yes       # only when it owns no repositories
280#+end_src
281
282Large orgs scope access with teams: set what plain membership implies,
283then grant per-repo roles through named teams (org admins always keep
284admin; the default =write= keeps the simple model):
285
286#+begin_src sh
287gitbay org settings members-role krz none   # write | read | none
288gitbay org team create krz core-devs
289gitbay org team add krz core-devs alice bob # org members only
290gitbay org team grant krz core-devs krz/gitbay write
291gitbay org team show krz core-devs          # members + grants
292gitbay org team revoke / remove / delete ...
293#+end_src
294
295* Issues
296
297Anyone who can read a repository can file and comment. Closing/reopening
298is for the author or anyone with write; labels and assignees need write.
299
300#+begin_src sh
301gitbay issue create --title "it breaks" [--body "..." | --file -]
302gitbay issue list [--state open|closed|all]
303gitbay issue show 4
304gitbay issue comment 4 --message "same here"
305gitbay issue edit 4 --title "better title" [--body|--file -]  # author or write
306gitbay issue close 4 / reopen 4
307gitbay issue label 4 --add bug --remove wontfix
308gitbay issue assign 4 --add alice
309gitbay issue milestone 4 v1.0                 # or "none" to clear
310#+end_src
311
312Inside a clone, the repository is inferred from the =origin= remote —
313that is why no =owner/name= appears above. Anywhere else, pass it as the
314first argument. Long text: =--body= inline, =--file -= from stdin, or
315neither on a terminal and =$EDITOR= opens.
316
317A wiki is the =.gitbay/wiki/= directory on the default branch — no
318separate repository, the same rendering pipeline as READMEs (markdown
319and org). Editing it is editing a file: a push, =repo commit-file=, or
320the web editor on a repository that permits server-authored commits (one
321requiring signed commits is push-only, since the server signs nothing):
322
323#+begin_src sh
324mkdir -p .gitbay/wiki
325# add Home.md (or .org), more pages, images; relative links between
326# pages just work on the web at /you/project/wiki
327git add .gitbay/wiki && git commit -m "wiki" && git push
328#+end_src
329
330Releases anchor notes and binary assets to a pushed tag (write access;
331assets stream over SSH, capped by the instance's =max_asset_bytes=).
332While a release exists its tag can be neither deleted nor moved, whether
333or not the tag is protected: delete the release first.
334
335#+begin_src sh
336gitbay release create v1.0 --title "First light" [--notes|--file -|$EDITOR]
337gitbay release asset add v1.0 tool-linux-amd64 < dist/tool-linux-amd64
338gitbay release asset get v1.0 tool-linux-amd64 > tool   # or the web download link
339gitbay release list / show v1.0 / delete v1.0 --yes
340#+end_src
341
342The web shows them under the repository's =releases= tab with rendered
343notes, sha256 sums, and download links. Feed readers subscribe at
344=/you/project/releases.atom=; commits are at =/you/project/log.atom=
345(the default branch) or =/you/project/log.atom/<ref>=, and an owner's
346activity on their public repositories at =/you/activity.atom=. The
347pages carry the discovery link.
348
349Commit messages act on issues when the commits land on the default
350branch (direct push, MR merge, =repo commit-file= or the web editor):
351=closes/fixes/resolves #4= closes the
352issue with a linking comment, and a bare =#4= leaves a reference
353comment. Each issue/commit pair acts once, ever. =Closes owner/name#N= closes an issue in another repository when
354you hold write there with a key whose scope reaches it; otherwise it
355stays a plain link. A deploy key closes nothing outside the repository
356it is bound to, but naming that repository by full path works as the
357bare form does. The comment left
358on the closed issue links the closing commit by its repository path, so
359closing a public repository's issue from a private one names the private
360repository there. A bare =owner/name#N= links and does nothing.
361
362Milestones group issues and MRs toward a release (write access to
363manage, attach with =issue milestone= / =mr milestone=; progress shows
364on the web at =/owner/name/milestones=):
365
366#+begin_src sh
367gitbay milestone create v1.0 --description "first release" --due 2027-01-01
368gitbay milestone list [--state open|closed|all]
369gitbay milestone close v1.0 / reopen v1.0
370#+end_src
371
372An org holds labels and milestones every repository under it sees
373beside its own. =issue label --add=, =issue milestone= and =mr
374milestone= resolve the org's row first; a repository cannot create a
375label or milestone with a name its org holds. Creating an org label or
376milestone whose name repositories under the org already use folds them
377in: their issues and merge requests move to the org's row. Org admins
378manage them; counts span the repositories you can read.
379
380#+begin_src sh
381gitbay org label set acme bug --color cf222e
382gitbay org label list acme / remove acme bug
383gitbay org milestone create acme v2 --due 2027-03-01
384gitbay org milestone list acme [--state open|closed|all]
385gitbay org milestone close acme v2 / reopen acme v2
386#+end_src
387
388On the web: =/acme/-/labels= and =/acme/-/milestones=, read-only.
389
390Issue templates: commit =.gitbay/issue-template.md= (and optional
391=issue-template-<name>.md= variants) to the default branch. =gitbay
392issue create= prefills =$EDITOR= with the default template, the web
393form prefills its textarea, and =gitbay issue templates= lists them.
394
395Lists narrow the same way on every surface: =issue list --label bug
396--assignee bob --author alice --milestone v1= (or =--milestone none=),
397=mr list --author bob --milestone v1=; the web's issue and merge request
398lists take the same names as query parameters, and each active filter
399shows with a link that drops it.
400
401Labels take a colour: =gitbay label set bug --color cf222e=; =label
402list= shows each with its colour and how many issues carry it, and
403=label remove= takes one off every issue. =issue label --add= still
404creates a colourless label on the fly.
405
406* Merge requests
407
408#+begin_src sh
409gitbay mr create --source feature --target main --title "add thing"
410gitbay mr create other/upstream --source you/fork:feature --target main --title "..."
411gitbay mr list / show 4 / diff 4
412gitbay mr checkout 4              # local branch mr/4 from the MR head
413gitbay mr review 4 --approve      # or --request-changes / --comment
414gitbay mr merge 4 [--strategy ff|merge|squash|rebase]
415gitbay mr close 4
416#+end_src
417
418Semantics worth knowing:
419
420- the MR head lives in the *target* repository as
421  =refs/merge-requests/N/head= (fetchable by any reader), so an MR
422  survives deletion of its source branch or fork. It is never removed
423  on merge or close; after a history rewrite an instance admin can
424  drop it with =admin mr prune=.
425- force-pushing the source updates the MR and marks existing reviews
426  stale — unless the diff is the one they reviewed. A rebase onto a
427  target that moved on changes every sha and nothing about the change,
428  so fresh reviews follow it to the new head; a push that changes the
429  diff stales them.
430- default strategy: fast-forward when possible, else a merge commit.
431  Squash makes one commit authored by the MR author, committed by the
432  merger. Rebase replays a linear range preserving authors; it refuses
433  ranges containing merge commits, and when fast-forward is possible it
434  *is* one (original commits and signatures land untouched).
435- on =require_signed_commits= branches only fast-forwards of fully
436  verified commits merge; everything server-created is refused with
437  instructions to rebase locally. =gitbay mr rebase <n>= is those
438  instructions: in a clone of the target it replays the source branch
439  onto the target and force-pushes it, then =mr merge <n>= fast-forwards.
440  The replay is local, so the commits carry your signature and not the
441  server's — it holds no key. A branch living in a fork is refused, with
442  the repository to run it in; rebase it there.
443
444Repo admins can gate merges (=repo settings ...=): =require-approvals
445<n>= (fresh, non-author approvals; each reviewer's latest review is
446their stance, and a fresh request-changes blocks), =require-resolved=
447(no open review threads), =require-checks= (all statuses green), and
448=require-codeowners= (an approval from an owner of every owned changed
449file). Owners come from a =CODEOWNERS= file on the target branch, root
450or =.gitbay/=, gitignore-style patterns, last match wins. The toggle is
451the opt-in, so a repository can carry the file as documentation of who
452to ask without it gating merges; with it on and no file on the target
453branch, the merge is refused and says so. It does not wait on
454=require-approvals=.
455
456=mr show= carries a =gates= block with where the merge request stands
457against all of them — approvals counted and required, owners still
458outstanding per file, open threads, checks, and whether a fast-forward
459is possible — and the merge request page shows the same block. =mr
460merge= names every unmet gate at once rather than the first. =mr
461review= says when a verdict is advisory, which it is from anyone
462without write access: the gates do not count it.
463
464Those gates apply to =mr merge=. A direct push to a protected branch
465passes none of them until =require-mr on=: then an existing protected
466branch refuses every push, including =repo commit-file= and the web
467editor, and the server's merge is its only writer. Creating the branch
468is still a push, since there is nothing to open a merge request against
469yet.
470
471A merge request whose target is another open merge request's source
472branch is stacked on it: =mr create= says so, =mr show= carries
473=stacked_on= and =stacked=, and merging the lower one retargets the
474upper onto =main= with its reviews kept. See [[Stacked-MRs][Stacked
475merge requests]].
476
477Review threads anchor to diff lines:
478
479#+begin_src sh
480gitbay mr diff-comment 4 --path main.go --line 12 --message "use log here"
481gitbay mr diff-comment 4 --reply 7 --message "done"     # join thread 7
482gitbay mr threads 4                                     # threads with staleness
483gitbay mr resolve 4 7 / unresolve 4 7
484#+end_src
485
486Threads render inline on the MR page. A force-push marks them stale
487(shown under "threads on earlier revisions") rather than guessing new
488anchors; =mr show= reports the unresolved count. Resolving is for the
489thread author, the MR author, or anyone with write.
490
491* CI builds
492
493A =.gitbay/ci.yml= in the repo runs jobs on every branch push:
494
495#+begin_src yaml
496jobs:
497  test:
498    steps:
499      - go test ./...
500#+end_src
501
502A config holds at most ten jobs of fifty steps each, each step under
5034096 bytes, in a file under 64 KiB; a longer step runs a script from the
504repository (=- sh ci/publish.sh=). A config past a cap, or otherwise
505unparseable, records a failed =ci/config= status on the commit and
506queues nothing, so look there when a push builds nothing.
507
508Each job becomes a build (=build list=, =build log=, the builds tab on
509the web) and a =ci/<job>= commit status, which =repo settings
510require-checks= can gate merges on. Steps run with =sh -c= on the
511instance's runner, stopping at the first failure; a broken config
512surfaces as a failed =ci/config= status. Environment: =GITBAY_REPO=,
513=GITBAY_SHA=, =GITBAY_REF=, =GITBAY_JOB=, =CI=true=, and =GITBAY_SSH=,
514the instance's ssh destination as the build reaches it (=git@gitbay.org=
515from a runner elsewhere; inside a container on the server's own runner
516the host is at a private address the runner fills in). A job that
517talks back to the instance — a release asset, a comment, a push to a
518pages branch — uses =$GITBAY_SSH= with a key it holds as a secret;
519the build's container has no key of its own. Two things about that
520container: a secret with newlines (a private key) arrives intact, and
521=ssh= expands =~= from the passwd entry, =/root=, not from =$HOME=,
522which is the build home — so keep an ssh config in the workspace and
523pass it with =ssh -F= (and =GIT_SSH_COMMAND="ssh -F ..."= for git),
524which also works on a runner with no container, where writing to
525=~/.ssh= would edit that machine's own configuration.
526
527Secrets: =repo secret set <owner/name> <NAME>= reads the value from
528stdin (never argv) and injects it into the repo's builds as =$NAME=;
529=repo secret list= shows names only, and the value is never echoed
530back. Anyone with write access can read a secret from inside a build,
531so scope them accordingly.
532
533Schedules: a job with =schedule: "17 11,23 * * *"= (five-field cron,
534server-local time; lists, ranges, and steps supported) runs on its cron
535against the default branch instead of on push. A default-branch push
536registers or updates the schedule. A job with =tags: "v*"= runs when a
537matching tag is pushed — and only then; =schedule= and =tags= are
538mutually exclusive. =build trigger <owner/name> <job>= queues any job
539immediately, and =build cancel <owner/name> <n>= withdraws one, queued
540or running: a running build stops at the runner within seconds and the
541log says who cancelled it. Both need write access.
542
543What each push shape queues, with dedupe, path filters, schedules and
544the reaper together, is one table on [[CI][CI]].
545
546** Your own runner
547
548Builds run on runners attached to the repository. An instance need not
549offer any: install =gitbay-runner= on a machine of yours and attach it.
550
551#+begin_src sh
552brew install krz/tap/gitbay-runner        # or a binary from the release
553gitbay-runner init -remote git@gitbay.org
554#+end_src
555
556=init= generates a key under =~/.config/gitbay-runner/=, writes
557=config.toml= beside it, and prints the public key with the command to
558attach it:
559
560#+begin_src sh
561gitbay repo runner add owner/name < ~/.config/gitbay-runner/id_ed25519.pub
562#+end_src
563
564or paste the key under Runners on the repository's settings page. Then
565=brew services start krz/tap/gitbay-runner=, or run =gitbay-runner= with
566no arguments; it reads the config file, and any flag overrides it.
567
568What it builds: every build for the repositories it is attached to,
569with the repository's secrets, and nothing else. Merge requests from
570forks are untrusted and wait unless the runner runs with =-untrusted=,
571which is only sensible with =-isolation podman -image <ref>= (see
572[[Admin][Admin]]). Attach one runner to several repositories by repeating
573=repo runner add=; run several runners on one account by running =init=
574on each machine. =repo runner list= shows each attached key, when it
575last polled and the build it holds; =repo runner remove <fingerprint>=
576detaches one (the key stays on your account; =keys remove= drops it).
577A runner key reaches only the runner protocol and read-only git, so a
578build step that reads it off disk cannot administer your account.
579
580* Large files (LFS)
581
582Standard Git LFS works over both transports with no setup beyond the
583usual =git lfs track=. SSH remotes authenticate through
584=git-lfs-authenticate= (deploy keys included: ro keys can download, rw
585keys upload); anonymous HTTPS clones of public repositories can fetch
586LFS objects with no credentials. Objects are verified against their
587sha256 on upload and capped at 512MB by default.
588
589* Pages
590
591On instances with =[pages] domain= set, a =pages= branch in any public
592repo is served as a static site: the repo named =pages= at
593=https://<you>.<domain>/=, every other repo at
594=https://<you>.<domain>/<repo>/=. Push HTML to publish; a CI job can
595build and push the branch for automatic deploys. Sites run on a
596separate origin — your scripts work, and the forge's cookies are out of
597reach.
598
599A repo can also serve its pages branch on a domain you own.
600=repo domain add <owner/name> <domain>= claims it and prints a DNS TXT
601challenge (=_gitbay-challenge.<domain>=); create the record, run
602=repo domain verify=, then point the domain's A/AAAA records at the
603instance (DNS-only if the domain sits behind a proxying provider — the
604instance issues its own certificates). Claims are exclusive per
605instance; unverified claims serve nothing and expire after 7 days.
606=repo domain list= reports pending/verified/expired.
607
608* Snippets
609
610A snippet is one or more named text files you own outside any
611repository, for a log or a fragment shared by URL. Create one from a
612file on stdin; the reply is the id and the URL:
613
614#+begin_src sh
615gitbay snippet create build.log --description "failing build" < build.log
616gitbay snippet file set <id> notes.txt < notes.txt   # add or replace a file
617gitbay snippet file get <id> build.log > build.log
618gitbay snippet edit <id> --visibility public
619gitbay snippet list                                  # yours
620gitbay snippet list <owner>                          # their public ones
621gitbay snippet delete <id>
622#+end_src
623
624Visibility is =public= (listed on your page), =unlisted= (anyone with
625the URL, listed nowhere; the default) or =private= (you alone; not
626found to everyone else). Files are text, valid UTF-8, each under the
627instance's =max_snippet_bytes=, at most 64 per snippet. A snippet keeps
628at least one file. There is no history: setting a file replaces it.
629
630On the web, =/<you>/-/snippets= lists yours, each snippet page renders
631its files with a raw link per file, and the same page creates, edits
632and deletes through the commands above.
633
634* Browser sessions
635
636=gitbay web login= mints a one-time URL; the session it opens lasts
637seven days. =gitbay web sessions list= shows each of yours by a short
638id with its creation and expiry, and =gitbay web sessions revoke <id>=
639or =--all= ends them from the terminal, which is where a lost laptop is
640handled.
641
642* Notifications
643
644When the instance has SMTP configured, activity mails you as well as
645filing the inbox row: someone opens an issue or MR on your repository,
646comments where you are a participant (author, commenter, reviewer, or
647mentioned), reviews, closes, or merges. =notifications settings mail
648off= keeps the inbox and stops that mail (login links are not activity
649and still arrive); the account page has the same switch.
650=notifications settings watch on= makes you a recipient of every issue
651and merge request on the repositories you can write to, as if you had
652run =repo watch= on each: it is consulted when a notice is delivered,
653so a grant or a revoke needs no watch row, and an explicit watch or
654mute on a repository still wins. Off by default; the account page has
655this switch too. Writing =@name= in an issue, merge request or
656comment files "mentioned you" in that account's inbox and makes them a
657participant of the thread, provided they can read the repository and
658have not muted it; watchers are not told about a mention addressed to
659someone else.
660You are never mailed about your own actions, and only verified primary
661addresses receive anything. Delivery retries on relay failure.
662
663* Web vocabulary
664
665Sign in / Log out, Search, sentence-case headings and buttons, product
666name lowercase.
667
668* Output rules
669
670=--json= is the contract; the plain output is for a person at a
671terminal, and follows these rules so every noun reads the same way.
672
673- A list command prints one row per item, tab-separated, no header.
674  Columns run identifier, state, then description; a trailing column
675  may carry a word (=due 2027-01-01=, =via team=). The =gitbay= CLI pads
676  the tabs into aligned columns when stdout is a terminal and leaves
677  them as tabs when piped, so =cut -f= sees the same bytes stock ssh
678  prints. Under =--json= nothing is touched.
679- An empty list prints nothing on stdout and =nothing to list= on
680  stderr.
681- A mutation prints one line: verb, object, identifier
682  (=created krz/gitbay#7=). A second line appears only for something
683  to copy: a URL, a token shown once.
684- A bad invocation prints =usage:= and the command's registered usage,
685  the same text =help <noun>= shows. Exit 2.
686- A refusal says who may and what to do instead: =only admins of acme
687  can manage teams; ask one to add you=. Exit 4. A thing that does not
688  exist, or that you may not know exists, is exit 3.
689- stdout is the result; stderr is everything else: progress, a note
690  that output was truncated, errors.
691- Verbs: =create= and =delete= for things with their own identity
692  (repository, issue, release, team), =add= and =remove= for attaching
693  something to them (a key, a member, a label on an issue), =set= for
694  a value, =revoke= for a credential, =show= and =list= for reads.
695
696* Scripting
697
698Every read command takes =--json= and emits one envelope:
699={"protocol_version": 1, "data": ...}=. stdout is data, stderr is
700messages. Exit codes are stable: 0 ok, 1 failure, 2 usage, 3 not found,
7014 denied, 5 server/protocol error. Usage means the arguments were wrong;
702a refusal — a name already taken, a state that does not allow the change
703— is a failure. Nothing ever prompts; destructive commands take =--yes=.
704
705For HTTP automation see [[API]].
706
707* CLI setup
708
709#+begin_src sh
710gitbay remote add myforge forge.example.org [--port n] [--user u] --default
711gitbay remote list
712gitbay init [name] [--private]   # git init + repo create + origin, in one step
713#+end_src
714
715Configuration lives at =~/.config/gitbay/config.toml=. The CLI shells
716out to your real =ssh=, so =~/.ssh/config=, the agent, and hardware keys
717all apply. It shares one connection per instance through a control
718socket under =~/.ssh=: the first command in five minutes pays the
719handshake and the rest ride it. =no_multiplex = true= on an instance in
720the config turns that off. Man pages: =gitbay man --dir <dir>=; completions:
721=gitbay completion bash|zsh|fish=.