Skip to content

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 24 Jul 14:01

ken v1.2.1 — memory campaign + security & correctness hardening

The biggest release since 1.0. Three strands came together: a memory campaign that gives you real control over what ken indexes and how much RAM it holds, a full security & correctness review (two process-kill bugs and seven majors, each fixed with a regression test), and bounded, cancellable shutdown.

No breaking changes. Every public 1.0 signature is unchanged; new API and env vars are purely additive. Upgrading is a drop-in — no config edits required.

🔐 Security note: 1.2.1 is a same-day security fast-follow to 1.2.0. It bumps golang.org/x/text to v0.39.0 to fix GO-2026-5970 (an infinite loop on invalid input), which govulncheck flagged as reachable via the Postgres connect path and Unicode normalization. The advisory landed after 1.2.0 was tagged, so 1.2.0's binaries shipped the vulnerable version — use 1.2.1. Everything else below is the 1.2.0 feature set, unchanged.


🗂️ .kenignore — control what ken indexes

Your repo commits things you don't want searched: built JS/CSS bundles, generated migrations, vendored code. ken was gitignore-only, so on artifact-heavy monorepos it indexed roughly 2× the files it should have — inflating cold-start time and memory.

Drop a .kenignore (gitignore syntax, nested per-directory) to fix that:

web/assets/          # compiled bundles
vendor/              # third-party code
**/migrations/*.php  # generated migrations
*.min.js
  • Union with .gitignore — excluded if either matches; evaluated independently so a !negation in one can't re-include what the other dropped.
  • .sembleignore honored as a fallback — a drop-in for anyone migrating from semble.
  • Default-on and inert without the file — your existing .gitignore behavior is untouched.
  • Even without one, ken now auto-skips oversized files (KEN_MAX_FILE_BYTES) and minified/generated files (KEN_MAX_AVG_LINE_BYTES).

On a committed-artifact monorepo this is the single biggest lever on index size, cold start, and RAM.

🧠 Lower, more predictable memory

For the long-lived ken-mcp server: GOGC=50 by default, an opt-in KEN_MEMLIMIT soft cap, and freed pages returned to the OS after builds and watch flushes — so idle RSS settles near the live index size instead of the build high-water. KEN_MAX_FILES rejects a hostile "millions of tiny files" repo rather than letting it OOM. A new scripts/rss_bench.sh measures it the same way external benchmarks do.

🔒 Security & correctness review

Every finding fixed and regression-tested:

  • top_k could OOM-crash the whole server (Critical). An unbounded top_k reached a giant allocation (and a filter path could overflow into a panic). Now clamped.
  • One source file could fatal-crash the server (Critical). The structural indexer parsed files with none of the size/stop-reason guards its sibling path had, so a pathological file could uncatchably overflow the goroutine stack. Both paths now share one guarded parser.
  • DB passwords no longer leak in a malformed-DSN error (MySQL + Postgres), which also fixes passwords containing / @ ?.
  • Symlink-escape closed on the watch path — an out-of-root symlink can no longer be indexed.
  • Plus: shutdown cleanup on stdin-EOF, cache eviction moved off the hot lock, watch-path enrichment parity, a lock-free-reader race in the reranker, and a batch of determinism/robustness fixes.

🔐 Verified model downloads

ken download-model and the background auto-fetch now verify each file as it streams — SHA-256 against HuggingFace's git-lfs ETag, and byte count against Content-Length / X-Linked-Size — so a truncated or swapped download is rejected before it lands. Concurrent fetches use a per-process temp file.

🛑 Graceful shutdown that can't hang

In-flight tool calls drain within KEN_MCP_SHUTDOWN_GRACE (default 5s), a second Ctrl-C force-quits, and a large in-flight index build now honors cancellation instead of blocking shutdown until it finishes.


New environment variables

Variable Default Purpose
KEN_MAX_FILE_BYTES 2MiB Per-file size cap for indexing
KEN_MAX_AVG_LINE_BYTES 1000 Skip minified/generated files (0 disables)
KEN_MAX_FILES 1000000 Admission cap — reject huge/hostile repos (0 = unlimited)
KEN_MEMLIMIT (unset) Soft memory limit (overrides GOMEMLIMIT)
KEN_MCP_SHUTDOWN_GRACE 5s Bounded in-flight drain window on SIGINT/SIGTERM

Install / upgrade

# Homebrew (macOS/Linux)
brew upgrade --cask ken     # new: brew install --cask townsendmerino/tap/ken
# Windows (Scoop)
scoop update ken            # new: scoop bucket add townsendmerino https://github.com/townsendmerino/scoop-bucket; scoop install ken
# Or with Go
go install github.com/townsendmerino/ken/cmd/ken@v1.2.1
go install github.com/townsendmerino/ken/cmd/ken-mcp@v1.2.1

Nothing to change on upgrade. If you're on an artifact-heavy repo, adding a .kenignore is the one thing worth doing.

Full changelog: CHANGELOG.md · compare v1.1.1...v1.2.1