# Changelog All notable user-visible changes are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses semantic versioning. Pre-1.0 releases may change commands with a clear entry here, as described in [docs/COMPATIBILITY.md](COMPATIBILITY.md). ## [Unreleased] ### Added - `sindook rewrap` gains incremental slot edits. `-keep` preserves the existing slots, passphrase slots included, without re-entering them, while `-r`/`-R`/`-new-*` append new ones, and the drop flags remove matching slots: `-drop-slot N` (the numbering `inspect` prints), `-drop-i IDENTITY`, `-drop-p`, `-drop-passfile FILE` (every slot that opens under the named credential), and `-drop-pass-slots` (every passphrase slot). Drop flags imply `-keep`; each selector must match at least one slot, and an edit may not leave a file with no slots. The `-deep` flag cannot combine with `-keep`/`-drop-*` because a fresh file key invalidates every kept slot. On v1 input `-keep` re-creates the opening credential as a v2 slot, upgrading the file in place. The library API is `box.RewrapEdit` taking a `box.SlotEdit`; kept slots are carried verbatim (a wrap is bound to the file nonce and the slot's own public parameters) and the payload is copied untouched, so every edit is a fast-mode rewrap with the same revocation caveat. - `docs/DESIGN_RATIONALE.md` records which format v2 decisions follow the published literature and which are project choices, with primary sources. The docs also state the slot-role boundary explicitly: recipient slots are the access-control layer, passphrase slots are for local unlock, escrow, and recovery. ## [v0.11.1] - 2026-08-31 ### Added - `sindook rotate -i IDENTITY (-to RECIPIENT)... [-deep] [-jobs N] [-glob PATTERN]... [-json] DIR|FILE...`, bulk retirement of one identity. Every candidate file is opened with the identity being retired; files it opens are rewrapped to exactly the `-to` recipients, files it cannot open are reported as `skipped` with the reason, and a failed rewrap is `failed` and leaves the original file untouched. Directory operands are walked for `*.sindook` files, `-jobs` reuses the bounded worker pool from `verify`, and `-json` reports one array of `{file, status, error?}` with status `rotated`, `skipped`, or `failed` (documented in docs/COMPATIBILITY.md). The exit code is non-zero only when a rewrap failed; skipping is not a failure. Fast mode inherits rewrap's limits: it does not revoke recipients who already hold a copy of the old file, and `-deep` re-encrypts under a fresh file key. - Running `sindook` with no command now leads with the three-command quick start (`init`, `seal`, `open`) and states that the tool is command-driven, before the full command list. Human-facing text only; exit codes and `-json` are unchanged. ## [v0.11.0] - 2026-08-31 Never released. The tag was cut from a tree whose windows-latest CI job failed: a new rotate test asserted walk output with a hard-coded path separator. The release pipeline stopped at its CI gate and published nothing; tags are immutable, so no v0.11.0 release exists. The module proxy serves the tag and its code is functional (the failure was in a test, not the tool); everything below first shipped in v0.11.1. ## [v0.10.0] - 2026-08-28 ### Added - `sindook scan`, a read-only cryptographic posture audit in two modes. `scan tls HOST[:PORT]...` checks certificate expiry and key strength, chain and hostname validity, deprecated TLS 1.0/1.1 acceptance (RFC 8996), and whether the server supports a hybrid post-quantum key exchange (X25519MLKEM768 or the SECP+ML-KEM groups); a normal handshake proves reachability before the hybrid-only probe runs, and inconclusive probes are reported as inconclusive instead of guessed. Ports that upgrade with STARTTLS are not supported; scan implicit-TLS ports. `scan files [PATH...]` finds unencrypted private keys, key files with permissive file modes (best effort, platform dependent), expired or soon-expiring certificates, and weak key sizes in commonly named key and certificate files. Findings carry remediations; endpoints that upgrade with STARTTLS, and cipher-suite inventories, are out of scope by design — use a dedicated tool such as testssl.sh alongside scan. Scan endpoints you operate or are authorized to assess. `-json` follows the doctor report envelope, documented in docs/COMPATIBILITY.md. - Verification baselines. `sindook verify -save BASELINE` records every verified file (path, sealed-file SHA-256, size, timestamp) in a JSON baseline; a later `sindook verify -baseline BASELINE` re-verifies and reports unchanged files, files whose sealed bytes changed, new files, and baseline entries missing from disk. With `-baseline` and no file operands, exactly the recorded set is verified. Baseline drift is report-only: only failed decryption changes the exit code. `-json` output gains optional `sha256`, `size`, and `baseline_sha256` fields, and the baseline format follows the same additive, versioned policy as the config file (docs/COMPATIBILITY.md). - Concurrent verification. `sindook verify -jobs N` checks up to N files at once; the default is up to 4 workers for multiple files and a single worker for one file or stdin. Results, `-json` output, `-save` baselines, and the exit code are identical to a serial run; the per-file progress meter appears only in single-job runs. ## [v0.9.0] - 2026-08-27 ### Added - Recipient groups. `sindook contacts group add team alice bob` saves a named recipient list, and `sindook seal -r @team` (or `sindook rewrap -r @team`) seals to every member, deduplicated, in sorted member order. Groups list saved contacts only (no nesting) and share the contact namespace, so a name is never both; removing a contact that a group lists is refused until the group is repaired. `contacts group list [-json]`, `show`, `add-member`, `remove-member`, and `remove` manage them. The config file gains an additive `groups` section: older sindook binaries ignore it, and configs without it load unchanged. - `sindook config` for scripted inspection and change of the managed configuration: `config get default-identity`, `config set default-identity PATH` (validated to exist, stored as an absolute path), `config unset default-identity`, and `config list [-json]`. - Public Go library API. The sealing engine moved from `internal/` to the top-level `box` package: `github.com/ruddro-roy/sindook/box` exposes `Seal`, `SealRecipient`, `SealPassphrase`, `Open`, `Rewrap`, `Inspect`, and `SelfTest`, with `github.com/ruddro-roy/sindook/xwing` as the key package. The stability policy is in docs/COMPATIBILITY.md. Code that imported the old `internal/box` path must switch to `github.com/ruddro-roy/sindook/box`. ### Changed - Continuous fuzzing now builds all fifteen declared fuzz targets (a compiler-wrapper quirk silently skipped targets whose names prefix another fuzz function), runs daily with a persistent, pruned corpus on the `corpora` branch, and fails the workflow if corpus storage stops advancing. ## [v0.8.1] - 2026-08-19 (the first published 0.8.x release; the v0.8.0 tag exists but was never released, see below) ### Added - One-line installs. `curl -fsSL .../scripts/install.sh | sh` on Linux and macOS and `irm .../scripts/install.ps1 | iex` on Windows install the latest verified release without administrator rights. Both scripts read nothing from standard input or the console, so piping is safe, and both still support download-and-run with `--version` pinning. - Compression: `sindook seal -z` compresses with gzip before encrypting and `sindook open -z` reverses it. A 1.5 MB server log seals to a few kilobytes. Armor and rewrap work on compressed files unchanged. The sealed-file format is unchanged; compression wraps the plaintext above the encryption layer, so nothing about the content is revealed beyond the compressed length. Opening a compressed file without `-z` writes the raw gzip stream, and opening an uncompressed file with `-z` fails with a message that names the flag. - Decompressed-size control: `open -z` and a new `verify -z` cap gzip expansion at 1 TiB by default, adjustable with `-max-decompressed` (accepts `2G`, `512MiB`, or a byte count; `0` means unlimited). A hostile archive that tries to expand past the cap fails with a clear error and no partial output is kept. `verify -z` additionally proves a compressed archive is fully recoverable, gzip checksum included. - Default identity in daily commands. After `sindook init`, `seal`, `open`, `verify`, and `rewrap` use that identity automatically when no credential flag is given, printing which identity they used. Explicit `-i`, `-r`, `-p`, and `-passfile` flags keep their exact prior meaning, and `SINDOOK_CONFIG_DIR` pointed at an empty directory restores the old fail-closed behavior for scripts that need it. - First-run hints. Missing-credential errors now teach the next command (`sindook init`), `open` with the wrong identity on a recipient file appends a `-p` suggestion, and `rewrap` without new slots names the flags that add them. ### Fixed - Decompression deadlock. `open -z` on a file whose gzip data is corrupt past the 64 KiB pipe buffer blocked forever once the pipe filled, because nothing closed the reader end after the decompressor stopped. The reader end is now always closed on every exit path, the compressor pipe is torn down when sealing fails, and a regression test fails on timeout if the deadlock returns. - Error priority after early decompression failure: the real cause (a corrupt stream or the size cap) is reported instead of the internal pipe teardown error. - The README pinned `go install ...@v0.7.1`, a release that was never published (latest was v0.7.0), so the command failed for users. ### Changed - README rewritten around a three-command quickstart with the one-line install first, a when-to-use section, and a comparison against age and GPG. Documentation wording reviewed: no em dashes, no author name. - Man pages updated for `-z`, `-max-decompressed`, `verify -z`, the credential defaults, and the new examples. - Corrected release history wording: v0.7.1 was prepared on main but its tag was never pushed and no release was published, so every v0.7.1 claim in the documentation was wrong. Its changes are part of v0.8.1. ## v0.8.0 (tagged, never released; use v0.8.1 or later) The v0.8.0 tag was pushed with all of the changes in v0.8.1 above, but its release workflow failed the CI gate before anything was published: two version tests expected the source-tree dev default and did not account for a tagged checkout, where Go's build info carries the tag and the release version correctly wins. Tags are immutable, so the fix and the release ship as v0.8.1. The v0.8.0 tag has no artifacts and must not be installed from; nothing about the sealed-file format differs. ## v0.7.1 (prepared, never tagged, never published) A v0.7.1 recovery release was prepared on main to supersede v0.7.0 without moving the public v0.7.0 tag, but its tag was never pushed and no GitHub release exists for it. Do not reference v0.7.1 in install commands or documentation. The prepared changes below shipped in v0.8.1 instead: ### Added - Version resolution via Go module build info: a binary installed with `go install github.com/ruddro-roy/sindook/cmd/sindook@v0.7.1` reports `sindook 0.7.1` instead of the source-tree dev default. Release builds (linker-stamped) report the exact tag, and dev builds stay visibly `0.7.1-dev` with commit provenance. - `contacts list` prints short `sha256:` fingerprints (first 16 bytes of SHA-256 over the decoded public key) instead of full keys; `contacts show NAME` and `contacts list -json` continue to print full keys. - `doctor` remediations are executable: a missing default public key now points at `sindook pubkey @default > IDENTITY.pub`. - Compatibility fixtures produced by the released v0.6.0 binary (`internal/box/testdata/v060-*.sindook`), pinned into the test suite so every future release proves it still opens v0.6.0 files. - A version-consistency check script (`scripts/check-version-consistency.sh`) that verifies man pages, the dev-default version, packaging manifests, and README install commands agree with a release version; wired into CI. - Documentation: product contract expanded ([docs/COMPATIBILITY.md](COMPATIBILITY.md)), v1 readiness checklist ([docs/V1_READINESS.md](V1_READINESS.md)). ### Changed - X-Wing key lifecycle is concurrency-safe: `Wipe` is idempotent, drops expanded key material, serializes with `Seed` and `Decapsulate`, and use after `Wipe` fails closed. - The unsupported-memory-lock platform check in `sindook doctor` is now a `warning` (previously `ok`), so the report honestly signals that key material may reach swap; warnings are not fatal. - Release pipeline is gated and draft-first: full CI must pass on the tagged commit before the draft release is created, and a verify job re-checks checksums, the Sigstore signature, the SBOM, and build provenance before the draft is promoted to public. - winget packaging moved to multi-file manifests; the installers' checksum verification fails closed on a missing or mismatched entry. - Source-tree dev default bumped to `0.7.1-dev`; man pages carry `sindook 0.7.1`. ### Fixed - `doctor` no longer suggests a `pubkey -i @default` invocation that the CLI would reject; the remediation uses the accepted positional form. - The version resolver now honors a real linker-stamped release version before tagged-module build info, matching the documented release-build precedence. - `doctor` and `selftest` now use the same resolved version as `sindook version`, so tagged module installs and linker-stamped release binaries do not report a stale source-tree dev version in health output. - Package manifest checks now fail when a manifest declares one version but points at another version's release URLs. ## v0.7.0 (2026-08-16) Public release. See the [v0.7.0 GitHub release](https://github.com/ruddro-roy/sindook/releases/tag/v0.7.0) for its notes; it was superseded by v0.8.0 after a prepared v0.7.1 recovery release turned out to have never been tagged or published. ## Historical releases - v0.7.0: public release; superseded by v0.8.0 (a prepared v0.7.1 was never tagged or published). - v0.6.0: exit code `3` split for authentication failures, memory-lock downgrade below 96 MiB `RLIMIT_MEMLOCK` to avoid CI OOM, FreeBSD memory locking. - v0.5.0 and earlier predate this changelog; see the Git history and the GitHub releases for details.