Skip to content

Latest commit

 

History

History
926 lines (756 loc) · 51.7 KB

File metadata and controls

926 lines (756 loc) · 51.7 KB

Changelog

All notable changes to Akua will be documented in this file.

The format follows Keep a Changelog, and this project adheres to Semantic Versioning once v1 ships. Until then, @akua-dev/sdk versions bump independently of the Rust workspace; breaking changes to v1alpha1 data shapes trigger a minor bump in the SDK.

Note: the SDK was published as @akua/sdk on JSR through 0.5.0. Starting with 0.6.0 it ships as @akua-dev/sdk on npm — JSR's 20 MB single-file/total-package cap is incompatible with the bundled napi addon (~129 MB compressed across the per-platform packages).

[Unreleased]

[0.8.21] — 2026-06-12

Added

  • Hosted API bridge in the single akua CLI (api.rs, docs/cli.md). akua api now calls the hosted Akua API with token auth, configurable base URL, workspace context, request field/body helpers, and Akua structured errors for auth, permission, rate-limit, transport, and timeout failures. akua api spec fetches the public OpenAPI document; elevated audiences return E_UNSUPPORTED until the server exposes authorized audience-specific specs.

Fixed

  • SDK launch docs and generated site now describe the shipped Node/Bun NAPI surface (docs/sdk.md, site/concepts/sdk.html). The public SDK page no longer advertises stale browser support, shell-out dispatch, or future Akua Cloud namespaces as part of @akua-dev/sdk; it documents the current package as a native-addon-backed Package SDK with render/export/check/lint/verify and vendor drift guards.
  • Large render/export regressions stay covered by launch-readiness tests (packages/sdk, crates/akua-cli). The SDK and CLI regression coverage now guards the large render-output path and export surface that previously drifted during launch hardening.
  • Helm values.schema.json defaults that contradict generated KCL types are omitted (values_schema.rs, helm_union_schema.rs). Chart schemas that declare unsafe defaults such as null for non-null fields, numeric defaults for strings, or mismatched array items no longer emit invalid KCL defaults that abort the render worker.

[0.8.20] — 2026-06-09

Fixed

  • Render budget handling is stable across persistent nested engine sessions (engine-host-wasm, render.rs). Helm/Kustomize sessions now refresh their Wasmtime epoch deadline before each call, so a reused session no longer inherits a stale expired deadline after idle time. Worker interrupt traps are also classified as E_RENDER_BUDGET_DEADLINE and return the timeout exit code instead of surfacing as generic KCL evaluation failures.

[0.8.19] — 2026-06-01

Fixed

  • helm-repo dep resolution tolerates v-prefixed chart versions (helm_repo_fetcher.rs). The resolver parsed every Helm repo index entry with strict SemVer and aborted the entire resolution on the first non-conforming entry, so a chart whose index publishes v-prefixed tags (e.g. loft-sh's vcluster: v0.28.0-next.12) failed with E_DEP_RESOLVE even when a valid target version was published. A single leading v is now stripped before parsing (matching Helm's Masterminds/semver behavior) and an entry is skipped only if it is still unparseable — valid v-prefixed versions resolve, truly-malformed entries are ignored, and NoMatchingVersion continues to list the index's raw version strings.

[0.8.18] — 2026-05-29

Fixed

  • akua cache now inventories and clears the helm chart cache ($XDG_CACHE_HOME/akua/helm) added with the helm-repo dep source. Previously the helm cache grew unmanaged: akua cache list did not show it and akua cache clear did not reclaim it. --helm scope flag added to akua cache clear; akua cache path and akua cache list now report the helm root alongside oci and git. The default clear (no scope flag) wipes all three caches.

[0.8.17] — 2026-05-29

Fixed

  • Helm subchart condition/tags disabling now honored (helm-engine-wasm). The embedded engine now calls chartutil.ProcessDependencies before rendering, so cassandra.enabled = false (etc.) actually skips the subchart. Previously bundled subcharts always rendered regardless of their condition — for every dependency source (repo, path, oci), not just helm-repo. Disabling a chart's subcharts now requires you to supply your own datastore/config values the subchart used to provide (expected: the chart points at your backend).

[0.8.16] — 2026-05-29

Security hardening release. Remediates all 17 findings from the 2026-05-29 internal security audit (no sandbox-escape was found — these are supply-chain integrity + DoS-resilience hardening). See docs/security-audit-2026-05-29.md.

Security

  • Hardening from the 2026-05-29 security audit (docs/security-audit-2026-05-29.md). akua publish now strips replace directives from the signed manifest (consumers never inherit a publisher's replace); untrusted chart values.schema.json property names + descriptions are validated/escaped before KCL codegen (no injection); helm/kustomize engine Stores get a memory cap + finite epoch (chart-DoS ceiling); HTTP fetch bodies + gzip→tar extraction are size-capped (OOM / decompression-bomb); BasicAuth redacts its password in Debug and no longer derives Serialize; the git transport forces TLS verification (ignores ambient GIT_SSL_NO_VERIFY); plus several lower-severity fixes (UTF-8 panic on registry error bodies, helm http:// scheme-downgrade rejection, helm chart-name + OCI userinfo validation, git tree-entry name guard, vendor add URL canonicalization). --timeout now bounds the render worker's epoch deadline (previously the worker always used the 6s default). No sandbox-escape was found; these are integrity + DoS-resilience hardening.

Changed

  • Signing posture documented precisely (CLAUDE.md). The "verify by default on pull" wording now states the actual model: digest-pinning is the universal verified-before-write integrity gate (always on); cosign signature/attestation verification engages, fail-closed, when a [signing].cosign_public_key is configured. Signing stays opt-in.

[0.8.15] — 2026-05-29

Typed composition and Bun. Cross-Package composition is now typed-only (the untyped pkg.render({ path }) form is gone, and nested renders finally resolve typed aliases), and the published SDK renders Helm charts under Bun — the native addon takes its engines directory programmatically instead of relying on env-var propagation Bun doesn't do.

Removed

  • Untyped pkg.render({ path = "..." }) composition form (breaking) (pkg_render.rs, stdlib/akua/pkg.k). Cross-Package composition now goes through typed dep aliases only: pkg.render(pkg.Render { package = "<alias>" }), where <alias> is declared under [dependencies] in the calling Package's akua.toml. Supplying path (or no target) is a hard error directing the user to the alias form — honoring CLAUDE.md's "No filesystem paths in user-authored KCL." Path dependencies in akua.toml ({ path = "./..." }) stay valid; only the inline pkg.render path call is gone. Migrate by declaring the sub-package as a dep and composing it by alias (see examples/08-pkg-compose).

Changed

  • Nested renders resolve typed aliases (package_k.rs). A composed sub-package can now use pkg.render(package = "<alias>") against deps declared in its own akua.toml, not just at the root. render_opts enters each render frame with the dep-alias map derived from that frame's resolved charts (RenderScope::enter_for_render), with budget inheritance preserved. This is the enabling change that let the untyped path form be removed.

Fixed

  • Helm/kustomize engines now load under Bun via the SDK (helm-engine-wasm, kustomize-engine-wasm, akua-napi). The per-platform native addon ships with engines off and resolves the engine wasm from disk; it previously found that directory only through the AKUA_NATIVE_ENGINES_DIR env var, which the JS loader set via process.env. Bun doesn't setenv on process.env assignment, so the addon saw nothing and rendering failed with helm-engine.wasm not built. The addon now accepts the engines directory programmatically via a new setEnginesDir binding (checked before the env var), which the loader calls at module-load time. The env var still works for Node back-compat. macOS note: napi-rs's generated loader unconditionally probes the unpublished @akua-dev/native-darwin-universal package before the per-arch one; the resulting bun install 404 / loadErrors entry is benign (the per-arch package then loads) and is documented in loader.js — we don't publish an empty universal package.

[0.8.14] — 2026-05-29

Real-world Helm rendering. This release makes akua render the charts people actually deploy — pulling from classic HTTPS Helm repositories, composing charts inside modular sub-packages, and surviving the schema shapes and output sizes of large upstream charts (temporal, argo-cd, traefik, prometheus, grafana, cassandra). It also fixes the release pipeline's version-stamping.

Added

  • HTTPS helm-repo dependency source (helm_repo_fetcher.rs). akua.toml deps can now name a classic Helm repository — repo + chart + version (exact or semver range) — alongside oci/git/path. Resolved against the repo's index.yaml at add/lock time, content-pinned by .tgz sha256 in akua.lock, rendered deterministically from the cache offline. Private repos use the existing host-keyed --auth. The CLI gains akua add --repo <url> --chart <name> --version <req>; the SDK's add() mirrors it.
  • examples/13-subpackage-helm — a modular sub-package that itself composes a Helm chart, exercising the cross-package context fix below.
  • examples/14-helm-repo-dep — a chart pulled from an HTTPS Helm repository (network-gated e2e test, run pre-release like examples_kcl_ecosystem).

Fixed

  • Composed sub-packages now resolve their own external deps (pkg_render.rs). A pkg.render-composed (or typed pkgs.<alias>) sub-package can now import charts.<x> (Helm) and import k8s.api… (kcl-ecosystem) declared in its own akua.toml — previously these failed with CannotFindModule because external-package context was only set up for the root. The recursion loads the child manifest, resolves its deps, and registers them for the child eval, with the parent's reject_replace/offline posture propagated so a sub-package can't open an escape the root forbade.
  • Helm NOTES.txt no longer breaks renders (helm.rs). The engine returns NOTES.txt (top-level and per-subchart) among its files, but those are free-form prose, not manifests. akua parsed every file as YAML, so notes containing kubectl …: lines aborted the whole render with could not find expected ':'. NOTES.txt is now skipped before parsing.
  • Union-typed chart values no longer crash the evaluator (values_schema.rs). A values.schema.json field typed ["string","integer","null"] was collapsed to its first member while its default was emitted verbatim (port: str = 8080), aborting the wasm KCL evaluator. akua now emits a real KCL union (int | str) and marks null-bearing unions optional. Unblocks charts like traefik.
  • Hyphenated dependency names now import correctly (mod_file.rs). A dep keyed cnpg-operator produced a cnpg-operator.k module that import charts.cnpg_operator couldn't see (- is not a KCL identifier). Dep names are sanitized to KCL identifiers at every materialization site, with collision detection and digit-leading handling.
  • Large renders no longer fail with an opaque I/O error (render_worker.rs). The worker's stdout pipe was capped at 1 MiB, so a chart rendering >1 MiB (e.g. argo-cd, ~1.36 MB) died with os error 29. The cap is raised to the worker's memory ceiling (256 MiB) and overflow now surfaces as a typed E_RENDER_OUTPUT_TOO_LARGE.
  • Sub-package stubs no longer leak charts.* imports (pkg_stub.rs). A sub-package's import charts.<x> was carried into the synthesized stub compiled in the root context, where charts isn't registered. Chart imports are stripped from stubs (the schemas are what the stub needs).
  • Release binaries report their tag version (release.yml). The pipeline derives the workspace version from the pushed git tag (scripts/set-cargo-version.sh) and asserts akua -V matches the tag in the build smoke-test. Previously CARGO_PKG_VERSION was pinned to the committed Cargo.toml, so 0.8.9–0.8.13 binaries (and their SLSA provenance / OCI annotations) all reported 0.8.8.

[0.8.8] — 2026-05-07

Host-keyed HTTPS basic auth for vendor add. The CLI and SDK now accept caller-supplied credentials keyed by URL prefix; akua does not auto-load ~/.netrc, ~/.docker/config.json, or any env var. Lockfile URLs are canonicalized at write time so credentials cannot leak through akua.lock. Manifest URLs with embedded user:pass@ are rejected at parse time.

Added

  • Host-keyed HTTPS basic auth for vendor add (host_auth.rs). The CLI gains repeatable --auth <prefix>=<user>:<password> and explicit --auth-file <path> flags; the SDK's vendorAdd() accepts an auth?: Record<string, BasicAuth> parameter. Resolution: longest-URL-prefix match (same rule as git's credential helper / .npmrc). Akua never reads ambient credential files (~/.netrc, ~/.docker/config.json) or env vars — the SDK/CLI surface is the only auth source. Aimed at multi-tenant SDK consumers (e.g. cnap install bootstrap) where ambient lookups can leak credentials cross-tenant.
  • Lockfile URL canonicalization. akua.lock's source field now stores the canonicalized URL — userinfo stripped, default ports stripped (:443 https, :80 http), trailing .git and / stripped. Idempotent. Even a malformed credentialed URL reaching the lockfile-write path cannot leak credentials.

Changed

  • Manifest hardening: akua.toml rejects git = "https://user:pass@..." URLs at parse time with new code E_MANIFEST_GIT_USERINFO. Hostile-to-secrets-in-VCS by default; the lockfile-canonicalization rule above is the second-line defense.

[0.8.7] — 2026-05-07

Workspace-vendor surfacing: the CLI now exposes akua vendor add, akua vendor check, and akua vendor list; the SDK mirrors those entry points. vendor add writes the lockfile pin alongside materializing the tree, vendor-first resolver lookup is universal across all dep kinds, and lockfile metadata clears on digest change so cosign signatures can't outlive the bytes they were produced over.

The 0.8.7-rc1 through rc4 tags exist but their release workflows were cancelled before publish; this is the first 0.8.7 release on npm / GitHub Releases / Homebrew.

Added

  • akua vendor with add, check, and list subcommands.
  • @akua-dev/sdk vendor methods: vendorAdd, vendorCheck, and vendorList.
  • vendor add writes a LockedPackage pin into akua.lock alongside materializing the tree, so vendor check and akua verify have a stable digest to compare against — required for the offline-render contract once the canonical source is GC'd.
  • examples/12-vendor-offline/ — end-to-end demonstration of the offline-render contract: .akua/vendor/upstream/ + akua.lock are committed, the canonical upstream-chart/ source is intentionally absent, and render still succeeds. Wired into the workspace with examples_vendor_offline.rs integration test.

Changed

  • Shipped verb counts in the CLI reference, architecture overview, and roadmap were bumped from 26 to 27.
  • E_CHART_RESOLVE renamed to E_DEP_RESOLVE. The code is emitted by add / update / render / lock / vendor for any dep-resolution failure (path / oci / git / vendor) — not only Helm charts. Pre-alpha rename; consumers grepping for the old code should update.
  • Vendor-first resolver lookup is now universal across dep kinds. The resolver previously only checked .akua/vendor/<name>/ for OCI and git deps; path deps fell straight through to the canonical source. After this release, .akua/vendor/<name>/ wins for path deps too — making vendor add semantics symmetric across all three kinds and unblocking install-pipeline patterns that GC the canonical source after vendoring.
  • Bytes-tied lockfile metadata (cosign signature, SLSA attestation, transitive dependency list, yanked, Kyverno-converter fields) now drops when the digest changes during a lockfile upsert. Preserving these across a digest change would write (digest=B, sig=sig(A)) entries that no consumer can verify. The source / version / digest triple is always rewritten; everything else is conditional on prior.digest == new.digest.

Internal

  • New Dependency::spec() returns a typed DependencySpec<'_> enum carrying the source-form data (path / oci+version / git+tag/rev), replacing the Option-triple pattern matching that scattered .expect("path dep has path") calls across the resolver and vendor modules. DependencySpec::Oci::version is &str (not Option) — manifest validation already enforces presence, so the type now encodes the invariant.
  • New AkuaLock::find_slot / upsert_at lockfile primitives — single-scan lockfile upserts. merge_into_lock is now O(n) over the workspace's deps, was O(n²).
  • Shared lock_file::VENDORED_LOCK_FALLBACK constant for the sentinel version / tag_or_rev value (was a bare "vendored" string literal at four call sites).
  • Shared chart_resolver::upsert_locked_from_source helper used by both merge_into_lock (resolver-driven) and vendor::add_impl (vendor-add-driven), so the lockfile shape stays identical regardless of which codepath produced the entry.

[0.8.6] — 2026-05-05

Internal-only release: coverage uplifts, testability refactors, and a content-hash-based worker freshness guard for release builds. No public CLI or SDK behavior changes — the headline is supply-chain transport coverage rising from 9–20 % to 73–95 % on the OCI push / pull / fetch paths.

Added

  • New crates/source-hash/ workspace member: a tiny pure-Rust lib + binary used by task build:render-worker to record the content hash of akua-render-worker.wasm's source inputs alongside the .wasm artifact.
  • verbs::dev::run_loop, verbs::sign::run_with, and verbs::publish::run_with (all pub(crate)) — testability seams that let unit tests drive the watch loop without ctrlc and the signing flow with an explicit passphrase, without std::env::set_var racing in nextest's process-per-test pool.
  • oci_transport::registry_scheme(registry): http for loopback hosts (localhost, 127.0.0.1, [::1]), https otherwise. Matches the convention docker, oras, crane, skopeo use for self-hosted local registries; not a test-only seam.

Changed

  • Default render-worker wall-clock budget bumped from 3 s to 6 s (ResourceLimits::epoch_deadline 30 → 60 ticks). The 3 s default worked for hand-written Packages but tripped during cold-load of the kcl-lang/k8s ecosystem bundle (~24 K lines of schemas) — the kcl loader's allocation pattern inside the wasm sandbox runs longer than the original budget on every cold render. SDK / future- API callers needing a tighter security boundary can still override via the public field.

Fixed

  • Render-worker freshness guard for release profiles. cargo build -p akua-cli --profile {release,ci-release} now hard-fails when the embedded akua-render-worker.wasm was built from different source content than the akua-core code that's about to embed it. Previously the build emitted a cargo:warning= and shipped the binary anyway, which let host/worker drift slip past CI. The check is content-hash-based via the new source-hash crate; mtime-based fallback applies if the hash file is missing (e.g. the worker was built outside the Taskfile flow).
  • Taskfile.yml build:render-worker previously listed only crates/akua-render-worker/** as sources:. Adds crates/akua-core/** to the dependency list so an akua-core edit re-triggers the worker build instead of leaving Taskfile reporting "up to date" while the akua-cli build.rs flags drift.
  • examples_kcl_ecosystem integration test gated behind #[ignore]. It pulls live from oci://ghcr.io/kcl-lang/k8s and occasionally trips the wasmtime epoch budget on cold caches. Now excluded from cargo test --workspace; task release:validate passes -- --include-ignored so the pre-tag smoke still exercises it.

Test coverage

File Before After
crates/akua-core/src/oci_pusher.rs 19.9 % 95 %
crates/akua-core/src/oci_puller.rs 9.2 % 87 %
crates/akua-core/src/oci_fetcher.rs 14.9 % 80 %
crates/engine-host-wasm/src/lib.rs 56 % 76 %
crates/akua-cli/src/verbs/publish.rs 0 % 87 %
crates/akua-cli/src/verbs/pull.rs 0 % 85 %
crates/akua-cli/src/verbs/dev.rs 0 % 85 %
crates/akua-cli/src/verbs/sign.rs 84 % 87 %
crates/akua-core/src/helm.rs 49 % 68 %
crates/akua-cli/src/observability.rs 31 % 50 %

~80 new tests, all running through cargo nextest in seconds. Branch coverage on the load-bearing security invariants: digest-mismatch detection, layer rejection, lockfile pin enforcement, bearer-auth retry, registry tampering, signing / attestation round-trip, encrypted-key signing.

[0.8.5] — 2026-05-05

Republish of 0.8.4 — the npm publish step on native-release 401'd mid-loop on v0.8.4, leaving @akua-dev/native-engines@0.8.4 and @akua-dev/native-darwin-arm64@0.8.4 published but the other 6 per-platform packages + the @akua-dev/native meta + @akua-dev/sdk missing from npm. v0.8.4 stays partial-published as a graveyard version; install @akua-dev/sdk@0.8.5 to get the SDK __dirname fix described under 0.8.4.

Fixed

  • native-release.yml publish step is now idempotent. Probes npm view <pkg>@<v> before each publish and skips packages already on the registry. A future partial-publish failure (network blip, OIDC hiccup) can be recovered by re-running the workflow rather than bumping the version. The 0.8.4 → 0.8.5 surgery yesterday is the kind of dance this avoids.

No code changes vs 0.8.4. SDK contract identical (all-napi, no WASM transport, no __dirname bug class, the same requires-block discussion etc. that landed in 0.8.4 stays valid).

[0.8.4] — 2026-05-04

Two-headline release. Critical SDK bug fix + main-CI green again.

Fixed

  • @akua-dev/sdk Akua.check() / Akua.export() no longer ENOENT. Published 0.8.3 SDK had a CI build-machine path baked into the bundled dist/mod.js:

    ENOENT: no such file or directory, open
      '/home/runner/work/akua/akua/packages/sdk/wasm/nodejs/akua_wasm_bg.wasm'
    

    Cause: bun build --target node --format esm substituted the wasm-pack wrapper's __dirname reference with the build-time absolute path, since ESM has no __dirname. Akua.render() was unaffected (different transport).

    Fix: drop the WASM transport from the SDK entirely. Every method (check, fmt, lint, tree, diff, export, inspect package mode) now routes through the napi addon — same path render / verify / version / whoami already used. Napi already had all the bindings since the addon was first introduced; the SDK was simply still using the wasm side-channel for "browser-portability" that the published Node-only package never collected on. Net dist bundle: 62 KB → 38 KB; one transport, no __dirname bugs by construction.

    See PR #50 review thread for the original bug report.

  • CI on main — two pre-existing failures cleared.

    • lock_rejects_helm_dep_referenced_via_import was written before the path-escape guard landed; its path = "../nginx-chart" now correctly trips E_DEP_RESOLVE before the kind-mismatch check. Test relocated so the chart sits inside the install workspace.
    • crates/helm-engine-wasm/fork/apply.sh exited 128 in CI when actions/cache restored a partial .git directory, then exited 1 when go-task fanned out to build:helm-engine-wasm from two dep paths concurrently. Three fixes: health-probe .git and re-clone if git status fatals; commit the patched state so repeat invocations hit the fast path; mkdir-mutex around the apply for concurrency safety. Taskfile cleanup removed the redundant build:engines from sdk:test so the race is gone even before the script-level guards fire.

Removed (breaking, pre-alpha — minor at our discretion)

  • Akua.renderSource(packageFilename, source, inputs?) legacy positional overload. The object form (renderSource({source, packageFilename?, inputs?})) is the only signature now. Existing callers update mechanically.
  • crates/akua-wasm/ and packages/sdk/wasm/ directories. Workspace member dropped; build:akua-wasm / build:akua-wasm:nodejs Taskfile tasks deleted; CLAUDE.md verb-shipping checklist updated to point at the napi crate instead of the WASM wrapper.

Added (Taskfile — coverage + quality)

New tasks for coverage-driven bug discovery and broader Rust quality:

  • coverage:rustcargo llvm-cov nextest over the workspace + feature-gated example pass + doctests. Auto-installs cargo-llvm-cov
    • cargo-nextest at pinned versions. Test failures don't abort report generation (ignore_error: true).
  • coverage:sdkbun test --coverage with text + lcov reporters.
  • coverage:gaps — lists Rust + SDK files below THRESHOLD% line coverage. Bug-discovery candidate list across both languages.
  • coverage:rust:open, coverage:sdk:open, coverage:clean.
  • auditcargo audit against the RustSec advisory DB.
  • hack / hack:testcargo hack --feature-powerset over 18 feature flags across 4 crates.
  • deps:unusedcargo machete.
  • miricargo +nightly miri test -p engine-host-wasm (the unsafe-heavy crate hosting Module::deserialize).
  • build:timingscargo build --workspace --timings HTML.

Workspace baseline at the time of this release: 80.39% line coverage Rust / 76.82% line coverage SDK. Bug-discovery candidates identified: crates/akua-napi/src/lib.rs at 0%, multiple CLI verbs (dev, publish, pull, vendor) at 0%, oci_puller.rs at 9.2%, oci_pusher.rs at 19.9%. Tests for these lined up in the next release.

Performance

  • Test execution ~2× faster in the dev loop. Same workspace, binaries pre-warmed:

    cargo test --workspace        58.6s
    cargo nextest run --workspace 29.3s
    

    Compile time (the dominant cost on cold CI) is unchanged; speedup shows up on the inner loop. nextest's process-per-test isolation also makes the integration suite immune to libtest's shared-state fragility.

[0.8.3] — 2026-04-29

Follow-up to 0.8.2. Two fixes that surfaced when the 0.8.2 release attempt actually executed the matrix end-to-end.

Fixed

  • Cross-compile cwasm architecture mismatch. macos-latest runners are now aarch64 (M-series). When the matrix cross-compiled x86_64-apple-darwin, akua-cli/build.rs precompiled the worker cwasm on the aarch64 host and embedded it in the x86_64 binary; runtime Module::deserialize then trapped with Module was compiled for architecture 'aarch64'. Fixed by passing cargo's TARGET triple through to wasmtime::Config::target() at build time — same path taken by every engine plugin's build.rs via the shared engine_host_wasm::build_script_config() helper. Native builds (host == target) skip the call so the cwasm hash stays byte-identical to prior releases.

Changed (CI)

  • cli-release: deleted the duplicate validate job. ci.yml already runs task ci (which exercises the example golden tests) on every push to main; by the time a release tag exists, main has been validated. The matrix smoke test (init && render) is the remaining guard against ship-without-worker / wrong-arch-cwasm regressions. Saves ~14 min off the release critical path.
  • cli-release: shared wasm-bundle job. Engines + render-worker .wasm are wasm32-wasip1, byte-deterministic across runners. Build them once on Linux, share via actions/upload-artifact, download per matrix runner. Saves ~5-10 min × 5 matrix runners. Side benefit: the Windows runner now ships with helm + kustomize engines (it previously skipped them because the helm fork's apply.sh is bash and won't exec on Windows).

[0.8.2] — 2026-04-29

Critical fix: every released CLI binary since 0.6.0 shipped without the wasmtime render-worker embedded — akua render against any package failed with E_RENDER_KCL "render sandbox unavailable — worker module wasn't compiled into this akua binary." The cli-release matrix runners never invoked task build:render-worker, so build.rs wrote an empty .cwasm placeholder and the binary went out the door with a 0-byte sandbox.

task release:validate (run on a separate validate runner) didn't catch it because cargo test reuses the validate runner's locally-built worker artefact in target/. The matrix runners had no such artefact and no test ever exercised the produced binary.

Fixed

  • cli-release matrix builds the worker + engines pre-cargo build. Two new steps before Build akua CLI: task build:engines and task build:render-worker. Without these, akua-cli/build.rs has nothing to embed.
  • Smoke test on the produced binary. New step runs <binary> init smoke && <binary> render against the scaffolded Package and asserts ./deploy/ is non-empty. Catches future ship-without-sandbox / ship-without-engine regressions before users hit them. Skipped on cross-compile targets (linux/arm64 on x86_64, windows on ubuntu) where the runner can't execute the binary.
  • build.rs hard-fails in release profiles when the worker .wasm is missing. Dev / test profiles still get the empty- sandbox fallback so unrelated unit tests can compile without a full worker build first; release and ci-release now turn the silent empty-bytes failure into a build error, not a runtime trap.

Action for users on 0.8.0 / 0.8.1

Upgrade to 0.8.2 — brew upgrade akua (homebrew tap auto-bumped), npm install -g @akua-dev/sdk@0.8.2, or download the GitHub release tarball. The earlier binaries cannot render any package.

[0.8.1] — 2026-04-29

Doc + example consistency for the alias-method form. No behavioural change; the change is bundled stdlib (akua/helm.k docstring) so the worker .wasm bytes differ.

Changed

  • Helm at the call site is now alias-method in every documented example: <chart>.template(<chart>.TemplateOpts{values = <chart>.Values{...}}). Same shape as pkgs.<name>.render for Akua packages — synthesized per dep by the resolver, no import akua.helm in user code. CLAUDE.md, docs/cli.md, docs/package-format.md, the akua/helm.k stdlib docstring, and examples 02 / 03 / 05 / 06 + their READMEs updated.
  • Engine-direct callables (akua.kustomize, akua.kro, akua.oci) stay first-class for engines whose input isn't a typed dep. Documented as the explicit non-dep surface.
  • akua.helm.template retained as escape hatch for advanced Helm cases (multi-chart dynamic dispatch, post-renderer variations the stub doesn't expose). Doc redirects new code to import charts.<name> first.

Fixed

  • Examples 02 / 03 / 05 / 06 used <chart>.Chart{...} as the argument to helm.template — a shape the chart-stub generator never actually emits. They render now (alias-method form).

[0.8.0] — 2026-04-29

The observability + security round. Three big additions on top of the 0.7 pkg.render substrate: a full tracing stack (with OpenTelemetry export), the pkgs.<alias> typed-input shape that mirrors charts.<name> for Helm, and a path-escape / replace rejection guard that closes the host-side dep-resolution attack surface.

Added

  • Observability stack. Wasmtime trap symbolication (generate_address_map + preserved name section in the worker .wasm) — opaque wasm function NNNN traps now resolve to KCL source frames. New tracing subscriber on the host wired to --log / --log-level / -v; worker-side spans replay through the host's stderr pipe so a single trace covers worker.invoke → bridge.call → kcl eval. OpenTelemetry export activates when any standard OTEL_* env var is set (no CLI flag needed — the OTel spec is the contract). AKUA_BRIDGE_TRACE=1 back-compat shortcut for the legacy bridge eprintln pattern.
  • docs/debugging.md — playbook for diagnosing render-pipeline failures: the three knobs (--log=json, --log-level=debug, RUST_LOG), target taxonomy (akua / akua::worker / akua::bridge), reading symbolicated traps, host-vs-worker triage, anti-patterns (running KCL outside the worker, eprintln! in plugin handlers).
  • pkg.render(package = "<alias>") — typed dep-alias resolution replaces path strings in user-authored KCL. The alias references akua.toml [dependencies]. Unknown alias errors list known aliases for typo discovery.
  • import pkgs.<alias> synthesized stubs. Each Akua-package dep gets a per-render <alias>.k mounting only the upstream's schemas + a render lambda. Consumers write upstream.render(upstream.Input { ... }), mirroring webapp.template(webapp.Values { ... }) for Helm. KCL type-checks the input at the call site — typos surface as compile errors, not as runtime worker traps.
  • Path-escape guard on chart_resolver::resolve_path — rejects absolute paths in user-authored path = "..." / replace = { path = "..." } and any post-canonicalize result that escapes the workspace root. Internal vendor / OCI cache paths bypass the guard by construction.
  • AKUA_REJECT_REPLACE=1 — production gate that fails any render whose dep graph touches a replace directive. Auto-on in agent context; CI / agent / container invocations no longer honor publisher-supplied replaces. Strict "1"-only (matches the AKUA_BRIDGE_TRACE convention).
  • akua render --timeout=<duration> / --max-depth=<N> — wires the existing BudgetSnapshot to the CLI surface. Go-duration parser (30s, 5m, 250ms); typo'd values surface as E_INVALID_FLAG. New akua_core::duration_parse crate-public.
  • @akua-dev/sdk RenderOptions.timeout / maxDepth — same knobs reach the SDK; threaded through napi via Context.timeout.
  • CLAUDE.md invariants — a dedicated "replace and path deps are workspace-local" section captures the threat model so reviewers reject violations.
  • docs/cli-contract.md §9 / §9.1 — logging contract + OpenTelemetry env-var surface. §5 grows the duration unit list + the new --max-depth.

Fixed

  • PackageK::load canonicalizes the path — bare --package package.k previously stored a relative path with empty parent; plugin handlers fed that empty dir to canonicalize and reported i/o resolving `` (the opaque trap that motivated the observability work). Now resolves to the package's absolute directory unconditionally.
  • Wasmtime config deprecation — drop wasm_backtrace(true); capture is on by default in wasmtime 43.

Internal

  • engine-host-wasm::shared_config enables symbolication.
  • Workspace [profile.release] strip overridden for the worker build (task build:render-worker keeps the wasm name section).
  • RenderFrame.resolved_pkgs (alias → abs dir) propagated through RenderScope::enter_for_render.
  • pkg_stub::extract_schemas + build_stub_module synthesize the per-dep stubs.
  • Three new error variants: ChartResolveError::AbsolutePathRejected, PathEscape, ReplaceRejected. New code E_INVALID_FLAG.

[0.7.0] — 2026-04-28

The pkg.render round: a synchronous engine plugin that mirrors helm.template / kustomize.build, plus the supporting work to make composition first-class (path-deps + OCI Akua-package deps, budget guards, structured error codes, a worked install-as-Package example).

Added

  • pkg.render is a synchronous engine plugin. Returns a real [{str:}] list, not a deferred sentinel. List-comprehension patches ([r | overlay for r in pkg.render(...)]), filter expressions, and slicing all work natively. Requires the akua KCL fork (cnap-tech/kcl@akua-wasm32, commit d584c0bc) which copies PLUGIN_HANDLER_FN_PTR out of its mutex before invoking the plugin callback so reentrant KCL eval no longer deadlocks.
  • OCI Akua-package deps. [dependencies] upstream = { oci = "..." } resolves to a KclModule even when the artifact carries package.k (no kcl.mod) and dev.akua.* annotations.
  • Budget header for pkg.render. BudgetSnapshot { deadline, max_depth } propagated through the render stack and checked before nested invocations. Default depth cap is 16; outermost callers can install an explicit deadline via RenderScope::enter_with_budget. Catches recursive-composition runaway.
  • Structured error codes for plugin failures: E_RENDER_CYCLE, E_RENDER_BUDGET_DEPTH, E_RENDER_BUDGET_DEADLINE. Routed through a marker→code lookup table.
  • examples/11-install-as-package/ — worked install-as-Package shape: outer Package overlays a tenant label, filters out a kind, and appends an extras ConfigMap on top of pkg.render'd upstream.
  • renovate.json — pre-1.0 cargo bumps no longer batch into a single PR.

Changed

  • Render-worker rebuild trigger now watches akua-render-worker/src
    • akua-core/src and emits a cargo:warning= when the staged .cwasm is stale.
  • akua init . derives the package name from basename($PWD) instead of writing name = ".".
  • E_PATH_ESCAPE errors now carry a hint field with both remediations (vendor under the Package or declare in akua.toml).
  • akua render --debug (under --json) emits evalResult alongside the summary — the post-eval resources list before YAML normalization.

Removed

  • The pkg.render deferred-sentinel mechanism + the E_PKG_RENDER_PATCH_UNSUPPORTED fail-loud arm. Patching the return is now native, so the workaround retired.

@akua-dev/sdk — [0.6.0] — 2026-04-27

The SDK moves from JSR to npm and renames to @akua-dev/sdk. This is also the version that pivots from the chart-tooling shape (pullChart, packChart, pullChartStream, inspectChartBytes — last shipped as @akua/sdk 0.5.0) to a CLI-mirror shape: an Akua class whose methods map 1:1 to the binary's verbs. Same --json envelopes, same typed errors, all in-process via a bundled napi addon.

Added

  • Akua class — every shipping CLI verb is a method:
    • Read-only: version, whoami, lint, fmt, check, tree, diff, export, verify.
    • Render: render({ package, inputs, out, dryRun, strict, offline }) returns the same RenderSummary envelope the CLI emits, byte-for-byte. renderSource({ source | package, inputs }) returns rendered YAML directly for source-string consumers.
  • Full feature parity with the binary in-process — Helm + Kustomize engines, OCI fetch, cosign verify, JSON Schema / OpenAPI export. No akua binary on $PATH, no shell-out. Native addon (@akua-dev/native, per-platform via Node-API / napi-rs) for the feature-rich path; the existing akua-wasm bundle stays for the pure-KCL fast path and browser targets.
  • renderSource({ source, package, packageFilename, packageDir, inputs }) — accepts raw KCL source or an on-disk Package; engine callouts (helm.template, kustomize.build) work transparently when a packageDir is provided.
  • Typed-error routing across the napi boundary: thrown errors preserve the code (E_PACKAGE_MISSING, E_RENDER_KCL, …) so instanceof AkuaUserError / AkuaSystemError etc. continues to work.

Removed

  • The chart-tooling surface (pullChart, pullChartStream, pullChartCached, packChart, packChartStream, inspectChartBytes, streamTgzEntries, unpackTgz, @akua/sdk/cache, SsrfError). Subsumed by Akua.render's in-process resolver, which ships the same OCI fetch + digest verify + cosign check as the CLI uses.
  • new Akua({ binary: '...' }) — the binary path option is gone (no shell-out anymore). new Akua() is the only valid construction.

Migration from 0.5.0

The two surfaces don't overlap; consumers of the chart-tooling APIs need to rewrite. The 0.5.0 line stays installable on JSR for legacy callers; new code should use 0.6.0 and the Akua-class shape.

@akua/sdk — [0.4.0] — 2026-04-18

Changed

  • pullChart / pullHelmHttpChart stream-consume the response body with a running per-chunk maxBytes guard; reader is cancelled on overrun so a hostile server can't keep pushing bytes after we've given up.
  • OCI path: preflight Content-Length against maxBytes before opening the blob stream (parity with the HTTP Helm path).

Security

  • P2 tar.ts lint errors resolved (Uint8Array<ArrayBuffer> TS variance). No runtime change — but removes noise that was hiding real errors in bun run lint.
  • P2 Native Error.cause throughout (was cause_ underscore field in a couple of subclasses).

@akua/sdk — [0.3.0] — 2026-04-18

Added

  • pullChartStream(ref, options) — streaming variant returning ReadableStream<Uint8Array>; pipe straight into inspectChartBytes / Convex / fetch bodies without buffering.
  • @akua/sdk/cache (Node-only subpath export) — pullChartCached(ref) shares $XDG_CACHE_HOME/akua/v1/ with the CLI. Respects AKUA_NO_CACHE and AKUA_CACHE_DIR.
  • SsrfError + SSRF guard: pull hosts resolving to loopback / RFC1918 / link-local IPs (incl. AWS metadata at 169.254.169.254) are rejected. Bypass with AKUA_ALLOW_PRIVATE_HOSTS=1.
  • streamTgzEntries / unpackTgz options: maxEntries (20 000), maxTotalBytes (500 MB), maxEntryBytes (100 MB) — gzip-bomb caps mirroring the Rust side.

Changed

  • Replace hand-rolled YAML parser in tar.ts with the yaml npm package. Fixes Helm-repo index.yaml compact-list edge cases that tripped the narrow parser (Bitnami, Jetstack).
  • Consolidated auth helpers into src/auth.tscredentialsToAuthHeader, toHost, base64Encode. Removes duplicate logic across oci.ts / helm-http.ts / docker-config.node.ts.
  • AkuaError now uses native ES2022 cause (was a cause_ underscore field).
  • Per-repo index.yaml fetch cache — a build pulling N deps from the same Helm HTTP repo issues one index fetch instead of N.
  • packChart scrubs dependencies[].repository starting with file:// before emitting Chart.yaml (matches CLI akua package).
  • docker-credential-* helpers: validate helper name regex, drain stderr to prevent pipe stalls, enforce 5 s timeout.
  • pullChart / pullHelmHttpChart now stream-consume response bodies with a running maxBytes guard — a server advertising an oversized Content-Length can't force a full buffer allocation.

Security

  • P0 Tar extraction — reject symlink + hard-link entries in Rust unpack_chart_tgz. Prevents arbitrary file read via akua inspect on a malicious chart whose entry points at /etc/passwd.
  • P0 engine-helmfile removed from default cargo features. Helmfile's Go-template exec / readFile / requiredEnv functions let an attacker package run arbitrary commands at build time. Opt in only for trusted packages.
  • P1 SSRF guard across Rust + SDK — private-range IP literals rejected; reqwest redirect policy re-validates each hop.
  • P1 helm-cli render engine now pre-populates charts/ via akua's audited fetcher and skips helm dependency update. Helm never makes network calls for an untrusted package.
  • P1 kcl.entrypoint + helmfile.path confined to the package directory (no absolute paths, no ..).
  • P2 Redacted Debug impls for OciAuth / RegistryCredentials / BasicAuth — prevents accidental ?auth tracing leaks.
  • P2 Strict OCI hostname validation (no userinfo, fragments, queries).
  • P2 Strict helm-layer media-type enforcement — reject manifests without the canonical application/vnd.cncf.helm.chart.content.v1.tar+gzip layer instead of falling back to layers[0].
  • P2 URL userinfo redaction in error messages. Prevents oci://user:pass@host/... userinfo from leaking into log lines.
  • P2 Content-Length preflight on OCI + HTTP Helm pulls; capped Vec::with_capacity preallocation (4 MB max) — a server spoofing Content-Length: u64::MAX can't force a 100 MB up-front allocation.
  • P2 CEL evaluation: source length capped at 8 KB, 5 s wall-clock timeout. Malicious x-input.cel can't pin the worker thread.
  • P2 Cache LRU eviction (AKUA_MAX_CACHE_BYTES, default 5 GB).
  • P2 --helm-bin must be an absolute path when --engine=helm-cli — prevents $PATH shadowing by a writable directory.
  • P2 manifest.schema path validated at load time (relative, no ..).
  • P2 Per-call FetchOptions override for AKUA_MAX_* limits — multi-tenant workers no longer share process-global caps.
  • P2 Migrated from deprecated serde_yaml to serde_yml (the maintained fork).

Docs

  • New top-level SECURITY.md — threat model, fixed attack surfaces, remaining caveats, reporting process.
  • Rewrote packages/sdk/README.md — worked examples for pullChart, packChart, dockerConfigAuth, streaming, cache, safety limits.
  • Top-level README.md updated: SDK shipped (was "planned"), project structure reflects actual layout.

@akua/sdk — [0.2.0] — 2026-04-18

Added

  • pullChart dispatches on scheme: oci:// (existing) and https:// / http:// (new, Helm HTTP repos).
  • packChart options: valuesSchema, metadata, signal — emit values.schema.json / .akua/metadata.yaml alongside Chart.yaml / values.yaml.
  • dockerConfigAuth() Node helper — reads ~/.docker/config.json, supports auth, identitytoken, credHelpers, credsStore.
  • AkuaError base class; OciPullError, TarError, HelmHttpError, DockerConfigError, WasmInitError all extend it.
  • buildMetadata(sources, fields?, options?) + packChart metadata option. Honours SOURCE_DATE_EPOCH for reproducible buildTime.
  • dependencyToOciRef(dep) helper.
  • ChartYaml type gained appVersion, keywords, home, sources, icon, annotations, maintainers (optional).

Changed

  • AbortSignal wired through packChart and packChartStream.

@akua/sdk — [0.1.0] — 2026-04-18

Initial published SDK.

Added

  • pullChart(ref, options) — pure-TS OCI pull, no helm binary.
  • unpackTgz, streamTgzEntries, packTgz, packTgzStream, inspectChartBytes.
  • buildUmbrellaChart, mergeSourceValues, mergeValuesSchemas, extractUserInputFields, applyInputTransforms, validateValuesSchema, hashToSuffix.
  • packChart + packChartStream.
  • Node (@akua/sdk) + browser (@akua/sdk/browser) entries.
  • Published to JSR via GitHub Actions OIDC.

CLI — [0.1.0] — 2026-04-27

First tagged release of the akua binary. Substrate-shape only — no curated catalog, no cluster control plane. Ten green examples render deterministically; 26 verbs implement the universal CLI contract.

Added

Authoring + render

  • KCL-typed Packages: package.k with import + schema + resources regions, published as signed OCI artifacts.
  • akua render — wasmtime-sandboxed evaluation. Engines (Helm v4, Kustomize) compiled to wasm32-wasip1 and hosted inside akua's own wasmtime — no $PATH, no shell-out, no ambient filesystem.
  • akua export — emit the Package's Input schema as JSON Schema 2020-12 or OpenAPI 3.1. Field docstrings become description; @ui(...) decorators become x-ui extensions for form renderers (rjsf, JSONForms) and admission-webhook validators.
  • @ui(...) schema decorators on Input attributes (order, group, widget, min, max, placeholder, …). Decorator arguments project losslessly into the exported schema; render strips them before handing source to KCL's resolver.
  • Determinism invariant: same inputs + same akua.lock + same akua version → byte-identical output. No now(), no random(), no env reads in the render pipeline.

Dependency + lockfile shape

  • akua.toml + akua.lock — human intent + digest-pinned ledger. Three source kinds: oci, git, path. [replace] sections for vendor + path overrides.
  • KCL ecosystem support — pull oci://ghcr.io/kcl-lang/* packages alongside Helm charts. import k8s.api.apps.v1 resolves against the upstream KCL bundle inside the sandbox.

Verbs (26 shipped)init · whoami · version · verify · render · add · dev · test · tree · pull · publish · sign · update · lock · push · repl · pack · remove · diff · check · inspect · lint · fmt · cache · auth · export. Universal contract: every verb supports --json, --plan, --timeout, --idempotency-key; typed exit codes 0–6; structured-error stderr.

Agent-first surface

  • Auto-detection of Claude Code, Cursor, Codex, Gemini CLI, Goose, Amp, OpenCode, Cline, and 25+ other agents. Detected sessions auto-enable --json, --no-color, --no-progress, --no-interactive.
  • Skill manifests under skills/ conforming to the Agent Skills Specification.

Signing + attestation

  • akua publish emits cosign signatures (ECDSA P-256 keyed) and SLSA v1 predicates by default; consumers verify on pull. Air-gap flow: akua packakua signakua verify --tarball.

SDK

  • @akua/sdk (packages/sdk) — in-process WASM via akua-wasm crate, wasm32-unknown-unknown target. Verbs callable without spawning the binary: version, whoami, render, renderSource, check, lint, fmt, inspect, tree, verify, diff, export. Same shapes the CLI emits — typed via ts-rs-generated TypeScript types and schemars-emitted JSON Schemas.

Security

  • Wasmtime sandbox is structural — no --unsafe-host escape hatch, no shell-out fallback. Memory / epoch / wall-clock caps enforced per render; capability-model preopens scope filesystem access.
  • Adversarial test suite: zip-bomb resistance, path traversal, symlink escape, PATH=/nonexistent invariance, fork-bomb caps — all green.
  • Every Rust-side hardening from the SDK 0.3.0 entry applies to the CLI: tar-extraction symlink rejection, SSRF guard, OCI media-type strictness, Content-Length preflight, engine-helmfile opt-in, helm-cli opted out of helm dependency update.

Status

Alpha. v0.1.0 is the first tagged release. Stable contracts: the 26-verb CLI surface, the universal flag/exit-code contract, the WASM-backed SDK methods, the wasmtime sandbox invariant. Anything in docs/roadmap.md under Phase 5+ may change before v0.2.0. Safe for CI and agent workflows today; pin akua versions for production rollouts.