.gitbay/wiki/Architecture/08-Operations.org

v1.43.1
gitbay/.gitbay/wiki/Architecture/08-Operations.org rendered · source · history · blame · raw

95 lines · 5589 bytes

 1#+title: Operations
 2
 3* Logging
 4
 5- The daemon logs with Go's =log/slog= default handler to stderr, which
 6  systemd sends to the journal. Retention is the journal's.
 7- Logged: listener start-up, schema version, worker failures (webhook,
 8  mail, push, mirror), sweeps and reaps with counts, SSH lookup errors.
 9- Not logged: request bodies, tokens, secrets. Mail errors are logged
10  with addresses redacted.
11
12* Audit log
13
14Table =audit_log=: actor, action, JSON data, time
15(=internal/store/audit.go=). Readable by admins with =audit=.
16
17| Recorded                                     | How                                                  |
18|----------------------------------------------+------------------------------------------------------|
19| Every successful mutating command, every surface | =Dispatch= writes =cmd <path>= with pruned argv and the source: key fingerprint, =web=, =api= or =host= (=internal/control/control.go=) |
20| SSH authentication failures and throttling   | =auth.failed= (IP, fingerprint), =auth.throttled= (IP) |
21| Registration                                  | =auth.registered=, =pending.expired=                 |
22| Administration                                | =admin user.*=, =admin email.*=, =admin invite.issued=, =admin repo.*=, =admin mr.prune=, =admin runners.forget= |
23| Repository events of security interest        | =push.forced=, =repo.runner.add/remove=, =pages.domain_verified= |
24
25Failed commands and reads are not audited. The separate =events= table is
26the product activity feed, not an audit trail.
27
28* Monitoring
29
30- =/healthz= returns the serving commit and a database check.
31- =deploy/cloud-init.yaml= installs an hourly heartbeat that checks the
32  service, disk, certificate expiry and backup age, and can POST to an
33  external monitor URL.
34- =admin runners= reports the build queue: pending builds, claims and
35  average and worst claim wait over 24 hours, reaped builds, and each
36  runner key's last poll.
37
38* Patching
39
40- Host: =unattended-upgrades= with automatic security updates and a
41  04:30 reboot (=deploy/cloud-init.yaml=).
42- Application: =govulncheck= nightly in CI; a module update is a
43  normal merge request and deploy.
44- CI image: rebuilt by the operator when =deploy/Containerfile.ci=
45  changes; weekly =podman image prune= removes old images.
46
47* Backup and recovery
48
49| Item            | Schedule | Kept | Contents                                                        |
50|-----------------+----------+------+-----------------------------------------------------------------|
51| Full archive    | nightly  | 7    | SQLite snapshot (=VACUUM INTO=), all repositories, LFS, SSH host keys; age-encrypted when =[backup] age_recipients= is set |
52| Database only   | hourly   | 48   | SQLite snapshot; age-encrypted when =[backup] age_recipients= is set |
53| Offsite (restic)| nightly  | per prune policy | =/var/lib/gitbay= and a staged database copy, to object storage |
54
55- The database snapshot is taken before repositories are read, and
56  each repository's HEAD, refs/ and packed-refs are archived before its
57  objects, so every archived ref finds the objects it reaches, unless
58  git's own automatic gc after a push repacks during the walk; the
59  archive can then miss objects, and =--verify= reports it. A push
60  during the backup is missing or present as unreferenced objects
61  (=cmd/gitbayd/backup.go=).
62- Excluded: WAL files, the hook socket, askpass scripts, generated
63  hooks.
64- =gitbayd admin backup --verify= checks SQLite integrity, that every
65  repository the database names is present, =git fsck
66  --connectivity-only= on each, release assets against their recorded
67  sha256, and LFS objects against their names (=backup.go=).
68  =gitbayd admin restore-drill= runs the same checks on a full
69  extraction and reports elapsed time and the newest recovered
70  activity (=restoredrill.go=).
71- Repository deletes, renames and transfers refuse while a full backup
72  runs (=internal/backuplock=), so the snapshot and the walk agree.
73- The host's restic credentials are append-only; the key that can
74  delete or prune snapshots is held off the host, so a compromised host
75  cannot destroy its own history (documented: Admin wiki).
76- Recovery point: about one hour for database-only data (issues, merge
77  requests, reviews), one day for repositories.
78- Recovery time: 8m43s from the offsite copy to a working clone in the 2026-09-29 drill, without host provisioning; the Admin wiki's Restore drill table has each drill.
79
80Restore procedure: extract the archive into an empty directory, point
81=server.root= at it, start =gitbayd=; hooks regenerate and the host key
82is preserved.
83
84* Operator levers during an incident
85
86| Need                               | Command                                          |
87|------------------------------------+--------------------------------------------------|
88| Stop a user                        | =admin user disable <name>=                      |
89| Remove a key                       | =keys remove= (own) or =admin user= commands     |
90| Kill a user's browser sessions     | =web sessions revoke --all= (as that user)       |
91| Revoke a token                     | =token revoke <name>=                            |
92| Stop a runner key claiming         | =repo runner remove=, =admin runners forget <fingerprint>= |
93| Hide a repository                  | =admin repo visibility <repo> private=           |
94| Close registration                 | =registration.mode = "closed"= and restart       |
95| See what happened                  | =audit= (filter by actor, action, time)                |