コンテンツにスキップ

Changelog

All notable changes to Kagura are documented in this file.

The format follows Keep a Changelog, and the project adheres to Semantic Versioning.

[Unreleased]

[0.4.0] — 2026-11-04

Added

  • Rewritten paper with explicit threat model and implementation boundaries, reproducible selected-pass measurements, and a separate historical shipped-profile deployment experiment. Generated tables and raw observations distinguish code growth, bounded correctness checks and startup failures.
  • Evaluation-tool regression checks across the Python CI matrix. Reports mark running, failed and completed measurements; table generation rejects incomplete reports and follows the recorded seed list.
  • kagura source compiler driver for LLVM 17–22, included in default builds and release bundles. Supports explicit toolchains, separate compilation, runtime discovery and policy preflight through native check-only validation.
  • Opt-in -kagura-string-scoped / "string": {"scoped": true} for XOR/AES: eligible nonthrowing, readonly, noncapturing consumers use per-call stack plaintext with a volatile wipe. Unsupported uses retain persistent storage.
  • MCP/CLI scoped-string policy generation and validation; doctor reports compiler-driver discovery and toolchain readiness.
  • Windows AntiDebug runtime entry points, loaded-module symbol resolution for DLL-import call indirection, and clean/patched API/IAT runtime tests.

  • Local kagura-mcp stdio server with environment diagnosis, registry/profile discovery, policy generation and validation, paginated audit inspection, and structured baseline/obfuscated binary comparison. File inputs are scoped to a configured workspace; the tools do not write or build application artifacts.

  • LLVM-independent kagura-cli.py config-check with policy names, types, numeric ranges and flavor validation. The MCP SDK is not needed by this CLI.

Fixed

  • Failed paper-evaluation reruns no longer leave earlier results available as the current measurement; table generation also removes stale output on error.
  • Paper descriptions distinguish Windows limitations at the historical measurement revision from runtime support added subsequently in PR #79.
  • Windows debugger PEB access supports MSVC intrinsics; AntiDebug omits POSIX ptrace startup on Windows. Hardware detection uses a real CPU trap context.
  • Preserve selected MSVC CRT linkage across split driver stages; safely scoped Windows pooled COMDAT literals can now be transformed.
  • Ordinary COFF external calls retain linker resolution instead of attempting unexported symbol lookup.
  • Strict native policy validation rejects invalid types/ranges and unused malformed flavors, including file/JSON errors; check-only mode writes no IR.
  • Correct plaintext-lifetime claims: default XOR/AES buffers remain persistent; scoped wiping is conditional and cannot erase consumer copies or registers.

  • Generate CLI policies from the same profiles and pass registry as the plugin, including current opt-in passes. Emit allowlist/denylist, tuning.seed and passes.dwarf in their supported locations. Report audit, symbol-map and build-ID options as additional compiler flags instead of ignored JSON keys.

Known limitations

  • The paper records startup aborts with shipped anti-debug profiles on its arm64 macOS host. A v0.4.0 pre-release check with two additional seeds reproduced those aborts; explicit anti-debug ablations passed bounded correctness checks. This release documents the issue without changing the runtime detector. Validate profile startup on each deployment target.

[0.3.0] — 2026-10-26

A repair release. Several documented flags did nothing at all; a few produced binaries that could not link or crashed the compiler. In every case the reason it went unnoticed was a hole in the test suite, so the suite was rebuilt too.

October IR safety follow-up

  • Fix string-split initialization for PHI operands by inserting it in incoming predecessor blocks (#57, #60).
  • Add opt-in -kagura-string-aggressive / "string": {"aggressive": true} for short/format strings and static aggregate references, with startup decryption and documented exclusions (#58, #59). Conservative collection remains the default. XOR runs before AES when both are enabled.
  • Preserve string GEP offsets, escaped pointer lifetime and pointer identity; validate complete use graphs and synchronize concurrent lazy initialization. Default AES plaintext now persists for the literal lifetime instead of using per-use stack buffers (#59).
  • Repair flattening dominance/EH handling, typed local/global encryption, encrypted lookup integer widths, musttail block boundaries, frame-sensitive function splitting, VM eligibility, MSVC descriptors and debug scope identity (#61–#69).
  • Validate JNI hooks before mutation (#70), preserve indirect call prototypes and support early constructors (#71), and correct typed pointer authentication access rewriting and guarded hardware initialization (#72).
  • Audit all 36 registered passes. The combined LLVM 21/macOS run passes 84 CTest tests, eight differential subjects and 3,456 verifier/serialization checks. Add a reusable cross-pass runner and a per-pass report with coverage limits in tests/audit/README.md (#73).

⚠️ Read this before upgrading

Passes that previously did nothing now do something. If you build with any of the flags below, expect your output to change — larger binaries, longer compile times, and possibly newly-exposed problems in code that was never actually obfuscated before. Re-measure size and performance before shipping.

Flag Behaviour before Behaviour now
-kagura-config=<file> ignored entirely selects passes as documented
-kagura-co no-op at every seed replaces constants with MBA expressions
-kagura-autoselect no-op narrows protection per function by risk score
-kagura-lt, -kagura-fsplit crashed clang apply their transforms
opt -passes=kagura-{bbs,bbr,dci,ibr,lt,fsplit,bbcheck,telemetry,pe,elt,mvo} silently inert apply their transforms
"profile": "FAST" \| "BALANCED" \| "STRONG" sv, anti-debug and (BALANCED/STRONG) tamper were left off enabled, matching the shipped integration/profiles/*.json

The -kagura-config change is the largest. A policy file requesting "profile": "STRONG" previously enabled no passes; it now enables around fourteen.

Profiles now require kagura_runtime to be linked. All three enable anti-debug. The shipped integration/profiles/*.json always did; the compiled preset behind a bare {"profile": "…"} did not, and the two are now one definition. If you select a profile without linking the runtime you will get undefined symbols at link time where you previously got a silently weaker binary.

integration/profiles/*.json are generated. Edit lib/Transforms/Profiles.def and run scripts/ci/gen-profiles.py. "seed": 0 is no longer emitted — it was the default anyway, and it made KaguraProfile.cmake pass -kagura-seed=0 on every profile build, which pinned the seed against any later -kagura-config.

Removed: KAGURA_LEGACY_PM and the legacy pass-manager shims. They required LLVM ≤ 16; the project requires 17.

Precedence is also now defined, where before it was undefined: an explicit command-line flag beats the config file, so -kagura-config=p.json -kagura-str=false disables string encryption rather than having the profile preset silently overwrite it.

Symbol filter lists merge instead of replacing. "allowlist", "denylist" and "protect" in a policy file used to overwrite -kagura-allow / -kagura-deny / -kagura-protect outright, which both contradicted that precedence rule and re-obfuscated the symbols the user had just excluded on the command line. The result is now the union of the two sources. If you were relying on the JSON winning, move the entries you want into the JSON — the flag no longer disappears. "audit_out" is a scalar and follows the ordinary rule: -kagura-audit-out on the command line now wins.

A policy file with a key the loader does not understand now says so. "anti-debug": false (hyphen) matched nothing and was dropped in silence, so every hand-written example in the docs — including one that told SDK vendors how to build a non-aborting binary — quietly did the opposite of what it said. Hyphens and underscores are now interchangeable on read, and any remaining unrecognised key is reported by name. Add -kagura-config-strict to fail the build instead of warning. Expect new warnings on existing policy files; each one is a setting that was never taking effect.

Fixed

Features that did not work

  • -kagura-config was a complete no-op. ConfigLoader ran as a pipeline pass, mutating the opt:: flags at pipeline run time, while Plugin.cpp reads those same flags at pipeline construction time to decide which passes to add. The policy file therefore always arrived after the decision it was meant to inform. The loader now runs before the pipeline is built.
  • -kagura-co never obfuscated a constant, at any seed. Every MBA identity is built from a ConstantInt operand, and the default IRBuilder<> carries a ConstantFolder that evaluated (V ^ R) ^ R straight back to V — so the pass stored the value that was already there. Now uses IRBuilder<NoFolder>.
  • -kagura-vm produced binaries that hung before their first line of output. The interpreter was fine; the pass emitted bytecode that did not mean anything. virtualize() handled binops, icmp, br, ret, load, store and three casts — PHI nodes, calls, GEPs, allocas, selects and switches all fell through to "emit NOP and carry on", and an operand it could not materialise made it continue out of the middle of an instruction. canVirtualize() accepted those functions regardless, so at -O1 every function with a loop became bytecode that computed nothing: the loop condition degenerated to a literal 0 and the VM took the same edge forever.

Measured across the test subjects with inlining disabled, all 16 cases where the old pass virtualized anything hung, segfaulted or aborted; the 10 that appeared to pass had virtualized nothing at all.

The pass is now all-or-nothing: any shape it cannot express leaves the function untouched. PHIs lower to stack-based parallel copies, allocas to arena offsets, calls through a relocation pool. Two further defects fixed along the way — the bytecode blob was a mutable global decrypted in place, so a second call to a virtualized function re-encrypted it; and the trampoline kept the attributes inferred from the original body, advertising memory(none) while calling the interpreter.

Verified: output identical to an unobfuscated build for every subject at -O0/-O1/-O2, across three PRNG seeds, on LLVM 21 and 22 — and repeated calls into the same virtualized function now return the same result. integration_vm_correctness guards it, and was confirmed to fail against the old code.

  • -kagura-lt and -kagura-fsplit crashed clang. Both emitted IR that fails LLVM's verifier: PHI nodes left ungrouped at the top of a block, and values erased while still in use (the freed slots were recycled, so surviving operands pointed at unrelated values of unrelated types). FunctionSplit now threads live-out values through allocas.
  • Eleven passes were inert under opt -passes=. Each re-checked its own -kagura-* enable flag inside the pass body, but that flag is only set by the -fpass-plugin entry point. Whether a pass runs is now decided once, when the pipeline is built.
  • -kagura-autoselect was dead three ways over: never injected into the pipeline, wrote metadata nothing read, and emitted only force-enable annotations, which merely restate the default for a pass already in the pipeline.
  • -kagura-anti-debug, -kagura-bbcheck and -kagura-telemetry could not link — kagura_check_tracer_pid existed only under #ifdef __linux__ while being emitted unconditionally, and kagura_bb_check and kagura_telemetry_event were never defined at all.
  • -kagura-str-aes could not link on Windows. core/blob_integrity.c needs only <stdint.h> yet sat in the if(NOT WIN32) source group.
  • KAGURA_UNITY_BUILD=ON did not compile — hasOnlyGuardableUses was defined identically in two files that share a unity group.
  • Four JSON policy keys — cse_break, string_split, bbcheck, elt — were accepted and silently ignored. The key table is now generated from PassRegistry.def.

Correctness

  • Two APInt width overflows in key generation, which aborted on any i8/i16 alloca or global.
  • A VMObfuscation register-map overflow. With assertions enabled it aborted; with them compiled out — which is what ships — it kept handing out increasing indices, so the emitted bytecode addressed past the end of VMState::regs[] and corrupted adjacent interpreter state at run time.
  • SymbolVisibility set hidden visibility on internal functions, which LLVM rejects (local linkage requires default visibility) and which achieves nothing anyway. It now hides unkept exported definitions.
  • Nine runtime checks were silently disabled. device_attest.c, integrity_report.c and play_integrity.c declared symbols __attribute__((weak)) under names that do not exist, then null-tested them — so kagura_appattest_local_check() always returned 1 and collect_violation_flags() always returned 0.
  • kagura_soft_response_check was exported with two incompatible signatures (POSIX vs Windows), so no portable caller could exist.
  • kagura_tamper_detected lived in an iOS source file, so Android and Windows links that pulled elf_integrity.o but not jailbreak_detection.o got an undefined symbol. Both callback names now live in core/tamper_response.c.
  • On Darwin, extern + weak does not produce a weak-undefined reference, so crash_symbolication.c's three __kagura_sym_* declarations were hard undefined symbols — breaking any consumer that force-loads the archive.
  • Frida port detection was trapped inside #ifdef __linux__, i.e. absent on the platforms Frida is most used against.

Build and packaging

  • SwiftPM could not build: all 40 source paths still referenced the pre-reorganization flat runtime/ layout. Package.swift is now at the repository root, where SwiftPM requires it.
  • Bazel built an empty library: glob(["../../runtime/*.c"]) matches nothing, and Bazel rejects uplevel references. A root BUILD.bazel declares the runtime from a package that actually contains it.
  • CocoaPods pulled Android and Windows sources into an iOS pod — all eight exclude_files entries were stale — and pointed at a plugin path that does not exist.
  • paper/*.tex and refs.bib were gitignored and untracked, so the source behind the README's Zenodo DOI existed on one machine only.

Added

  • Link smoke tests — 30 of them, generated from PassRegistry.def, each compiling and linking a real executable. No test had ever linked one, which is how three passes shipped referencing undefined symbols.
  • The lit suite now runs. Its harness had four stacked faults and CI never installed lit, so 29 tests were skipped with a STATUS message while the build stayed green. CI now installs lit and fails if the suite is not registered.
  • An assertions-enabled CI job. Every other job builds Release, so assert() is compiled out and the three assertion-firing bugs above could never have failed a CI run.
  • Pass tests append verify to every pipeline and build subjects at -O0 as well as -O2, since most of what these passes get wrong involves PHI nodes, which barely exist before mem2reg.
  • cmake/ module directory; all eight KAGURA_* options documented in one place. KAGURA_BUILD_FUZZ previously did not exist unless tests were also enabled.
  • install()/EXPORT rules. Release bundles were assembled by hand-copying build-tree paths; staging is now cmake --install.
  • integration/profiles/{fast,balanced,strong}.json — one definition of each profile, replacing five drifted copies across the integration directories.
  • runtime/internal.h. runtime/ had no headers at all; every cross-TU contract was a hand-written extern, kagura_on_tamper_detected alone appearing 19 times in three inconsistent forms.
  • include/kagura/runtime.h — the public C API of the runtime: the detectors an integrator can call, the hooks they can override, and the protected value types. docs/runtime.md had promised this header for some time and it did not exist, so every "Runtime hardening" sample in the cookbook failed at the #include. It also documents the trap those samples fell into: an int kagura_check_*() is a predicate, a void kagura_*_check() runs the check and fires the tamper hook, and the two are not interchangeable. (Not yet in the install list.)
  • -kagura-config-strict — treat an unrecognised key in a policy file as an error rather than a warning. Intended for CI: an ignored key is a silent downgrade of the shipped binary and is invisible in the build output.
  • docs/requirements.txt, pinning the documentation toolchain.

Changed

  • CMAKE_BUILD_TYPE defaults to RelWithDebInfo. A bare cmake -B build previously produced an unoptimised build with no NDEBUG, matching neither what CI tests nor what releases ship.
  • Duplication removed: FNV-1a (9 implementations → 1, with the constants pinned to canonical vectors on both the pass and runtime sides), /proc/self/maps scanning (9 → 1), path_exists (6 → 1), dyld image scanning (5 divergent pattern lists → 1 union), module constructor priorities (7 magic integers → enum CtorPriority).
  • runtime/CMakeLists.txt splits sources per platform. Previously every android/*.c was compiled on macOS as an empty translation unit and every ios/*.c on Linux.

Definitions that existed more than once

Every item here was a value or list written out in two or more places, kept in step by a comment asking the reader to remember the others.

  • The strength profiles lived in four places — applyProfile(), integration/profiles/*.json, the table in docs/configuration.md and the prose in integration/profiles/README.md — and had already diverged over sv / anti_debug / tamper (see above). One definition now, in lib/Transforms/Profiles.def, with a CI job that fails if the generated JSON is stale.
  • The VM opcode table was a C++ enum in include/kagura/VM.h and 48 #defines in runtime/core/vm_interpreter.c, plus four frame limits. A disagreement between the emitter and the interpreter of a wire format does not fail to compile and does not fail an IR-level test; it ships. Both sides now expand include/kagura/VMOpcodes.def.
  • The CLI flag declarations. Options.cpp generated its definitions from PassRegistry.def while Options.h listed the 40 matching externs by hand, three of them twice. Numeric tuning parameters were worse: definition, extern, and the JSON-key-to-flag mapping in ConfigLoader.cpp were three independent copies. All generated from KAGURA_TUNING rows now.
  • The pass source list in lib/Transforms/CMakeLists.txt was repeated in the unity-build section, and five files were in one copy but not the other. Both are globbed per directory now. (The groups had never taken effect anyway: UNITY_GROUP is ignored without UNITY_BUILD_MODE GROUP. Enabling it for real exposed a maskForWidth() defined identically in two Data/ files, now in Utils.)

Layout

  • include/kagura/Passes.h was 400 lines whose section headings had drifted from the directory layout. One header per category under include/kagura/Passes/, each named for the lib/Transforms/ subdirectory that implements it. Passes.h remains as the umbrella.
  • New lib/Transforms/ directories: ABI/ (VTableProtection.cpp, which is not a CFG pass), VM/ (VMObfuscation.cpp, which is not platform-specific) and Support/ for private headers. ObfuscationMetrics.cpp joins the other non-obfuscating passes in Infrastructure/.
  • scripts/ splits into cli/ (tools you run on your own build), eval/ (measurement) and ci/ (checks on this repository). See scripts/README.md.
  • The install rule now lists the shipped headers instead of globbing include/kagura/*.h, which had been publishing the LLVM pass-plugin headers to consumers who have no LLVM — and which excluded the .def files those headers expand, so the installed Options.h could not be included at all.

Documentation

  • Every hand-written JSON example used keys the loader could not read. "str-aes", "anti-debug" and friends are spelled with underscores in the schema, and the doc examples used hyphens throughout, so none of them did anything. docs/cookbook/sdk-vendor.md was the worst case: it explained that an SDK must not crash on detection and then wrote "anti-debug": false, which was ignored, leaving "profile": "STRONG"'s AntiDebug on. The loader now accepts both spellings and the examples use the canonical one.
  • docs/runtime.md cited a header and two functions that do not exist. include/kagura/runtime.h now exists (see Added); kagura_check_loaded_libraries was never a symbol — the real name is kagura_suspicious_lib_loaded — and kagura_run_review_risk_check is the shell script scripts/cli/review-risk-assessment.sh, not a C function. The same three names appeared across four cookbook pages.
  • The required-symbol matrix was wrong in most rows. It listed kagura_anti_debug_init as required from the runtime when the pass defines it, named two AntiDebug symbols that no pass emits, credited StringEncryptionAES with kagura_zero_buf, and had no row at all for BasicBlockChecksum, Telemetry, JNIObfuscation or ObjCObfuscation. Rebuilt from the emitters in lib/Transforms/.
  • -kagura-bbcheck was advertised as working anti-patching. It is scaffolding: the shipped kagura_bb_check is a weak always-passing stub, as Known issues has said since 0.2.1. It has been removed from the README threat table, downgraded in docs/security-model.md, and its performance.md figures relabelled as the cost of the call sites alone.
  • Six Japanese "translation coming later" stubs deleted. docs/integration/{xcode,android,unreal,unity,cmake}.ja.md and docs/contributing.ja.md were 13-line placeholders standing in front of English originals of up to 535 lines, and mkdocs-static-i18n already falls back to the complete English page when a .ja.md is absent. A JA reader now gets the real page instead of a dead end. cmake.ja.md also carried a profile table that contradicted Profiles.def.
  • Four .ja.md pages had drifted from their English originals and were brought back into sync: passes/data.ja.md and pass-order.ja.md were missing -kagura-string-split and -kagura-cse-break, passes/before-after.ja.md was missing two whole sections, and index.ja.md was missing the Cookbook and Security Model cards.
  • docs/tuning.md claimed every flag on the page could be set from "tuning"; five can. docs/passes/{control-flow,platform}.md filed elt, vtp and vm under the wrong subsystem — the same mismatch include/kagura/Passes.h records having fixed on the header side. quick-start.md claimed the release archive ships include/kagura/*.h; it ships three files.

Known issues

  • Dynamic JNI registration remains incomplete. Hook/IR mutation is repaired, but runtime helpers, method signatures and native function-address registration still need implementation. The IR audit does not establish working JNI registration or platform/device loader behavior.
  • clang -fpass-plugin=… -mllvm -kagura-… does not work below LLVM 22. Pre-existing, and it is the invocation the README leads with. clang parses -mllvm options before -fpass-plugin has loaded the plugin, so the -kagura-* options are not registered yet and clang exits with "Unknown command line argument '-kagura-str'". Verified failing on LLVM 21 with both this branch's plugin and main's; accepted on 22.

Working on every supported version: the shipped kagura-opt, or opt --load-pass-plugin=<plugin> -kagura-… -passes=…. The test suite uses the latter, which is why this went unnoticed — nothing exercised the documented path. The docs now carry the caveat; picking a real fix (register the options earlier, or make kagura-opt the documented entry point) is still open. - -kagura-bbcheck cannot detect binary patching. Its checksum is computed over LLVM IR opcodes before code generation; the runtime has machine code and no way to recompute that value, and block_id restarts at 1 in every function so it is not a unique key. kagura_bb_check is a documented always-pass stub. Fixing this requires the pass to emit post-codegen byte ranges instead. - Most of the evaluation tooling still runs in no CI job. differential-test.sh and verify-reproducible.sh are now in the matrix, but tests/redteam/, tests/symbolic_exec/, tests/decompiler_eval/ and tests/frida_resistance/ are not, so nothing checks that the resistance claims in docs/security-model.md still hold. scripts/eval/battery_impact.py is the only other one wired up. - A policy file cannot be validated without compiling something. Unknown keys are diagnosed now, and -kagura-config-strict makes them fatal, but both still require a translation unit to run against. A -kagura-config-check mode that lints the file on its own — for an editor or a pre-commit hook — is still open (TODO D.1a).

[0.2.1] — 2026-07-31

Added

  • Windows: a working way to run the passes. -fpass-plugin is unavailable on Windows because the MSVC-targeted LLVM ships with LLVM_ENABLE_PLUGINS=OFF, and the static KaguraObfuscator.lib the build produced there had no consumer — the documented "link it into your driver tool" had no such tool. kagura-opt now links the passes in and registers them by calling getKaguraPluginInfo() directly, so building with -DKAGURA_BITCODE_TOOLS=ON gives Windows users an IR-level entry point.
  • KAGURA_FORCE_STATIC_PLUGIN — selects the same static-linkage path on macOS and Linux, so the Windows build can be reproduced without a Windows machine. CI runs this configuration on Linux.

Fixed

  • Windows CI verified nothing. With no loadable module, the entire test suite short-circuited to a single echo, so a green Windows job only meant the sources compiled. The suite now drives the passes through kagura-opt on that platform.
  • The static KaguraObfuscator archive no longer links the imported LLVM targets. It never needed to resolve those symbols itself, and inheriting their INTERFACE include directories as -isystem put the macOS SDK C headers ahead of libc++, breaking <cstddef>. Consumers link the components (Core, Support, Analysis, TransformUtils, Passes) instead.

[0.2.0] — 2026-06-30

A wide-coverage release. Two new IR passes, three platform attestation runtime stubs, a full documentation site with Japanese translations, a CI fix for Windows / MSVC compatibility, two post-build CLI utilities, and a single-source-of-truth refactor for the pass registry.

Added

New passes

  • -kagura-cse-break (Data) — Defeats decompiler CSE-recovery (Ghidra, IDA hex-rays, Binary Ninja MLIL) by duplicating shared SSA expressions so every user sees a fresh definition. Functionally identical, syntactically distinct — pattern-matching deobfuscators can no longer merge the expressions back into a single readable line.
  • -kagura-string-split (Data) — Fragments long (≥16 byte) string literals across multiple smaller private globals; recombines them at runtime via a flag-guarded init stub on first access. Defeats strings -n <large> and contiguous-blob assumptions. Composes with kagura-str / kagura-str-aes: when both are enabled, neither plaintext nor ciphertext exists contiguously in the binary.

Platform attestation runtime stubs

  • runtime/ios/device_attest.c — C bindings for Apple DeviceCheck (iOS 11+) and App Attest (iOS 14+, A10+). Availability gates, nonce generation (arc4random_buf + clock + atomic counter mix), and a fast local pre-screen that short-circuits the async Apple round-trip when the environment is obviously bad.
  • runtime/windows/etw_detection.c — ETW provider enumeration to detect analysis-tool registration (Cheat Engine, Procmon, Process Hacker, ScyllaHide GUIDs). Stub-only by default; enable the full TDH path with -DKAGURA_ETW_FULL=1 + tdh.lib.

Android Play Integrity (runtime/android/play_integrity.c) is part of the 0.1.x history and is documented here for visibility.

Documentation site

  • Full MkDocs site published at https://qusamulab.github.io/kagura, deployed automatically from main via .github/workflows/docs.yml.
  • Multi-section sidebar nav: Getting Started · Passes · Integration · Cookbook · Security Model · Configuration · Runtime · Project.
  • Cookbook: four recipe pages — Banking / FinTech, Mobile game / anti-cheat, SDK / library vendor, DRM / license enforcement. Each includes a threat model, complete policy JSON, build commands, source-side annotations, runtime hardening code, verification commands, and explicit "still on you" boundary callouts.
  • Security Model (docs/security-model.md): STRIDE coverage matrix (strong / partial / out-of-scope), hard non-goals (plaintext secrets, side channels, determined adversaries), five load-bearing assumptions, self-evaluation tool matrix, recommended defense-in-depth layering.
  • Japanese localization (mkdocs-static-i18n): English default at /, Japanese mirror at /ja/. 30 pages translated covering Home, Getting Started, every Passes page, Configuration, Runtime, Game Protection, Testing, Architecture, Contributing, Integration index, and per-system integration overviews. Cookbook + Security Model JA translations will follow in a subsequent release.

Tooling

  • scripts/kagura-strip.py — Post-build hygiene CLI. Zeros out LC_UUID (Mach-O) / .note.gnu.build-id (ELF) so release binaries don't leak a rebuild fingerprint. Run after strip.
  • scripts/kagura-diff.py — Section / symbol / string diff between baseline and obfuscated binaries. Text, HTML report, or JSON dump. Validates that a release build hid the symbols and encrypted the strings it was supposed to.

Community & DX

  • GitHub Issue templates (bug, feature, pass-proposal) with required YAML fields, labels, and category dropdowns.
  • PR template with type-of-change checklist, test plan, and new-pass checklist.
  • .github/ISSUE_TEMPLATE/config.yml surfaces Discussions and the docs site, routing questions away from Issues.

Changed

  • refactor: table-driven pass registry — lib/Transforms/PassRegistry.def is now the single source of truth for every Kagura pass. Plugin.cpp shrinks from 378 → 197 lines; Options.cpp from 205 → 116 lines (net −250 lines). Adding a new pass is now a one-line edit in the registry table — no more synchronizing three separate if-chains across Plugin.cpp and Options.cpp.
  • README slimmed to hero + threats table + docs links. Detailed content migrated into the docs site.
  • MkDocs nav consolidated to collapsible left-sidebar groups (Home, Getting Started, Passes, Integration, Cookbook, Security Model, Configuration, Runtime, Project) instead of 10 cluttered top tabs.

Fixed

  • Windows CI (fix/ci-windows-llvm-bump) — Build & Test (Windows, Clang-CL) job was failing on main with error STL1000: Unexpected compiler version, expected Clang 20 or newer after a windows-latest runner image update. Bumped the LLVM tarball from 19.1.7 → 21.1.5 to satisfy the new MSVC STL requirement.

PR list

PRs merged in this release:

  • 36 — chore: add MkDocs (foundation)

  • 37 — docs(mkdocs): collapsible left-sidebar nav with subsections

  • 38 — ci(windows): bump LLVM 19.1.7 → 21.1.5 for MSVC STL requirement

  • 39 — chore(batch-a): community templates, post-build CLIs, docs banner

  • 40 — docs(batch-b): security model + cookbook recipes

  • 41 — feat(batch-e): platform attestation stubs (iOS / Windows)

  • 42 — docs(batch-c): i18n — Japanese translations with English default

  • 43 — feat(batch-d1): kagura-cse-break — defeat decompiler CSE recovery

  • 44 — feat(batch-d2): kagura-string-split — fragment string literals

  • (this PR) — refactor: table-driven pass registry + CHANGELOG for v0.2.0

[0.1.2] — 2026-05-24

See git log v0.1.1..v0.1.2 for details. Highlights:

  • Paper link added to Citation
  • Zenodo DOI badge
  • Documentation polish

[0.1.1] — 2026-05-13

Initial public release iteration. See git log v0.1.0..v0.1.1.

[0.1.0] — 2026-05-09

First public release of Kagura as MIT-licensed OSS.