From 6c3f577842962dd57e9b14060fc2238d7f3ee30c Mon Sep 17 00:00:00 2001 From: tatulatu700-lang Date: Thu, 20 Aug 2026 16:46:50 +1000 Subject: [PATCH 1/6] docs: add initial language-agnostic PROTOCOL.md wire specification --- PROTOCOL.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 PROTOCOL.md diff --git a/PROTOCOL.md b/PROTOCOL.md new file mode 100644 index 000000000..f8761ff04 --- /dev/null +++ b/PROTOCOL.md @@ -0,0 +1,24 @@ +# Gitlawb Wire Protocol Specification (v1) + +## 1. Identity & DIDs +- **Key Type:** Ed25519 (`ed25519`). +- **Supported Methods:** `did:key`, `did:web`, and `did:gitlawb`. +- **Resolution:** Deterministic public key extraction from multicodec prefixes for verification. + +## 2. Authentication (RFC 9421) +- **HTTP Signatures:** Route requests require `Signature-Input` covering `@method`, `@path`, and `content-digest`. +- **Signer Identity:** `keyid` points to the actor DID; algorithm standard is `alg="ed25519"`. +- **Integrity:** `Content-Digest` SHA-256 header validation on mutating POST/PUT endpoints. + +## 3. Proof of Intelligence (iCaptcha) +- **Gate:** Enforced via `403 icaptcha_proof_required` responses. +- **Headers:** Clients must present `x-icaptcha-url`, `x-icaptcha-level`, and a valid `x-icaptcha-proof`. + +## 4. Ref-Update Certificate +- **Schema:** `gitlawb/ref-update/v1`. +- **Payload:** Canonical JSON bytes including target repository, commit OID, previous OID, and actor DID. +- **Validation:** Multi-signature threshold validation against configured branch protection rules. + +## 5. Storage & Git Transport +- **Smart-HTTP:** Standard `/{owner}/{repo}/info/refs` and `git-upload-pack` endpoints. +- **Content Addressing:** Git SHA-256 mapped to IPFS CID chunks with IPNS-backed branch pointers. From 3b00725fd37ff73c638017ec1047603a57599739 Mon Sep 17 00:00:00 2001 From: tatulatu700-lang Date: Thu, 20 Aug 2026 16:50:29 +1000 Subject: [PATCH 2/6] docs: add language-agnostic PROTOCOL.md wire spec --- PROTOCOL.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index f8761ff04..72fcf3241 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -1,24 +1,23 @@ # Gitlawb Wire Protocol Specification (v1) ## 1. Identity & DIDs -- **Key Type:** Ed25519 (`ed25519`). -- **Supported Methods:** `did:key`, `did:web`, and `did:gitlawb`. -- **Resolution:** Deterministic public key extraction from multicodec prefixes for verification. +- **Key Types:** Ed25519 (`ed25519`). +- **Methods:** `did:key`, `did:web`, `did:gitlawb`. +- **Resolution:** Deterministic public key extraction from multicodec prefixes. ## 2. Authentication (RFC 9421) -- **HTTP Signatures:** Route requests require `Signature-Input` covering `@method`, `@path`, and `content-digest`. -- **Signer Identity:** `keyid` points to the actor DID; algorithm standard is `alg="ed25519"`. -- **Integrity:** `Content-Digest` SHA-256 header validation on mutating POST/PUT endpoints. +- **HTTP Signatures:** Requires `Signature-Input` covering `@method`, `@path`, and `content-digest`. +- **Signer Identity:** `keyid` points to the actor DID; algorithm is `alg="ed25519"`. +- **Integrity:** SHA-256 `Content-Digest` verification on write endpoints. ## 3. Proof of Intelligence (iCaptcha) - **Gate:** Enforced via `403 icaptcha_proof_required` responses. -- **Headers:** Clients must present `x-icaptcha-url`, `x-icaptcha-level`, and a valid `x-icaptcha-proof`. +- **Headers:** Clients must present `x-icaptcha-url`, `x-icaptcha-level`, and `x-icaptcha-proof`. ## 4. Ref-Update Certificate - **Schema:** `gitlawb/ref-update/v1`. -- **Payload:** Canonical JSON bytes including target repository, commit OID, previous OID, and actor DID. -- **Validation:** Multi-signature threshold validation against configured branch protection rules. +- **Payload:** Canonical JSON bytes defining target repo, commit OID, previous OID, and actor DID. ## 5. Storage & Git Transport -- **Smart-HTTP:** Standard `/{owner}/{repo}/info/refs` and `git-upload-pack` endpoints. +- **Smart-HTTP:** Endpoints at `/{owner}/{repo}/info/refs` and `git-upload-pack`. - **Content Addressing:** Git SHA-256 mapped to IPFS CID chunks with IPNS-backed branch pointers. From 31343d25af2b68146445f85c974440fa565aa26e Mon Sep 17 00:00:00 2001 From: Kevin Codex Date: Mon, 31 Aug 2026 14:35:09 +0800 Subject: [PATCH 3/6] docs: scope PROTOCOL.md to shipped alpha behavior Address all five review findings: did:key-only resolution (did:web / did:gitlawb marked planned), SHA-1 object format with IPNS demoted to planned and the REST refs CID mapping described instead, the receive-pack route and the 404-fetch vs 401-push auth split documented, iCaptcha wire shape corrected (client sends only x-icaptcha-proof; url/level are 403 response headers; ICAPTCHA_MODE defaults off over a small endpoint set), and the core gitlawb/ref-update/v1 schema spelled out field-for-field with node push receipts explicitly split off as a different document. --- PROTOCOL.md | 80 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index 72fcf3241..93c579635 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -1,23 +1,77 @@ -# Gitlawb Wire Protocol Specification (v1) +# Gitlawb Wire Protocol Specification (v1, alpha) + +This document describes the wire behavior a second implementation can interop +with **today**. Anything the node does not ship yet is explicitly marked +*planned*; nothing below is aspirational unless it says so. ## 1. Identity & DIDs -- **Key Types:** Ed25519 (`ed25519`). -- **Methods:** `did:key`, `did:web`, `did:gitlawb`. -- **Resolution:** Deterministic public key extraction from multicodec prefixes. + +- **Key type:** Ed25519. +- **Method (alpha):** `did:key` only. Resolution is deterministic public-key + extraction from the multicodec prefix (`z6Mk…`, multicodec `0xed01`). +- Signed requests presenting any other DID method are rejected; the node's + auth layer answers with a hint that only `did:key` is supported in alpha. +- *Planned:* `did:web` and `did:gitlawb` resolution. Clients MUST NOT assume + either is accepted anywhere yet. ## 2. Authentication (RFC 9421) -- **HTTP Signatures:** Requires `Signature-Input` covering `@method`, `@path`, and `content-digest`. -- **Signer Identity:** `keyid` points to the actor DID; algorithm is `alg="ed25519"`. -- **Integrity:** SHA-256 `Content-Digest` verification on write endpoints. + +- **HTTP signatures:** `Signature-Input` covers `@method`, `@path`, and + `content-digest`: + `sig1=("@method" "@path" "content-digest");keyid="did:key:…";alg="ed25519";created=` +- **Signer identity:** `keyid` is the actor DID (`did:key` only, see §1). +- **Integrity:** `Content-Digest` is SHA-256 over the request body and is + verified when the header is present. ## 3. Proof of Intelligence (iCaptcha) -- **Gate:** Enforced via `403 icaptcha_proof_required` responses. -- **Headers:** Clients must present `x-icaptcha-url`, `x-icaptcha-level`, and `x-icaptcha-proof`. + +- **Mode:** controlled by `ICAPTCHA_MODE` = `off` | `shadow` | `enforce`, + **default `off`**. A node with the gate off never demands proof. +- **Scope:** the gate applies to a small set of write endpoints (agent + registration and repo create/fork), not to all writes. +- **Wire shape:** the client-supplied header is `x-icaptcha-proof` — that is + the only iCaptcha header a node reads from a request. When proof is + required and missing/invalid, the node answers + `403 icaptcha_proof_required` and sets `x-icaptcha-url` and + `x-icaptcha-level` **on the response** (also mirrored as JSON fields) so + the client can go solve a challenge and retry. ## 4. Ref-Update Certificate -- **Schema:** `gitlawb/ref-update/v1`. -- **Payload:** Canonical JSON bytes defining target repo, commit OID, previous OID, and actor DID. + +Two distinct certificate-shaped payloads exist; this section documents the +**core signing schema**, which is what `gitlawb/ref-update/v1` names: + +- **Type string:** `gitlawb/ref-update/v1`. +- **Body (canonical JSON, the signed bytes):** `type`, `repo` (the + repository DID), `ref_name`, `from` (previous hash, 64 hex chars, + all-zeros for a new ref), `to` (target hash, 64 hex chars), `seq` + (monotonically increasing, replay prevention), `timestamp` (RFC 3339), + `nonce`. +- **Signatures:** a list of `{signer: did:key, sig: base64url-unpadded + Ed25519}` entries appended outside the signed body; thresholds are counted + over distinct signers that verify. + +The node's REST API separately serves **push receipts** — a different, +node-issued JSON payload attesting that a push was processed. Receipts are +not `gitlawb/ref-update/v1` documents and are not interchangeable with them; +they are documented with the node's HTTP API, not here. ## 5. Storage & Git Transport -- **Smart-HTTP:** Endpoints at `/{owner}/{repo}/info/refs` and `git-upload-pack`. -- **Content Addressing:** Git SHA-256 mapped to IPFS CID chunks with IPNS-backed branch pointers. + +- **Object format:** bare repositories are created with + `--object-format=sha1`; production OIDs are 40 hex chars. *Planned:* + SHA-256 object format. +- **Smart-HTTP endpoints:** + - `GET /{owner}/{repo}/info/refs` — ref advertisement (fetch and push + service discovery). + - `POST /{owner}/{repo}/git-upload-pack` — clone/fetch. + - `POST /{owner}/{repo}/git-receive-pack` — push. **Authentication is + mandatory**: an unsigned push is refused with `401`. +- **Fetch vs push auth split:** fetch-side requests against a withheld or + private repo answer `404` (existence is not confirmed to unauthorized + callers), while the push path answers `401` for missing/invalid + signatures. A second implementation must not treat the two symmetrically. +- **Content addressing:** the node records IPFS CID metadata per branch and + serves it over REST (the repo `refs` endpoint maps branch heads to CIDs). + *Planned:* IPNS-backed branch pointers; IPNS is not part of the shipped + wire contract. From 1a576ea030ff6ab21dbc0f0d5ed420a303d9bfaf Mon Sep 17 00:00:00 2001 From: beardthelion <56458543+beardthelion@users.noreply.github.com> Date: Tue, 10 Nov 2026 21:36:01 -0600 Subject: [PATCH 4/6] docs(protocol): close the outstanding review findings on the wire spec - @path binds path plus query, not RFC 9421's path-only value; a second implementation signing info/refs without ?service= fails verification - content-digest is a mandatory covered component; a body-less request signs the SHA-256 of the empty body, also accepted when absent - TLS transport stated in the signature section with a pointer from smart-HTTP - the ref-update certificate section is marked planned: the schema is defined in code but no shipped endpoint serves it, and its 64-hex hash fields reserve the SHA-256 width while repos are sha1 today - smart-HTTP documents the mandatory ?service= values, pkt-line framing, and the upload-pack/receive-pack result content types --- PROTOCOL.md | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index 93c579635..e7f4dea05 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -16,12 +16,23 @@ with **today**. Anything the node does not ship yet is explicitly marked ## 2. Authentication (RFC 9421) +- **Transport:** signed requests are expected over TLS; the node does not + terminate HTTP signatures over cleartext. - **HTTP signatures:** `Signature-Input` covers `@method`, `@path`, and `content-digest`: `sig1=("@method" "@path" "content-digest");keyid="did:key:…";alg="ed25519";created=` + All three components are mandatory: a `Signature-Input` missing one is + rejected as `incomplete_signature`. +- **`@path` value:** the node binds `@path` to the path *and* query string of + the request, not to the path-only value RFC 9421 defines for `@path`. A + second implementation must sign `GET /r/info/refs?service=git-upload-pack` + including the `?service=…` part. - **Signer identity:** `keyid` is the actor DID (`did:key` only, see §1). - **Integrity:** `Content-Digest` is SHA-256 over the request body and is - verified when the header is present. + verified when the header is present. Because `content-digest` is a covered + component, every signed request signs a digest value: on a body-less + request the signed value is the SHA-256 of the empty body, which the node + also accepts when the header is absent entirely. ## 3. Proof of Intelligence (iCaptcha) @@ -38,6 +49,15 @@ with **today**. Anything the node does not ship yet is explicitly marked ## 4. Ref-Update Certificate +*Planned:* the `gitlawb/ref-update/v1` schema below is defined in code, but no +shipped endpoint serves or verifies these certificates today; nothing in +sections 2-3 produces one on the wire. It is documented here so a second +implementation shares the type vocabulary ahead of the wire surface. + +The hash fields reserve 64 hex characters for the SHA-256 object format; the +repositories the node serves are sha1 with 40-hex OIDs (see §5), so the field +width is a forward reservation, not what a current push produces. + Two distinct certificate-shaped payloads exist; this section documents the **core signing schema**, which is what `gitlawb/ref-update/v1` names: @@ -62,11 +82,20 @@ they are documented with the node's HTTP API, not here. `--object-format=sha1`; production OIDs are 40 hex chars. *Planned:* SHA-256 object format. - **Smart-HTTP endpoints:** - - `GET /{owner}/{repo}/info/refs` — ref advertisement (fetch and push - service discovery). - - `POST /{owner}/{repo}/git-upload-pack` — clone/fetch. + - `GET /{owner}/{repo}/info/refs?service=git-upload-pack` — fetch + advertisement. + - `GET /{owner}/{repo}/info/refs?service=git-receive-pack` — push + advertisement. + The `?service=` parameter is mandatory and takes exactly those two + values; a missing parameter is a `400`, not an omitted-service default. + - `POST /{owner}/{repo}/git-upload-pack` — clone/fetch. Request and + response bodies are pkt-line framed; the result content type is + `application/x-git-upload-pack-result`. - `POST /{owner}/{repo}/git-receive-pack` — push. **Authentication is - mandatory**: an unsigned push is refused with `401`. + mandatory**: an unsigned push is refused with `401`. Same pkt-line + framing; the result content type is + `application/x-git-receive-pack-result`. + Signed requests on this path follow §2, including TLS transport. - **Fetch vs push auth split:** fetch-side requests against a withheld or private repo answer `404` (existence is not confirmed to unauthorized callers), while the push path answers `401` for missing/invalid From 1213ef8658e215a14e309e0bd1099c9b3bb84bcd Mon Sep 17 00:00:00 2001 From: beardthelion <56458543+beardthelion@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:10:00 -0600 Subject: [PATCH 5/6] docs(protocol): correct digest, transport, path, and icaptcha claims - Content-Digest is required on every signed request: the node refuses a signed request without it (400 missing_content_digest); a body-less request still sends the SHA-256 of the empty body, never omits it - transport: deployments should run TLS in front, but the node verifies signatures on whatever transport reaches it, no scheme gate - path normalization documented: one trailing .git stripped, owner may be full did:key or bare multibase, and @path is the literal path_and_query bytes; the signing example is now signable as written - icaptcha enforce with no loaded verifier key stays inert, so the 403 reads as enforce-plus-loaded-key, not enforce alone --- PROTOCOL.md | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index e7f4dea05..f03354903 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -16,8 +16,10 @@ with **today**. Anything the node does not ship yet is explicitly marked ## 2. Authentication (RFC 9421) -- **Transport:** signed requests are expected over TLS; the node does not - terminate HTTP signatures over cleartext. +- **Transport:** production deployments should run the node behind TLS + (typically a reverse proxy). The node itself has no scheme check: it verifies + RFC 9421 headers on whatever transport the request arrives over, so nothing + at the signature layer refuses a cleartext request. - **HTTP signatures:** `Signature-Input` covers `@method`, `@path`, and `content-digest`: `sig1=("@method" "@path" "content-digest");keyid="did:key:…";alg="ed25519";created=` @@ -25,14 +27,17 @@ with **today**. Anything the node does not ship yet is explicitly marked rejected as `incomplete_signature`. - **`@path` value:** the node binds `@path` to the path *and* query string of the request, not to the path-only value RFC 9421 defines for `@path`. A - second implementation must sign `GET /r/info/refs?service=git-upload-pack` - including the `?service=…` part. + second implementation must sign the request exactly as it goes on the + wire, e.g. `GET /zOwner/myrepo.git/info/refs?service=git-upload-pack` + including the `?service=…` part (see §5 for the owner and `.git` + normalization). - **Signer identity:** `keyid` is the actor DID (`did:key` only, see §1). - **Integrity:** `Content-Digest` is SHA-256 over the request body and is - verified when the header is present. Because `content-digest` is a covered - component, every signed request signs a digest value: on a body-less - request the signed value is the SHA-256 of the empty body, which the node - also accepts when the header is absent entirely. + verified against it. Every signed request MUST send the header: a signed + request without it is refused `400 missing_content_digest` before routing. + On a body-less request the header still carries the SHA-256 digest of the + empty body (`sha-256=:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=:`), + it is never omitted. ## 3. Proof of Intelligence (iCaptcha) @@ -41,11 +46,13 @@ with **today**. Anything the node does not ship yet is explicitly marked - **Scope:** the gate applies to a small set of write endpoints (agent registration and repo create/fork), not to all writes. - **Wire shape:** the client-supplied header is `x-icaptcha-proof` — that is - the only iCaptcha header a node reads from a request. When proof is - required and missing/invalid, the node answers - `403 icaptcha_proof_required` and sets `x-icaptcha-url` and + the only iCaptcha header a node reads from a request. When the gate is + `enforce` and a verifier public key is loaded, a missing or invalid proof + answers `403 icaptcha_proof_required` and sets `x-icaptcha-url` and `x-icaptcha-level` **on the response** (also mirrored as JSON fields) so - the client can go solve a challenge and retry. + the client can go solve a challenge and retry. Fail-safe: `enforce` with + no public key loaded (unset and unfetchable) stays inert and admits the + request rather than rejecting every request. ## 4. Ref-Update Certificate @@ -81,6 +88,12 @@ they are documented with the node's HTTP API, not here. - **Object format:** bare repositories are created with `--object-format=sha1`; production OIDs are 40 hex chars. *Planned:* SHA-256 object format. +- **Path normalization:** `{repo}` may carry one trailing `.git` suffix, + stripped before lookup (so `myrepo.git` resolves to `myrepo`; a repo + literally named `foo.git` addresses as `foo.git.git`). `{owner}` may be the + full `did:key:…` string or the bare multibase segment the `did:key:` prefix + implies; both resolve to the same owner. None of this changes `@path`: + sign the request path and query exactly as sent. - **Smart-HTTP endpoints:** - `GET /{owner}/{repo}/info/refs?service=git-upload-pack` — fetch advertisement. From 61b9c4b27c5ba08e9063c7c58e21b89602c83662 Mon Sep 17 00:00:00 2001 From: beardthelion <56458543+beardthelion@users.noreply.github.com> Date: Wed, 11 Nov 2026 11:24:15 -0600 Subject: [PATCH 6/6] =?UTF-8?q?docs(protocol):=20stop=20requiring=20TLS=20?= =?UTF-8?q?on=20signed=20smart-HTTP=20in=20=C2=A75?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §5 still told readers signed git-receive-pack traffic follows §2 "including TLS transport" after §2 was corrected to state the verifier has no scheme check. A second implementation could infer TLS is part of the signed push contract and reject cleartext requests the node accepts. Point the path at §2's RFC 9421 header rules and defer transport policy to §2's non-normative deployment note. --- PROTOCOL.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index f03354903..ba5e97f80 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -108,7 +108,9 @@ they are documented with the node's HTTP API, not here. mandatory**: an unsigned push is refused with `401`. Same pkt-line framing; the result content type is `application/x-git-receive-pack-result`. - Signed requests on this path follow §2, including TLS transport. + Signed requests on this path follow the RFC 9421 header rules in §2. + §2's transport note applies here too: the verifier has no scheme check, + so nothing at the signature layer requires this push to arrive over TLS. - **Fetch vs push auth split:** fetch-side requests against a withheld or private repo answer `404` (existence is not confirmed to unauthorized callers), while the push path answers `401` for missing/invalid