Commit graph

5 commits

Author SHA1 Message Date
Parfii-bot
bc7e099697 refactor(v0.22): kei-store AsyncBackend trait + shared tokio runtime (Track B)
Extracts an AsyncBackend trait in kei-store so future GCS/Azure/Bunny
backends implement 5 async fns, not re-invent the sync-over-tokio
bridge. Closes architect P1 + P2 findings.

NEW src/async_backend.rs (189 LOC):
  - trait AsyncBackend with get/put/list/list_recursive/delete/exists
  - AsyncBackendStore<B: AsyncBackend> — generic MemoryStore impl
    (sync-over-async via shared runtime)
  - shared_runtime() -> &'static Runtime via OnceLock
    (multi-thread, 2 workers, enable_io+enable_time)
  - path helpers (validate_rel, short_hash, is_manifest_key) moved
    here as single source of truth

NEW src/s3_cloud/backend.rs (120 LOC):
  - S3AsyncBackend impl of AsyncBackend — 5 async fns using the
    existing aws-sdk-s3::Client

MODIFIED src/s3_cloud/mod.rs (200 → 43 LOC):
  pub type S3CloudStore = AsyncBackendStore<S3AsyncBackend>;
  Thin re-export + inherent new(cfg) constructor.
  Doc-header documents the extension seam: 'adding GCS = impl 5 async fns'.

MODIFIED src/s3_cloud/keys.rs (66 → 40 LOC): compat shim — re-exports
  validate_rel / short_hash / is_manifest_key from async_backend.
  Old call-sites + 4 unit tests unchanged.

Deps: async-trait = 0.1 added under s3 feature; tokio now has
  rt-multi-thread feature too.

FIXES N=2 Store footgun: prior impl created a current_thread Runtime
  per instance — 2 instances in one process = 2 runtimes, block_on
  panic if caller is on another runtime. Shared multi-thread runtime
  via OnceLock means N instances all share 2 workers.

REAL VERIFICATION (agent-pasted):
  cargo check -p kei-store: clean
  cargo check -p kei-store --features s3: clean
  cargo test -p kei-store --release: 10+9+0 = 19 passed
  cargo test -p kei-store --features s3 --release: 38+9+6 = 53 passed
    (+7 vs baseline 46)

Tests added (7):
  async_backend::tests::shared_runtime_is_singleton
  async_backend::tests::validate_rel_rejects_absolute
  async_backend::tests::validate_rel_rejects_parent
  async_backend::tests::short_hash_deterministic
  async_backend::tests::is_manifest_key_matches_format
  s3_cloud::tests::async_backend_shared_runtime_handles_two_store_instances
  s3_cloud::tests::async_backend_runtime_is_multi_thread

Public API preserved: S3CloudStore::new / .branch / .current_branch /
  .key / .backend_name. Factory + integration tests untouched.

Pre-existing: list_inner 38 LOC (moved verbatim from mod.rs, not
  refactored per Core Rule 3).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:06:50 +08:00
Parfii-bot
f12eb9f83c fix(v0.21.1): wave-audit consolidated — 5 critic HIGH + 2 security HIGH + 3 polish
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>
2026-04-22 20:03:17 +08:00
Parfii-bot
e5cd0d6790 feat(v0.21): kei-store real S3 backend behind opt-in 's3' feature flag
Promotes S3 from MVP stub to functional via aws-sdk-s3. Default builds
unchanged (zero new deps). Feature flag ensures users who don't need
S3 don't pay the ~5MB binary / C-toolchain cost.

Cargo.toml: new [features] s3 = [...] gating 4 optional deps:
  aws-sdk-s3 = 1.130.0
  aws-config = 1.8.16 (with behavior-version-latest)
  tokio = 1.52.1 (current-thread runtime, no multi-threaded bloat)
  bytes = 1 (S3 body passthrough)

s3_cloud/ module (4 files, Constructor Pattern):
  mod.rs (190 LOC) — S3CloudStore + MemoryStore trait impl
  client.rs (81 LOC) — aws-config builder, KEI_STORE_S3_ENDPOINT
    override for R2 / Wasabi / MinIO / any S3-compat
  keys.rs (60 LOC) — path-traversal guard + DJB2 hash helper
  tests.rs (63 LOC) — builder + prefix + key-guard unit tests

Factory routing (factory.rs):
  with 's3' feature + bucket URL → S3CloudStore (real network)
  without 's3' feature → S3Store stub (existing MVP, preserved)

Security posture:
  - Branch-prefix isolation rejects  traversal at keys.rs layer
  - aws-config default credential chain (env → ~/.aws → IMDS);
    no bespoke credential handling
  - rustls, not OpenSSL (matches existing crate tree)

Tests: 22 existing + 11 new (4 keys + 3 client + 5 mod + 5 smoke)
  cargo test -p kei-store (default features): 9 passed
  cargo test -p kei-store --features s3: 22 + 9 + 5 = 36 passed
  cargo clippy -p kei-store --features s3: clean

Real stdout verified for all verify criteria. No fabrication.

MANIFEST.toml [primitive.kei-store] deps updated to reflect feature
opt-in model.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:59:11 +08:00
Parfii-bot
ef95bf2a7c fix(kei-store): path-traversal guard (F2 RELEASE BLOCKER) + S3 stub gate (F7) + GitHub RULE 0.1 guard (F8)
F2: filesystem.rs + s3.rs 'fn full' now Result<PathBuf>, rejects absolute + ParentDir components. 7 new unit tests.
F7: factory.rs rejects 'backend=s3' without KEI_STORE_ALLOW_S3_STUB=1; backend_name() = 's3-local-stub'.
F8: github.rs push() blocks github.com unless KEI_STORE_ALLOW_GITHUB_PUSH=1 (RULE 0.1).
2026-04-22 13:36:17 +08:00
Parfii-bot
19ee220e0a feat(primitives): 4 Rust crates for deep-sleep — conflict-scan, refactor-engine, graph-check, store
- kei-conflict-scan: rules/hooks/blocks/orphans/CP detection (6 tests)
- kei-refactor-engine: plan-mode + advisory patch format, zero-conflict guarantee (5 tests)
- kei-graph-check: wikilinks/handoffs/block-refs validator (4 tests)
- kei-store: trait + 5 backends (filesystem/github/forgejo/gitea prod, s3 stub) (8 tests)

1916 LOC Rust total; all files <200 LOC; 23/23 tests pass.
2026-04-22 08:28:22 +08:00