Closes 10 audit findings from 4-agent wave (critic + security +
architect + validator) on v0.21.0.
CRITIC HIGH (5):
H1 s3_cloud::commit() was listing with delimiter='/' — nested
writes silently dropped from manifest hash. Added
list_recursive() (no delimiter), filter manifest-*.json from
hash input.
H2 S3Cfg access_key_env + secret_key_env were advertised in TOML
but never read. Wired via resolve_explicit_creds() with
aws-credential-types. Partial-set or empty-resolve → error.
H3 display::sanitize_display missing in detach.rs + mount.rs
(regression of v0.19.2 L9 ANSI injection fix). Applied at 8
print sites. 2 new integration tests.
H4 adapters/jsonmcp.rs RESTORED (was lost in earlier merge).
107 LOC shared module: load_json_or_empty / upsert_under_key /
remove_under_key / persist. claude_code 163→105, cursor 165→106,
zed 178→114. Unified error handling via ConfigParseError.
H5 ENV_LOCK shared across kei-store tests. New test_env.rs (24 LOC)
exposed under cfg(any(test, feature='s3')). github.rs +
s3_cloud/tests.rs + s3_smoke.rs all use shared mutex. Fixes
parallel-test race on KEI_STORE_S3_ENDPOINT.
SECURITY HIGH (2):
SEC-H1 scripts/install-actionlint.sh — added sha256 verify
(shasum/sha256sum) before extract. ACTIONLINT_SHA256_OVERRIDE
env var for CI injection. Per-platform constants marked
[UNVERIFIED: SKIP] pending live checksums.txt fetch (agent had
no WebFetch this session — user follow-up: paste from
https://github.com/rhysd/actionlint/releases/download/v1.7.12/checksums.txt).
SEC-H2 S3 SSRF/IMDS guard. validate_endpoint() rejects:
loopback (127/8, ::1, localhost), link-local (169.254/16,
fe80::/10), metadata hostnames (google/azure). Override via
KEI_STORE_S3_ALLOW_INTERNAL=1. HTTP rejected unless
KEI_STORE_S3_ALLOW_INSECURE=1. Custom endpoint now REQUIRES
explicit creds (no IMDS chain leak via third-party endpoint).
4 reject + 3 accept tests pass.
POLISH (3):
D1 docs/USB-BRAIN-GUIDE.md — ⚠️ WARNING block under Prerequisites:
exFAT/FAT32 NOT safe for multi-client attach (SQLite WAL needs
shared-mem mmap). Use ONE client at a time on those FSes.
New Troubleshooting entry 'SQLite corruption on mount-attach'.
D2 '~5 MB release binary growth' now labelled [estimate, E5 —
not yet measured] in CHANGELOG.md + s3_cloud/mod.rs header.
D3 scripts/validate-workflow-shas.sh exits 2 (not 0) when
UNVERIFIED_COUNT > 0 and GITHUB_TOKEN absent. Distinguishes
'network denied' from 'all good'.
REAL VERIFICATION (pasted by agent):
cargo check -p keisei -p kei-store: Finished (clean)
cargo test -p keisei --release: 30 passed 0 failed
cargo test -p kei-store --release: 10 + 9 passed (default features)
cargo test -p kei-store --features s3 --release:
31 + 9 + 6 = 46 passed (with s3)
bash -n scripts/*.sh: OK
regen-counts.sh --check: no drift
Constructor Pattern: largest new src 200 LOC (s3_cloud/mod.rs, at
limit). jsonmcp.rs 107 LOC. test_env.rs 24 LOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
21 KiB
21 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Entries are generated from the git history via
_primitives/_rust/kei-changelog (a conventional-commits walker).
Regenerate a single version block with, e.g.:
_primitives/_rust/target/release/kei-changelog \
--from v0.14.2 --to v0.15.0 --version v0.15.0 --update CHANGELOG.md
Unreleased
Work in flight on
feat/v0.16-changelog-genand follow-up branches. Only placeholders — no corresponding commits exist yet. Any line that ships must be replaced with the real commit summary before release.
Added
- primitives (v0.21 — keisei SSoT relocation +
Scopeenum):- Marker file relocated from
~/.claude/keisei-attached.tomlto~/.keisei/attached.toml.~/.claude/is Claude-Code-specific territory and should not host cross-adapter keisei state.config::read()performs a one-shot migration the first time it runs under v0.21: if the legacy file exists and the new location is empty, the marker moves over (new file written, legacy file deleted) and a stderr notice is emitted. Scopeenum (user/project) on theClientAdaptertrait. Adapters declaresupported_scopes();config_path(scope),attach(brain, scope),detach(brain_name, scope)are scope-aware. Claude Code and Cursor support both scopes; Continue and Zed are user-only.keisei attachgains--scope=<user|project>(defaultuser);keisei mountstays host-wide (Scope::Userfan-out by design).- Marker schema v3: each
[[attachments]]entry carriesscope = "user" | "project". Pre-v0.21 markers without the field default toScope::Usersilently. New error variantError::ScopeUnsupported { client, scope, supported }fires when a caller asks for a scope the adapter doesn't advertise.
- Marker file relocated from
- primitives (v0.21 —
kei-storereal S3 backend):S3CloudStore— functional S3 / R2 / MinIO / Wasabi backend viaaws-sdk-s3v1. GetObject / PutObject / ListObjectsV2 (paginated) / DeleteObject wired behind the existingMemoryStoretrait (sync-over-async via a single-thread tokio runtime). Enableskeisei attach s3://my-bucket/brain/as a real cloud-mount path, not just a local stub.- Opt-in feature flag
s3on thekei-storecrate — off by default so users who don't need cloud pay zero binary weight. Enabling adds tokio + hyper + rustls + aws-sdk-s3 (~5 MB release binary growth [estimate, E5 — not yet measured; would requirecargo build --releasebefore/after feature flag]). - AWS default credential chain honoured (env vars →
~/.aws/credentials→ IMDS). No new credential format; RULE 0.8 secrets-single-source unchanged. - Endpoint override for non-AWS S3-compat providers via
KEI_STORE_S3_ENDPOINTenv var (runtime) ors3.endpointinstore-config.toml(persistent). Path-style addressing auto-enabled when a custom endpoint is set (MinIO / some R2 configs). - "Branch" semantics: S3 has no native branching, so a branch is modelled as a key prefix (
<branch>/<path>).branch()sets the active prefix in-memory; defaultmain. - Factory auto-routes:
backend = "s3"+ features3+s3.bucketset → real cloud; otherwise falls back to the v0.14 local-manifest stub (still behindKEI_STORE_ALLOW_S3_STUB=1). - Path-traversal guard parity with
FilesystemStore: absolute and..-component paths rejected before keys are spliced.
- tests/battle: Docker-based clean-Ubuntu install test —
tests/battle/Dockerfile.install-test+verify.sh+battle-entry.sh+ README. Builds a freshubuntu:24.04image, runsinstall.sh --profile=<minimal|core|dev|full>under--yes, then asserts post-install counts (blocks ≥ 79, skills ≥ 39, top hooks ≥ 10,_libhooks ≥ 2), runshooks/_lib/test-gate.sh, and validatessettings.json. First real-world "does it work on a fresh machine?" signal — CI previously only ran--no-executedry-runs. v0.21 ship-blocker for any profile that regresses. - primitives (v0.20 — brain schema v2 + per-client hint):
- Brain schema v2 with per-platform
mcp_serverdispatch — a single brain directory can now host binaries for darwin-arm64/darwin-x64/linux-x64/linux-arm64/windows-x64 andkeisei attachpicks the right one automatically. Schema v1 (single string) still accepted for backward-compat. ClientAdapter::post_attach_hint()— per-client reload instruction, no more hardcoded Claude-Code string in the orchestrator.
- Brain schema v2 with per-platform
- primitives:
keiseiCLI MVP —attach <brain-path>+statussubcommands for mounting a portable exobrain directory into Claude Code. First step of the v0.18 exobrain architecture (multi-client adapter surface prepared; onlyclaude-codeadapter ships in MVP). - primitives (v0.19 — multi-client exobrain):
keisei mount <brain-path>— attach a brain to EVERY detected AI client in one shot (Claude Code + Cursor + Continue + Zed).keisei detach— remove the brain from every client recorded in the marker, preserving user's other MCP/context-server entries.keisei list-adapters— tabular dump of every registered adapter and whether it's detected on this host.- 3 new
ClientAdapterimplementations:cursor(.cursor/mcp.jsonproject-local or~/.cursor/mcp.jsonglobal),continue(~/.continue/config.{yaml,json}— YAML preferred, JSON fallback),zed(~/Library/Application Support/Zed/settings.jsonon macOS or~/.config/zed/settings.jsonon Linux, undercontext_servers). keisei-attached.tomlschema v2 — carries a list of[[attachments]](client_type + config_path) instead of a singleclient_type. v1 markers read transparently (auto-migrated in memory).- New error variants:
AdapterFailed { client, reason }andConfigParseError { path, reason }.
- Placeholder: CHANGELOG.md generation wired through
kei-changelog(this file). - Placeholder:
.github/workflows/release.yml— tag-driven multi-platform release. - Placeholder: pre-built-binary install path in
install.sh(KEI_SKIP_RUST_BUILD=1). - added:
kei-mcp-serversingle-binary compile for 5 platforms (linux/darwin/windows × x64/arm64 where available) viabun build --compile— v0.18 Phase 1 of the exobrain distribution architecture. Ships as bare binaries +.sha256sums on every GitHub release;install.shdetects a dropped binary at_primitives/_rust/target/release/kei-mcp-server-<os>-<arch>and skips bun/npm build. Opt-out viaKEI_SKIP_MCP_BUILD=1. See_ts_packages/packages/mcp-server/BUILD.md.
Changed
- Placeholder: plugin / block format refresh targeted for v0.16.0.
Security
- primitives/keisei (v0.19.2 audit polish — M1):
keisei-attached.tomlmarker is nowchmod 0o600on unix (Windows unchanged — no equivalent bit). The marker carries the resolvedbrain_pathand every attached client's config path; restricting it to owner-only closes the residual "other local user can enumerate attached brains" surface. - primitives/keisei (v0.19.2 audit polish — L9): every manifest-sourced string printed by
statusandattach(brain name, brain path, client/config paths) is now scrubbed throughdisplay::sanitize_display, which replaces every ASCII control byte (< 0x20or== 0x7F) with?. Closes the escape-sequence injection surface from a maliciousbrain.namelike"evil\x1b[2Jpayload"that would otherwise clear the user's terminal or rewrite already-printed lines. - primitives/keisei (v0.19.2 audit polish — L12):
manifest.tomlis now capped at 64 KiB (Error::ManifestTooLarge { size, max }). The check runs offfs::metadatabeforeread_to_stringso an attacker-supplied 1 GB file can't exhaust memory inside the toml parser. Legit manifests are ~1 KB; the cap is three orders of magnitude of headroom.
Fixed
- Placeholder: hook-bypass edge case follow-up to v0.15.1.
- primitives/kei-store (v0.21.1 audit wave, HIGH-1):
S3CloudStore::commit()now calls a newlist_recursive(prefix)helper (ListObjectsV2 withoutdelimiter) so every nested key under the branch — e.g.write("traces/x.jsonl", ...)— contributes to the manifest hash. The previous implementation calledlist("")which under the hood useddelimiter="/"and hid all sub-directory writes from the commit, silently breaking hash-stability.commit()ALSO strips any existingmanifest-*.jsonentries from the input so the hash is stable across repeated commits on unchanged data. - primitives/kei-store (v0.21.1 audit wave, HIGH-2):
S3Cfg::access_key_env+S3Cfg::secret_key_envare now wired through to the aws-sdk-s3 builder. When both are set, we resolve the named env vars into an explicitCredentialsprovider and overlay it on the SDK config. Partial configuration (only one of the two set) now returns an error rather than silently ignoring it. Previously both fields were dead — configured users were getting the ambient AWS default chain instead of the named pair. - primitives/kei-store (v0.21.1 audit wave, HIGH-5): all tests that mutate process env on
KEI_STORE_*vars now take a sharedtest_env::ENV_LOCKmutex (exposed undercfg(any(test, feature = "s3"))). Prevents cargo-test parallelism from racing multiple tests on the same env state.github.rsdedups onto the shared lock;s3_cloud/tests.rs+tests/s3_smoke.rsnow use it. - primitives/keisei (v0.21.1 audit wave, HIGH-3):
detach.rs+mount.rsnow scrub every manifest-sourced string (brain name, brain path, config path, client type, error reason) throughdisplay::sanitize_displaybeforeprintln!/eprintln!.status.rs+attach.rswere already compliant; this closes the L9 regression gap for the other two print sites. Two new integration tests (detach_sanitizes_control_chars_in_marker_fields,mount_sanitizes_control_chars_in_error_reason) assert source-level guard presence. - primitives/keisei (v0.21.1 audit wave, HIGH-4): extracted
adapters/jsonmcp.rs(~107 LOC) as the shared JSON merge/remove/persist helper used by theclaude-code,cursor, andzedadapters. All three adapters drop from ~170 LOC to ~105 LOC each and share a uniform error-surfacing contract (Error::ConfigParseError { path }rather than raw serde_json on parse failure).continue_adapter.rsis YAML-based and is unaffected. - security (v0.21.1 audit wave, H1):
scripts/install-actionlint.shnow verifies SHA-256 of the downloaded tarball before extraction. Per (OS, ARCH) hashes are pinned at the top of the script and documented as the output ofchecksums.txton the upstream release page. If a hash is markedSKIP(documented as[UNVERIFIED]pending live fetch), the installer prints a WARNING. Missingshasum/sha256sumis a hard exit 2 — refuses to install an unverified binary. Env overrideACTIONLINT_SHA256_OVERRIDE=<hex>lets CI inject the hash at runtime. - security (v0.21.1 audit wave, H2):
kei-store::s3_cloud::client::validate_endpointrejects loopback / link-local / metadata hosts (127.0.0.0/8,::1,169.254.0.0/16,fe80::/10,metadata.google.internal, etc.) and plain-HTTP URLs by default. Closes the SSRF / IMDS-leak surface where an attacker-controlledKEI_STORE_S3_ENDPOINTpointed athttp://169.254.169.254would cause the AWS default credential chain to sign requests against the instance metadata endpoint and leak IMDS creds. Env overrides:KEI_STORE_S3_ALLOW_INTERNAL=1(local MinIO / tests),KEI_STORE_S3_ALLOW_INSECURE=1(plain-HTTP). When a custom endpoint is set, explicitaccess_key_env+secret_key_envare REQUIRED — the default credential chain is no longer consulted for non-AWS endpoints. - docs (v0.21.1 audit wave, D1):
docs/USB-BRAIN-GUIDE.mdnow warns that exFAT / FAT32 are NOT safe for multi-client attach — SQLite WAL shared-memory mmap doesn't work reliably on those filesystems. Recommends APFS / ext4 / NTFS forkeisei mount. Troubleshooting entry "SQLite corruption on mount-attach" added with recovery steps. - docs (v0.21.1 audit wave, D2): the "~5 MB release binary growth" claim for the
s3feature is now labelled[estimate, E5 — not yet measured]in both CHANGELOG.md and thes3_cloudmodule doc-comment. Prevents over-claim until a realcargo build --releasebefore/after comparison is landed. - scripts (v0.21.1 audit wave, D3):
scripts/validate-workflow-shas.shnow exits 2 when UNVERIFIED pins exist AND noGITHUB_TOKENwas provided (rate-limit path). Previously silently returned 0 which masked incomplete verification in CI. - primitives/keisei (v0.19 audit hardening): close 3 Security HIGH + 3 Critic HIGH + 2 Critic MEDIUM findings. Path-escape guard on
mcp_server+memory/artifacts/manifests(absolute /../ canonical-mismatch →PathEscape); brain-name regex^[a-z][a-z0-9_-]{0,63}$(InvalidName); symlink-rooted brain inputs rejected (BrainIsSymlink— closes USB →$HOMEpivot); MCP-entry collision check across all 4 adapters (NameConflictinstead of silent clobber); dropped unusedrusqlitedep (no C toolchain tail);BrainPaths.{memory,artifacts,manifests}relaxed toOption<String>;$KEISEI_HOME/$HOMEresolver deduped intopaths.rsSSoT;fsx::write_atomicrewritten ontempfile::NamedTempFilefor Windows + cross-fs correctness; 5 adversarial integration tests added (16 total pass). - primitives/keisei (v0.19.2 polish): dropped unused
ClientAdapterimports frommount.rs+detach.rs;Error::NotAttachedandAttachRecord::has_clientnow carry explicit#[allow(dead_code)]markers documenting that they're reserved for future callers / test-only respectively.cargo check -p keiseiis warning-clean; integration suite is 19/19 pass (3 new:marker_file_has_0600_perms_on_unix,status_sanitizes_control_chars_in_brain_name,manifest_too_large_rejected).brain.rsmodule-level doc-comment now lists the v0.19 invariants (path confinement / symlink reject / name regex / manifest size cap) and flags schema v2 as v0.20.
Security
- Pinned all GitHub Actions (
ci.yml,release.yml) by full commit SHA to defend against CVE-2025-30066-class supply-chain attacks via mutable tag re-pointing. - Removed
|| bun installfallback fromrelease.ymlbuild-mcp-binary job — lockfile is now strictly REQUIRED (H4 audit finding). - Added
.github/dependabot.ymlfor weekly SHA update PRs on github-actions, npm, and cargo ecosystems. - v0.20.1 — workflow validation defense-in-depth: motivated by the 2026-04-22 incident where
dtolnay/rust-toolchain@3c5f7ea...SHA-pinned a specific Rust version (1.94.1 branch tip) instead of "install current stable", breaking CI for 4 jobs. Added three gates against the incident class:scripts/install-actionlint.sh(pinned v1.7.12 installer, macOS-arm64 + linux-x64),scripts/lint-workflows.sh(actionlint runner, advisory if binary missing),scripts/validate-workflow-shas.sh(git-ls-remote everyuses: <repo>@<sha40>pin; exits 1 onSHA MISSING, soft-continues on network errors with[UNVERIFIED]),scripts/pre-commit-workflow-lint.sh(symlink-to-install pre-commit hook, fires only when workflow files are staged), and newworkflow-lintCI job running the two validators on every push + PR.
0.15.0 — 2026-04-22
Added
- primitives:
kei-artifacttyped handoff pipeline (BMAD-style doc passthrough) (3f303b7) - blocks: 5 cognitive mode blocks + 2 manifest wirings (
fdfc690)
0.14.2 — 2026-04-22
Added
- hooks: runtime controls via
KEI_DISABLED_HOOKS+KEI_HOOK_PROFILE(v0.14.2) (1a448e8)
Removed
- genesis-scan from public kit (internal tool, Bundle-only) (
268226b)
0.14.1 — 2026-04-22
Added
- ci: GitHub Actions workflows +
.claude/worktreesgitignore (407e8b7)
Changed
- readme + install: reconcile all count drift (F4 RELEASE BLOCKER) (
0199fd4) - rust: misc schema/main refactor in 8 crates (assorted CP splits) (
61448b9) - mock-render: split
main.rs227 LOC into 4 cubes (F5a Constructor Pattern) (ad5977d)
Fixed
- kei-auth: remove
--keyCLI flag (F12 HIGH —/proc/cmdlineleak) (b449587) - kei-refactor-engine: retract 'git apply-ready' claim (F1 RELEASE BLOCKER) (
f50ef43) - kei-store: path-traversal guard (F2 RELEASE BLOCKER) + S3 stub gate (F7) + GitHub RULE 0.1 guard (F8) (
ad9c53f)
0.14.0 — 2026-04-22
Added
- primitives: 10 Rust crates extracted from LBM (Genesis-scrubbed) (
a5e6649) - ts-packages: 6 TS packages — MCP server + 5 external-API adapters (
7b647d5)
Changed
- rust-core: Constructor-Pattern splits in
kei-router+kei-auth(afed921)
0.13.0 — 2026-04-22
Added
- integration: deep-sleep wired into MANIFEST + sleep-setup Phase 3b + README (
bcd80f6) - primitives: 4 Rust crates for deep-sleep —
conflict-scan,refactor-engine,graph-check,store(0f75493) - skills:
/onboardauto-project-analyze with 3-mode apply (full-auto / step-by-step / full-manual) (1396139)
Changed
- readme: "Why Rust, not Python" paragraph in author note (
92c918a) - readme: clarify "my sample, not claim of originality" in author note (
47d2448) - readme: add "double sorry" disclaimer in author note (
3d5d768) - readme: move "From the author" to opening, expand with transformer-error context (
fd67315) - readme: add "From the author" note (
b103c3d)
0.12.0 — 2026-04-22
Added
- integration: Phase A incubation wired into trigger + install + README (
d72de64) - skills:
/sleep-on-it6-phase wizard +kei-sleep-queueCRUD + incubation prompt (30df6cb)
0.11.0 — 2026-04-22
Added
- integration:
--with-sleep-syncflag + README Cloud REM sync section (1dd05c6) - skills:
/sleep-setup5-phase wizard (click + 1 free-text URL) (b658f81) - hooks:
session-end-dumpcallskei-sleep-syncafter ingest (1ab39d5) - primitives:
kei-sleep-setupwizard +kei-sleep-synchelper + trigger template (4fdaab6)
0.10.0 — 2026-04-22
Added
- integration: register
genesis-scanin MANIFEST core+full + README +install.shsizing (93ba0a0) - hooks:
git-pre-commit-genesis— template for repo symlink into.git/hooks/pre-commit(670af3f) - primitives:
genesis-scanRust — patent-IP leak detector (CI / pre-commit) (5db8548) - integration: wire
kei-memoryinto MANIFEST + settings-snippet + README for v0.10 (0b5da5a) - skills:
/self-audit5-phase triage pipeline (334a867) - hooks: 3 self-audit triggers — stop / milestone / error-spike (
a5c3896) - primitives:
kei-memoryRust crate — offline session analyzer (Genesis-clean) (448fc07)
0.9.1 — 2026-04-21
Added
- install: interactive menu (whiptail / dialog / plain) + confirm screen +
--yes/--no-execute(4809269)
0.9.0 — 2026-04-21
Added
- install: modular profiles +
--add/--remove/--listincremental install (b1b8de0) - primitives:
MANIFEST.toml— SSoT for 21 primitives + 6 profiles (764a999)
Changed
- readme: install profiles table + migration note for v0.9.0 (
47931a3)
BREAKING: default install profile is now
minimal(wasfull). Re-run with--profile=fullto preserve prior behaviour.
0.8.0 — 2026-04-21
Added
- install: copy
_primitives/+ build Rust workspace; registeragent-fork-logger+site-wysiwydhooks (b0d9389) - hooks:
site-wysiwyd-checkPostToolUse(Edit | Write) drift advisory (c2041b4) - skills:
/site-createpipeline (phases 0–4 — phases 5–6 deferred) (839ae57)
Changed
- compose-solution: prior-art grep paths + phase-5 cross-refs for 10 pipelines + 21 primitives (
f664cbc) - readme: v0.8.0 — 73 blocks / 34 skills / 21 primitives / 6 hooks / 11 bridges + pipelines section (
ed7d566)