Commit 9e4827a050

9e4827a050e337dd51a558790ecefa1c123b20ea

parent: 5f1152bca4

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-28 22:27 UTC

docs: restore puts the secret key file back before starting gitbayd

Ref #259

Layout: unified · split

.gitbay/wiki/Admin.org +17 −8
@@ -489,8 +489,11 @@ restic credentials), so verifying an encrypted archive happens there
489489or on a restore host.
490490
491491Restore: extract into an empty directory, point =server.root= at it,
492start gitbayd. Host keys are preserved, so clients keep their
493known_hosts entries; hooks regenerate at startup.
492restore =server.secret_key_file= from its own copy (mode 0600, owned
493by the account gitbayd runs as), then start gitbayd. No archive carries
494the key file, and without it gitbayd refuses to start. Host keys are
495preserved, so clients keep their known_hosts entries; hooks regenerate
496at startup.
494497
495498** Schedule and recovery point
496499
@@ -518,8 +521,10 @@ too.
518521** Offsite copies
519522
520523bay1 also takes a nightly restic snapshot of =/var/lib/gitbay= and
521=/var/lib/gitbay-stage= (the staged database copy) to an S3 bucket at
522Scaleway, with a key that can only add snapshots. The key that can
524=/var/lib/gitbay-stage= (a database copy and =config.toml=, staged
525there) to an S3 bucket at Scaleway, with a key that can only add
526snapshots. Nothing else under =/etc/gitbay= is in it: not the secret
527key file, not =apns.p8=. The key that can
523528remove them lives on the operator's machine, in
524529=~/.config/gitbay/offsite.env=, and never on bay1: a compromised host
525530cannot destroy its own history. Forgetting, pruning and rewriting all
@@ -568,7 +573,10 @@ each value prefixed with the id of the key that sealed it
568573=server.root=, and therefore in neither the local archives nor the
569574main restic repository. It must be copied off the host separately;
570575without it a restored database's secrets cannot be opened, and
571gitbayd refuses to start against them.
576gitbayd refuses to start against them. A separate restic repository
577for it and =apns.p8= is planned (runbook D of the data-at-rest plan)
578and not yet in place, so today the only off-host copy is one the
579operator makes by hand after =init= and after every =rotate=.
572580
573581#+begin_src sh
574582gitbayd admin secrets init # once; deploy/install.sh does it on first install
@@ -598,9 +606,10 @@ backup code (=cmd/gitbayd/backup.go=, the offsite job), and recorded
598606below. The disaster it rehearses is losing bay1, so the local archives
599607are gone with it and the sources are the main offsite restic
600608repository (repositories, LFS, the staged database, =config.toml=),
601the keys repository (=secret.key=, =apns.p8=), and the operator's
602password manager (=offsite.env=, the keys repository's password and
603token, =backup-identity.txt=). The steps are in the data-at-rest
609the off-host copy of =secret.key= and =apns.p8= (a keys repository once
610runbook D creates it; until then the operator's hand-made copy), and
611the operator's password manager (=offsite.env=, the keys repository's
612password and token once it exists, =backup-identity.txt=). The steps are in the data-at-rest
604613plan's operator runbook
605614(=docs/plans/2026-09-27-data-at-rest-and-backup.md=).
606615
.gitbay/wiki/Architecture/06-Data-and-Cryptography.org +1 −1
@@ -45,7 +45,7 @@ throttling (=internal/sshd/sshd.go=).
4545| API tokens, sessions, login links, email codes, invites | SHA-256 of a 256-bit random value; the value is shown once and never stored (=internal/store/sessions.go=) |
4646| CI secrets, webhook secrets, mirror tokens, APNs device tokens | AES-256-GCM under a key file outside the database and outside =server.root=; additional data binds table, column and row (=internal/seal=, =internal/store/secrets.go=) |
4747| SQLite file | mode 0640, directory 0750 |
48| Backups | local archives age-encrypted when =[backup] age_recipients= is set; restic encrypts the offsite copy; neither carries the secret key file, whose offsite copy is a separate keys repository |
48| Backups | local archives age-encrypted when =[backup] age_recipients= is set; restic encrypts the offsite copy; neither carries the secret key file, which is copied off the host by hand until a separate keys repository is set up |
4949| Disk | no application-level encryption; any disk encryption is the host's |
5050
5151The database file or a backup read by anyone other than the =gitbay=
cmd/gitbayd/backup.go +5 −2
@@ -47,8 +47,11 @@ affordable, and the database is the copy of issues, merge requests and
4747comments that exists nowhere else. Repositories are not in such an archive,
4848so it supplements a full backup and does not replace one.
4949
50Restore: extract into an empty directory, point server.root at it, start
51gitbayd. Host keys are preserved, so clients keep their known_hosts entries.
50Restore: extract into an empty directory, point server.root at it,
51restore server.secret_key_file from its own backup (mode 0600, owned by
52the daemon user), start gitbayd. No archive carries the key file, and
53without it gitbayd refuses to start. Host keys are preserved, so clients
54keep their known_hosts entries.
5255
5356With [backup] age_recipients set, the archive is encrypted to those age
5457public keys and its name ends in .age. --verify then needs --identity
deploy/cloud-init.yaml +6 −1
@@ -11,7 +11,12 @@
1111# - opens ufw for 22, 80, 443, 2222
1212#
1313# It does NOT install the gitbayd binary (it is not hosted anywhere yet);
14# scp it to /usr/local/bin/gitbayd afterward and `systemctl start gitbayd`.
14# scp it to /usr/local/bin/gitbayd afterward, then create the secret key
15# and hand it to the daemon user before starting:
16# gitbayd --config /etc/gitbay/config.toml admin secrets init
17# chown gitbay:gitbay /etc/gitbay/secret.key
18# systemctl start gitbayd
19# On a restore, put the key file's off-host copy there instead of init.
1520
1621package_update: true
1722packages: