.gitbay/wiki/Users.org

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

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