Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
07832a4
feat(node): add a persistent libp2p identity key file
beardthelion Aug 11, 2026
47446e2
fix(node): load the libp2p identity from the persistent key file
beardthelion Aug 11, 2026
5b64408
fix(node): create the p2p key with 0600 at creation and reject a loos…
beardthelion Aug 11, 2026
4e320b4
fix(node): publish the p2p key atomically and pin its directory
beardthelion Aug 12, 2026
025f95b
style(node): comma instead of a dash in the key-load warning
beardthelion Aug 12, 2026
13359b3
fix(node): require the p2p key path to name a directory
beardthelion Aug 14, 2026
a3589d8
test(node): run the key-permission probe in its own process
beardthelion Aug 14, 2026
befb9e5
docs(node): scope the p2p key permission claim and note the rotation
beardthelion Aug 14, 2026
774ea36
fix(review): close a `..` escape from the p2p key-path check
beardthelion Aug 14, 2026
f0995d4
fix(review): reject `..` in an absolute p2p key path too
beardthelion Aug 14, 2026
85ff328
fix(node): scrub the serialized p2p private key from memory
beardthelion Aug 14, 2026
e6432e7
fix(node): validate the p2p key path before securing its parent
beardthelion Aug 28, 2026
b049ee5
fix(node): satisfy clippy single-element-loop on p2p path test
beardthelion Aug 28, 2026
0f17b51
fix(node): tighten the p2p key storage contract before mutation
beardthelion Aug 28, 2026
4796e99
fix(node): refuse foreign-owned p2p key paths and cap reads on all pl…
beardthelion Aug 28, 2026
e7e178b
fix(node): hold the p2p key directory open across its trust checks
kevincodex1 Aug 31, 2026
5011670
fix(node): walk the p2p key ancestor chain descriptor-anchored
beardthelion Sep 1, 2026
1404aeb
fix(node): refuse group-writable and non-directory p2p key ancestors
beardthelion Sep 1, 2026
fd9216f
fix(node): verify the cwd anchor for relative p2p key paths
beardthelion Sep 1, 2026
db994e8
test(node): stop rendering uids in p2p assert-message strings
beardthelion Sep 1, 2026
0a92145
test(node): add walk rows to the p2p key-storage contract matrix
beardthelion Sep 1, 2026
1de4e6a
docs(node): note the verified working directory for relative p2p key …
beardthelion Sep 1, 2026
be626ba
fix(node): pin and verify the mode of every key object this process c…
beardthelion Sep 2, 2026
221f901
fix(node): create and publish the node identity key through the pinne…
beardthelion Sep 2, 2026
082200e
fix(node): split p2p key config validation from live storage validation
beardthelion Sep 2, 2026
90b4cdc
docs(node): state the two-domain key-path policy and how to see a deg…
beardthelion Sep 2, 2026
01e0463
test(node): pin the degrade signal and wait for both startup lines
beardthelion Sep 2, 2026
32d41e0
fix(node): keep key publication transactional and path-policy split
beardthelion Sep 3, 2026
2a7d2e8
fix(node): refuse a writable cwd for a bare identity key
beardthelion Sep 3, 2026
e7e4a16
fix(node): load identity without following pathnames
beardthelion Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,32 @@
# Generate with: gl identity new
GITLAWB_KEY=/data/keys/identity.pem

# Path to the node's persistent libp2p identity key file. Must include a
# directory, because the node will not keep its p2p identity key in the working
# directory. A value that cannot name a securable key file (a bare filename,
# `..` traversal, a trailing slash, the filesystem root, or a `~/` path that
# escapes home) is refused before the node binds, so the process exits.
# Anything wrong with the storage itself (a symlinked or non-directory parent,
# an unsafe cwd or ancestor, an unreadable or malformed key) leaves p2p off
# while HTTP keeps serving, logged at error as p2p_identity_key_load_failed and
# counted by the gitlawb_p2p_identity_key_load_failed metric. /health still
# reports healthy in that state, so alert on the event or the metric. Fixing
# the storage and restarting restores the same PeerId.
# On Unix it is created 0600 inside a 0700 directory. A key directory that
# grants access beyond its owner is tightened to 0700 on start; one closed too
# far is refused with a chmod 700 remedy rather than widened. On other
# platforms no permissions are enforced.
# If the node logs that it tightened a key directory that was GROUP OR WORLD
# WRITABLE, treat the key that was sitting there as possibly exposed: delete
# it so a fresh identity is generated on the next start. An ordinary 0755
# directory is tightened too, but others could not read a 0600 key or replace
# its directory entry, so do not rotate for that case. A directory that was
# merely closed too far is refused, not tightened, and no key in it was ever
# exposed.
# Keep it on a persistent volume so the PeerId survives redeploys.
# Default: ~/.gitlawb/p2p.key
#GITLAWB_P2P_KEY=/data/keys/p2p.key

# Publicly reachable URL of this node (used in peer announcements)
GITLAWB_PUBLIC_URL=https://your-node.example.com

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ WORKDIR /data

ENV GITLAWB_REPOS_DIR=/data/repos \
GITLAWB_KEY=/data/keys/identity.pem \
GITLAWB_P2P_KEY=/data/keys/p2p.key \
GITLAWB_HOST=0.0.0.0 \
GITLAWB_PORT=7545 \
GITLAWB_P2P_PORT=7546
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ Important node settings:
| `GITLAWB_P2P_PORT` | libp2p QUIC/UDP port. Use `0` to disable. |
| `GITLAWB_BOOTSTRAP_PEERS` | Comma-separated HTTP peer URLs. |
| `GITLAWB_P2P_BOOTSTRAP` | Comma-separated libp2p multiaddrs. |
| `GITLAWB_P2P_KEY` | Path to the node's persistent libp2p identity key file, which fixes the PeerId across restarts. Must name a key file inside a directory, such as `/data/keys/p2p.key` or `./keys/p2p.key`. Two kinds of problem are handled differently. A value that cannot name a securable key file at all (a bare filename, `..` traversal, a trailing `/`, the filesystem root, or a `~/` path that escapes home) is refused before the node binds, so the process exits. Anything wrong with the storage itself (a symlinked or non-directory parent, an unsafe ancestor or working directory, an unreadable or malformed key) leaves p2p off while HTTP keeps serving; the node logs it at error level as `p2p_identity_key_load_failed` and sets the `gitlawb_p2p_identity_key_load_failed` metric to 1. Note that `/health` still reports healthy in that state, so alert on the log event or the metric rather than on the health check. Correcting the storage and restarting restores the same PeerId. On Unix a new key is created `0600` inside a `0700` directory. A key directory that grants access beyond its owner is tightened to `0700` on start; one that is closed too far is refused with a `chmod 700` remedy rather than widened, since a directory locked down on purpose is an operator decision. Rotate the key only when the node logs that the directory was group- or world-writable (or the key file itself was group- or world-readable). An ordinary `0755` directory is tightened too, but others could not read a `0600` key or replace its directory entry. An existing key file is never modified: one with group or other bits set is rejected (`chmod 600`), and so is one this node cannot read. On other platforms no permissions are enforced. Default `~/.gitlawb/p2p.key`; point it at a persistent volume when running in a container. |
| `GITLAWB_BOOTSTRAP_DISABLE_SEEDS` | Disable embedded seed peers for isolated dev/test networks. |
| `GITLAWB_REQUIRE_SIGNED_PEER_WRITES` | Require signed peer announce/sync writes. Defaults to `false` during the staged rollout below. |
| `GITLAWB_ENFORCE_OWNER_PUSH` | Require the authenticated pusher to be the repo owner on `git-receive-pack`. **Defaults to `true`.** A `did:key` signature is authentication, not authorization — anyone can mint a key and sign — so with this off every signed caller may push to every repository, private ones included. Delegated and CI keys count as non-owners: a UCAN `git/push` capability is verified but not yet honored for authorization, so they cannot push while this is on. Set `false` only for a rolling upgrade; see [`docs/RUN-A-NODE.md`](docs/RUN-A-NODE.md). |
Expand Down Expand Up @@ -420,6 +421,28 @@ Production note: change the default Postgres password before exposing a node pub

Legacy-pin window: releases before the CID-resolver work stored the provider CID (Kubo dag-pb / Pinata) as a pinned object's resolver key. The `/ipfs/{cid}` resolver now recomputes the raw-content CID from the object bytes and refuses to serve a key that does not match, so `GET /api/v1/ipfs/pins` can still advertise an unrepaired legacy CID that 404s. Such a row is repaired opportunistically the next time a push carries the object again (its key is rewritten to the raw CID, the old value kept in `legacy_provider_cid`), but git negotiation omits objects the node already has, so most legacy rows never re-enter a push delta. A deferred one-shot startup sweep, not this opportunistic path, is what fully retires the advertise-then-404 window. Rows whose object bytes are gone stay withheld.

### Upgrading: the PeerId rotates once

This node's libp2p identity is now a keypair generated on first start and kept
at `GITLAWB_P2P_KEY`, rather than one derived from the node DID. Every node
therefore gets a new PeerId once, on the first start after upgrading, and keeps
it from then on as long as that key file survives (put it on a persistent volume
in a container).

Two things to check before upgrading:

- Any `GITLAWB_P2P_BOOTSTRAP` multiaddr that pins a peer's old PeerId with a
`/p2p/<PeerId>` suffix stops matching once that peer upgrades. Update the
suffix, or drop it and let identify supply the current one. Addresses without
the suffix keep working untouched.
- `GITLAWB_P2P_KEY` must name a key file inside a directory, such as
`/data/keys/p2p.key` or `./keys/p2p.key`. A bare filename is refused at
startup, since the node will not keep its identity key in the working
directory.

Peers found over `GITLAWB_BOOTSTRAP_PEERS` and the embedded seed list are
unaffected, since those are HTTP URLs and carry no PeerId.

---

## Optional node staking
Expand Down
1 change: 1 addition & 0 deletions crates/gitlawb-node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ sqlx = { version = "0.8", features = ["postgres", "runtime-tokio-rustls", "chron
clap = { version = "4", features = ["derive", "env"] }
bytes = "1"
libc = "0.2"
zeroize = "1"
cid = { workspace = true }
hex = { workspace = true }
sha2 = { workspace = true }
Expand Down
Loading
Loading