{
 "generated_at": "2026-10-08T00:00:00Z",
 "project": "Byteanvil",
 "current": "2.4.1",
 "note": "Release history as data; each entry has a page at /rel-<version-with-dashes>.html and an Atom feed at /feed.xml.",
 "releases": [
  {
   "version": "2.4.1",
   "date": "2026-10-06",
   "page": "rel-2-4-1.html",
   "store_format_change": false,
   "summary": "A maintenance build: the progress counter no longer stalls on large uploads, and prune stops double-counting shared payloads.",
   "notes": [
    "Fixed a stall in push --progress where the byte counter stopped updating after the first 2 GiB while the upload itself continued normally.",
    "config --explain now names the exact file path a value came from instead of printing the layer name alone.",
    "Prune no longer reports a reclaimable total that counts a payload twice when two versions share an object-storage key."
   ]
  },
  {
   "version": "2.4.0",
   "date": "2026-09-14",
   "page": "rel-2-4-0.html",
   "store_format_change": false,
   "summary": "Promotion notes, a stricter verify, and the union reading of combined retention rules.",
   "notes": [
    "Added promote --note, stored on the history entry and shown by show --history.",
    "verify gained the intermediate canonical-form check, so a reformatted manifest is reported instead of quietly passing on its signature alone.",
    "Retention learned the union semantics for channels defining both keep and older_than; previously the age rule won and kept less than operators expected.",
    "Server reloads retention and read_only on SIGHUP."
   ]
  },
  {
   "version": "2.3.0",
   "date": "2026-07-14",
   "page": "rel-2-3-0.html",
   "store_format_change": false,
   "summary": "Archive imports keep their original dates, and large object-storage pulls stopped eating memory.",
   "notes": [
    "import --mtime-as-created for archives whose chronology matters.",
    "Pattern matching reports versions that fail SemVer parsing as warnings and skips them rather than guessing a shape.",
    "Payload reads from object storage are streamed, cutting peak memory on a 400 MiB pull from roughly 430 MiB to 12 MiB."
   ]
  },
  {
   "version": "2.2.0",
   "date": "2026-05-26",
   "page": "rel-2-2-0.html",
   "store_format_change": false,
   "summary": "Pinning arrives, and an empty selection stops masquerading as success.",
   "notes": [
    "Pinning: promote --pin and --unpin, with a PIN column in ls.",
    "prune --include-pinned added and immediately restricted to a single named artifact.",
    "Exit code 7 introduced for an empty selection, which used to be reported as a plain success."
   ]
  },
  {
   "version": "2.1.0",
   "date": "2026-04-07",
   "page": "rel-2-1-0.html",
   "store_format_change": false,
   "summary": "Object storage as a payload backend, and tokens that can push without being able to release.",
   "notes": [
    "Object storage over HTTP as a payload backend via server.store; manifests stay on the local filesystem.",
    "Tokens carry a permission set, so a build account can push without being able to promote to release.",
    "A token found in a repository .anvil.toml is now a startup error."
   ]
  },
  {
   "version": "2.0.0",
   "date": "2026-02-11",
   "page": "rel-2-0-0.html",
   "store_format_change": true,
   "summary": "Signing, the in-store trusted-key list, and store format 4 with its independently verifiable directories.",
   "notes": [
    "Ed25519 manifest signing, the trusted-key list inside the store, and key new / key trust / key untrust.",
    "Store format 4: the manifest moved from a sidecar index into the version directory, making each directory independently verifiable. serve --migrate-store upgrades a 1.x store in place.",
    "Exit codes 3 and 4 split apart; both conditions previously returned 3.",
    "Removed the undocumented /v0/blob route that predated the manifest model."
   ]
  },
  {
   "version": "1.4.2",
   "date": "2026-01-14",
   "page": "rel-1-4-2.html",
   "store_format_change": false,
   "summary": "Two crash and duplication fixes found in the field.",
   "notes": [
    "Fixed a crash on a manifest whose meta map contained an empty-string key.",
    "Client retries no longer fire on a coordinate conflict, which could publish a duplicate history entry."
   ]
  },
  {
   "version": "1.4.0",
   "date": "2025-12-09",
   "page": "rel-1-4-0.html",
   "store_format_change": false,
   "summary": "Directory imports, date-ranged listings, and a longer default timeout for wide-area pushes.",
   "notes": [
    "import --from-dir --pattern for migrating an existing build share.",
    "ls --since accepting ISO dates and relative spans.",
    "Default request timeout raised from 30s to 60s after reports from sites pushing disk images over a wide-area link."
   ]
  },
  {
   "version": "1.3.0",
   "date": "2025-09-23",
   "page": "rel-1-3-0.html",
   "store_format_change": false,
   "summary": "Machine-readable output on every command, and the columnar format marked unstable.",
   "notes": [
    "--json on every command, with one object per line.",
    "show --history expands the promotion log.",
    "The columnar output was documented as unstable, since two downstream scripts had already broken on a width change."
   ]
  },
  {
   "version": "1.2.0",
   "date": "2025-07-01",
   "page": "rel-1-2-0.html",
   "store_format_change": false,
   "summary": "A macOS build, metadata written beside pulls, and a refusal to start on a world-readable key.",
   "notes": [
    "macOS arm64 builds published alongside the two Linux architectures.",
    "pull --manifest writes the metadata beside the payload.",
    "Server refuses to start on a world-readable key file, exiting 5."
   ]
  },
  {
   "version": "1.1.0",
   "date": "2025-04-15",
   "page": "rel-1-1-0.html",
   "store_format_change": false,
   "summary": "Retention became configuration, and prune became dry-run by default.",
   "notes": [
    "Retention policy moved into configuration; it had been compiled-in constants.",
    "prune became dry-run by default. This was the first change requested by someone outside the original team, and it was the right request."
   ]
  },
  {
   "version": "1.0.0",
   "date": "2025-02-20",
   "page": "rel-1-0-0.html",
   "store_format_change": true,
   "summary": "The first public release under BSD-2-Clause, with store format 1 and its compatibility promise.",
   "notes": [
    "First public release, under BSD-2-Clause.",
    "Store format 1 declared stable, with a written compatibility promise: a client never refuses to read an older store.",
    "Single statically linked binary for both roles; the separate client package was dropped."
   ]
  },
  {
   "version": "0.5.0",
   "date": "2024-12-16",
   "page": "rel-0-5-0.html",
   "store_format_change": false,
   "summary": "Reverse-dns names and SemVer validation replaced the free-form strings of the first months.",
   "notes": [
    "Reverse-dns artifact names replaced free-form strings, after two teams independently created an artifact called gateway.",
    "SemVer validation on push, rejecting what had until then been accepted as a version."
   ]
  },
  {
   "version": "0.3.0",
   "date": "2024-09-05",
   "page": "rel-0-3-0.html",
   "store_format_change": false,
   "summary": "The three fixed channels and the append-only promotion log appeared here.",
   "notes": [
    "Channels introduced with the fixed set of three, replacing a boolean named released.",
    "Promotion recorded as an append-only log rather than an overwrite of the channel field."
   ]
  },
  {
   "version": "0.2.0",
   "date": "2024-07-23",
   "page": "rel-0-2-0.html",
   "store_format_change": false,
   "summary": "Digests and server-side immutability: a repeated coordinate now fails.",
   "notes": [
    "SHA-256 digests computed and stored; the first version only recorded file sizes.",
    "Immutability enforced server-side, so a repeated coordinate fails with exit code 2."
   ]
  },
  {
   "version": "0.1.0",
   "date": "2024-06-11",
   "page": "rel-0-1-0.html",
   "store_format_change": false,
   "summary": "The first tagged build of the internal tool: upload, list, download, port 8711.",
   "notes": [
    "First tagged build of the internal tool started in April 2024: upload a file, list what is there, download it again.",
    "Default port fixed at 8711, which has not moved since."
   ]
  }
 ]
}