forge restore moved litestream's copy of identity.db into place with whatever mode litestream gave it; forge identity restore made it 600 first, and now identity_pick does too. Scenario: a stand-in litestream writes the file readable by anyone, and the restored one is 600.
- commit
- 1283f9e0f1748aa765dd47489ae9a95fe74249d1
- parent
- 3e6ffab7b6c67950bc8c4dc702a6e0d275ddd489
- author
- cat <admin@dev.site>
- ci
- ✓ passed in 115s, signed by cat
- kind
- fix risk score 1.1 of 3, Jev's estimate
- review
- #30 Security: sign-in, invites, owner pages, lake, caps and the edge
ci attestation
forge-attestation 1 type ci commit 1283f9e0f1748aa765dd47489ae9a95fe74249d1 tree fa935637f46bfd1922e31ab558d4ba42dfed759f script .forge/ci 8d2000a416f8160bcf710d98b72af9eb76287f58 result pass exit 0 started 2026-10-19T08:02:13Z duration 115s runner (not shown here; refs/notes/ci has it) system Darwin 25.5.0 arm64 env host log sha256:534ead1c5a82f591db37f1ce6c4afa724115a9665887d196966fab19e3ccb7df 326 lines |pushed wip/i18n |7230645 running .forge/ci: greet.sh: say what it is | | sh: /bin/sh | | ok greet |7230645 pass in 0s, signed |pushed main |remote: review #4: /dami/greet/reviews/4/ |pushed wip/i18n |25d5d21 running .forge/ci: wave at the world | | sh: /bin/sh | | FAIL greet : got 'hello, world o/', want 'hello, world' |25d5d21 fail in 0s, signed |forge: 1 commit(s) did not pass |remote: review #6: /dami/greet/reviews/6/ |pushed ana/wave |e96bc06 running .forge/ci: update the test for the wave | | sh: /bin/sh | | ok greet |e96bc06 pass in 0s, signed |remote: review #6: /dami/greet/reviews/6/ |pushed ana/wave |warning: You appear to have cloned an empty repository. |site home: deployed be3ccbc -> /sites/ana/dotfiles/home/ |created ana/notes |remote: review #1: /ana/notes/reviews/1/ |pushed ana/todo |landing #1 A todo list: ana/todo -> main |244c0e6 running .forge/ci: web, server and Python versions of greet | | sh: /bin/sh | | ok greet |244c0e6 pass in 0s, signed |pushed main |1bac811 running .forge/ci: messages: notes for the later regions | | sh: /bin/sh | | ok greet |1bac811 pass in 0s, signed |remote: review #7: /dami/greet/reviews/7/ |pushed dami/regions |created dami/forge |warning: You appear to have cloned an empty repository. |site guide: mkdir -p guide/fonts && cp site/assets/fonts/*.woff2 guide/fonts/ |site guide: deployed 9d60294 -> /sites/dami/forge/guide/ |(node:95047) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built ana/dotfiles in 0s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.hUmi5V/wt/demo/root/.forge/www |(node:95232) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built ana/notes in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.hUmi5V/wt/demo/root/.forge/www |(node:95419) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built dami/forge in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.hUmi5V/wt/demo/root/.forge/www |(node:95599) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built dami/greet in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.hUmi5V/wt/demo/root/.forge/www |(node:96318) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time |(Use `node --trace-warnings ...` to show where the warning was created) |built - in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.hUmi5V/wt/demo/root/.forge/www |235 of 235 pages render the same without 11ty |guide: 8 files, links ok |ok
Signature: good, cat
| M | bin/ | 2 | ++ |
| A | test/ | 26 | ++++++++++++++++++++++++++ |
@@ -3738,6 +3738,8 @@ identity_pick() {
37383738 if [ -f "$db" ]; then bk=$(node "$FORGE_HOME/lib/identity.ts" changed "$db"); fi37393739 if [ "$lt" -ge "$bk" ]; then37403740 rm -f "$db-wal" "$db-shm"3741+ # Only git reads it, as forge identity restore leaves it.3742+ chmod 600 "$tmp"37413743 mv "$tmp" "$db"37423744 say "restore: identity.db is litestream's, which changed last ($(utc "$lt"))"37433745 else@@ -0,0 +1,26 @@
1+# When forge restore takes identity.db from litestream's replica (it2+# changed after the backup's copy), the file is the git user's alone (600),3+# however litestream wrote it. A stand-in litestream restores a copy of4+# identity.db that is readable by anyone.5+. "$(dirname "$0")/../lib.sh"6+user dami7+ok ssh_as dami token a8+"$FORGE" backup --dry-run --force >/dev/null 2>&1 || fail "backup --dry-run"9+mv "$FORGE_ROOT/.forge/backup/stage" "$T/stage"10+# The replica, a token later.11+export FORGE_NOW=$((T_NOW + 60))12+ok ssh_as dami token b13+FORGE_STATE=$FORGE_ROOT/.forge ok node "$T_HOME/lib/identity.ts" snapshot "$T/replica.db"14+mkdir -p "$T/bin"15+cat >"$T/bin/litestream" <<SH16+#!/bin/sh17+o=18+while [ \$# -gt 0 ]; do case \$1 in -o) o=\$2; shift ;; esac; shift; done19+cp "$T/replica.db" "\$o" && chmod 644 "\$o"20+SH21+chmod +x "$T/bin/litestream"22+export PATH=$T/bin:$PATH FORGE_BACKUP_REPOSITORY=$T/restic FORGE_BACKUP_PASSWORD=test23+r=$T/restored24+ok restore_into "$r" --from "$T/stage"25+has "$T_LOG" "identity.db is litestream's, which changed last"26+eq "$(mode "$r/.forge/identity.db")" 600