# Changelog
All notable changes to this crate are documented here. The format is
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
this crate adheres to [Semantic Versioning](https://semver.org/).
## [0.1.0] - 2026-10-23
The founding entry — the protocol core's first release. `alkgit` is
the git smart protocol for the alk family as a **pure protocol crate**
(ADR-010, the alktty/alktunnels template): the producer and consumer
halves of the git smart protocol riding alkcall channels on the
`alk/git` ALPN, with the backend seam (five `#[async_trait]` traits)
every storage implementation implements. The crate carries no binary
and no front doors — the alkhttp `git`/`lfs` features and alkssh are
family infrastructure; a downstream app embeds this crate, brings its
own door, and supplies its backends. The shipped alkstore + alkblobs
composition is the separate `alkgit-substrate` crate; this crate is
substrate-free by construction (ADR-025's two-member workspace).
The posture is **honest advertisement** (ADR-003): the wire advertises
exactly what is served — `fetch=wait-for-done` with the full ack loop,
`object-format=sha1` pinned (the `sha256` feature gates a passthrough
per OQ-05), unimplemented surface declined fail-closed rather than
half-served, and every session bounded by budgets (ADR-009): wall
clock, request bodies, advertisement refs, negotiation rounds, haves
per round, pack sizes, the ingestion decompressed-size cap, the
packless peek window, and the blocking-pool concurrency.
### Added
**Wave 1 — the seam** (engine-blind, everything later waves compose
against):
- The backend-trait family (ADR-018 §4): `GitRegistry` /
`GitRegistryStore` (the registry family) and `GitRefs` /
`GitPackGen` / `GitPackIngest` (the object-storage family, all
`Send + Sync + #[async_trait]`, dyn-compatible, keyed on the
resolved `RepoRecord`).
- The wire-shape seam types (ADR-018 §5): `RefLine` (with the unborn
`oid: None` symref form), `RefUpdate`, `RefOutcome`,
`PreparedPush`, `PushOptions`.
- The registry record: `RepoRecord` + `Visibility` + `AccessAction`
(the ADR-015 three-action grant shape, opaque grant keys,
`deny_unknown_fields` serde) and the repo-id grammar
(`validate_repo_id`, backend.md §repo-id grammar).
- The error model: `RegistryError` (five variants) and `StorageError`
(three variants) — infrastructure errors are `Err`, protocol
failures are values (ADR-018 §7).
- The budget model `Limits` (ADR-009) with every default pinned;
`max_object_size` (the ingestion decompressed-size cap) added in
wave 5 pre-freeze.
- The session-substrate machinery shapes (ADR-005): the duplex
pkt-line layer (`Session`, `Sideband`, `RawTail`) and the
stateless smart-http framing (`StatelessRequest`,
`StatelessResponse`, `BodyReader`), plus the error taxonomy
(`SubstrateError`, `Service`).
- The in-memory backend test doubles (the contract-reference
convention — every protocol wave's conformance work runs its
machines against them).
**Wave 2 — the wire serves fetch** (upload-pack, dependency-free):
- The V2 advertisement (`advertise_v2` — exactly the honest
capability list, ADR-003) and the ls-refs machine (`serve_ls_refs`
/ `parse_ls_refs_args` — peel/symrefs/ref-prefix arguments, the
unborn-HEAD symref line; the wave-5 rider-1 capture corrected the
wire form to the upstream literal `unborn` token — a zero-oid HEAD
is a fetch want of the null oid for a real client).
- The negotiation ack loop (ADR-014): `serve_fetch_round` +
`parse_fetch_args` — no-`done` rounds answered with the exact
`acknowledgments` grammar (`ACK <oid>` per recognized have, `NAK`
only when no ACKs, never `ready`), the done round streaming
`closure(wants) − closure(recognized subset)` as the packfile
section, the want-less round answered empty — all behind the
`GitPackGen::common_haves` seam (exists + is-commit).
- The serving entries: `serve_duplex` (advertisement-once + command
loop under the wall clock) and `serve_stateless_post` /
`serve_stateless_advertisement` (one POST = one command
invocation, response rules: one-shot flush, never `0002`), both
taking the ADR-007 `AuthorizedRepo` marker by type.
- The authorization front: `authorize` (the ADR-011/015 policy
function — public read allows anonymous, write is always
authenticated + granted, unknown-repo ≡ unauthorized) and
`validate_ref_name` (ADR-013 §9's deny-list).
- The native transport (ADR-002/016): `GitAdapter` (the direct-ALPN
producer half), the git-daemon request-line grammar
(`parse_request_line` / `read_request_line`), and the
`channels/git/sub` channels open op (`register_openable`) —
unknown ≡ unauthorized collapsed to one generic denial at the gate.
**Wave 3 — receive-pack** (ADR-013 as amended by ADR-020):
- The V0 push advertisement (`advertise_v0`): caps NUL-attached on
the first ref line, the `capabilities^{}` sentinel for empty repos,
private repos advertise nothing (ACL first).
- The push request machine (`parse_push_request` /
`serve_receive_pack`): command grammar (create/delete zero-id
shapes), shallow rejection up front, `push-options` gated off v1,
`receive-pack`'s nothing-to-do shape.
- The bounded packless peek (ADR-020 §1): the `packless_peek` window
with the four ordering rules pinned (push-options consumed before
the peek; zero-bytes → packless CAS-only path; a partial pack
header is a malformed-push error, never a silent downgrade).
- The pack stream seam: `bridge_pack_source` (the async→blocking
relay, `PackSourceBridge` / `PackSourceFault`) — wire-side pack-size
budget, the raw-tail escape on both substrates — and
`GitPackIngest::prepare` (the prepare/apply split; the single CAS
home in `GitRefs::apply_updates`; every failure framed as the
`unpack ng` leg, never `Err`).
- The status report framing (ADR-013 §8): band-1-wrapped with its
inner flush when `side-band-64k` was selected, bare otherwise —
substrate-owned so handlers cannot get it wrong.
**Wave 4 — the consumer half + the op family**:
- `GitSession` (ADR-017 as amended by ADR-023): the typed git client
over gix-transport's git-daemon `Connection` on the alkcall
`BiStream` compat halves. `connect_direct` rides the ADR-016
request-line preamble; `open_via_channels` takes the post-open
stream. `ls_refs` + `fetch` speak the client side of the same V2
grammar we serve (full ack-loop negotiation, rounds as full
stateless commands, the done round's pack streamed to the
caller's sink); `push` is the hand-rolled send-pack (mirror of
ADR-013's server shapes — gitoxide has no send-pack), with the
client riders: a packless push holds the write side open until the
report (ADR-020 §3) and every push reads the full report before
the session could drop (ADR-023 §5). Client-direction budgets
fail closed typed.
- The `git/repo/*` call-op family (ADR-012 §3/§5, gate per
ADR-015 §3): `create` / `delete` / `update` / `get` as
`OperationSpec` + handler pairs over `Arc<dyn GitRegistryStore>`
(`register_repo_ops`), the PATCH semantics (review 002 R-3), the
error-code table with the unknown ≡ unauthorized collapse, create
seeding the caller's `{read, write, manage}` grants and minting
ownership.
**Wave 5 — validation & release readiness:**
- The session split (review 005 R-1): `session_fetch` / `session_push`
as flat sibling modules — zero public-API change.
- The ingestion decompressed-size cap (review 004 R-2,
pre-freeze): `Limits::max_object_size` — pack-declaration-
independent expansion bounding with incremental inflate (no
allocation sized by the declared size); a breach is pack-level
validation (`unpack ng`), never a session error.
- OQ-19 rider 2, the positive-ack capture: multi-have no-`done`
rounds ack each recognized have in request order and the done
round's boundary set equals the recognized subset — validated on
both serving substrates (disposition: validated, no retraction).
- OQ-19 rider 1, the unborn-HEAD real-client validation: `git
ls-remote` and a real clone of an unborn repo complete over both
substrate doors, the unborn HEAD symref line served per the
upstream literal-`unborn` emission (disposition: validated; the
machine-side flush-only session end landed with the capture — the
client's trailing flush is the quiet end, not a fault).
- OQ-19 rider 3, the real-client grammar tokens (validated against
git 2.43.0 over both substrate doors): `include-tag` accepted and
honored by omission (un-gated in the real client's V2 send path;
the pack is the want closure as generated); `session-id=` honestly
retracted (the advertisement never declares the capability, so a
conforming client never sends it; the fail-closed decline stays);
shallow/filter/server-option declined as unimplemented surface.
- The `fuzz/` harness: libFuzzer targets over the crate's ingress
surfaces (the pkt-line framings, the request-line grammar, the
negotiation command ingress, the repo-id grammar, the open-op
params) with the committed corpus gate joining the verification
list.
### Notes
- The `sha256` feature is a passthrough (wire capability line +
request-side validation) — not a storage-format offer; the pinned
default is sha1 (OQ-05).
[0.1.0]: https://git.alk.dev/alkdev/alkgit/releases/tag/alkgit-v0.1.0