.gitbay/wiki/Users.org

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

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