North star: akua is a sandboxed-by-default rendering substrate. Every render runs inside a wasmtime WASI sandbox with memory / CPU / wall-clock caps and capability-model filesystem preopens. No shell-out, ever. Untrusted Packages are safe to render on shared hosts.
Invariant lives in CLAUDE.md ("Sandboxed by default. No shell-out, ever."). Detailed model in docs/security-model.md.
The roadmap is ordered by implementation phase, but releases cut across phases. This section lists the minimum boxes each release ships with. Phases below give the detail.
Release principle. The first number anyone sees is the one they judge the project by. akua's brand promise — CLAUDE.md's "Sandboxed by default. No shell-out, ever. Untrusted Packages are safe to render on shared hosts." — is not something we ship alongside a "but actually…" caveat in v0.1.0 release notes. Either the invariant holds, or we don't cut v0.1.0. Pre-release work that would normally land as an alpha is shipping on
maintoday under the explicit pre-alpha label in CLAUDE.md; contributors and early adopters who want to preview can build frommainthemselves.
Target: anyone CLAUDE.md promises safety to. Solo authors, internal CI pipelines, agent consumers, and hosted build services accepting PR-submitted Packages, in-browser dev loops loading third-party examples, multi-tenant operators. If the security model doesn't let us include the last three, v0.1.0 isn't ready.
Security invariant — v0.1.0 blocks until this holds end-to-end:
- Phase 4 shipped — every
akua renderinvocation (CLI or SDK) runs inside a wasmtime WASI sandbox with memory / fuel / epoch caps + capability-model filesystem preopens. No native render fallback. - Phase 4B shipped —
@akua-dev/sdkdelivers the same render path viawasm32-unknown-unknowninside the host JS runtime's own sandbox. Identical guarantees for SDK consumers — same invariant, different sandbox. - Path-escape + symlink-escape rejected at the plugin boundary (shipped Phase 0 — guard existing tests).
- No shell-out in any render path. No
--unsafe-hostflag. No feature gate that opens one (shipped Phase 0 — guard this at code review forever). docs/security-model.md"Operational guidance today (pre-Phase 4)" section deleted — the guidance exists only because the invariant doesn't yet hold; once it holds, it goes.- Adversarial integration tests: fuel-exhaustion, epoch-exhaustion, memory-bomb, path-escape, symlink-escape, import-escape (KCL
import fooagainst forbidden dirs) — each surfaces a typed error, not a panic or a timeout.
Shipped on main today (all load-bearing for v0.1.0):
- CLI contract + ~27 verbs (
init,render,check,lint,fmt,test,dev,repl,add,remove,tree,lock,update,verify,diff,inspect,pack,push,sign,pull,publish,cache,auth,whoami,version,export,vendor) - Deterministic raw-YAML render writer + per-output sha256
Package.kloader viakcl_lang::API+ theakua.*KCL stdlib- Helm + Kustomize WASM engines
- Typed
charts.*deps over path / OCI / git withreplace+ lockfile digests - Cosign keyed verify + SLSA v1 attestation on publish
- Full air-gap crypto loop:
akua pack→akua sign→ transfer →akua verify --tarball→akua push --sig - Operational verbs:
akua cache,akua auth,akua lock [--check],akua update [--dep]
Still to ship for v0.1.0 (ordered by blast radius if skipped):
- Phase 4 — wasmtime-hosted
akua render. Security invariant for the CLI. - Phase 4B —
akua-wasmbundle via JSR. Security invariant for the SDK + full verb coverage. - Adversarial test suite targeting the sandbox (listed above).
- Docs sweep — see v0.1.0 release punch list. Explicitly: the "this doesn't yet hold" caveats in security-model.md go.
What v0.1.0 does NOT promise (and says so in release notes):
- Rego test runner + policy engine — KCL test runner only.
- Cosign keyless (fulcio + rekor) — keyed verify + SLSA attestation only.
- Hosted multi-tenant serve —
akua serveis v0.2.0. - Recursive attestation walk over transitive deps — direct deps only.
These are feature absences, not invariant violations. Users know exactly what they're getting.
Exit gate for v0.1.0: CLAUDE.md's invariants (sandbox + no-shell-out + signed-by-default + deterministic) each hold against the adversarial test suite, docs reflect reality, release notes list only feature absences (never caveats that make the brand promise weaker).
- Phase 5 —
akua serve(~2-3 weeks). Single process handles N concurrent renders with per-tenant isolation. v0.1.0 already delivers the per-render sandbox; v0.2.0 adds the concurrent-tenants HTTP surface on top. akua attest+akua verify --att— offline DSSE/SLSA alongside the existing signature flow.
- Phase 6 B — cosign keyless verify (fulcio + rekor).
- Phase 7 C follow-up — recursive attestation walk over transitive deps.
- Phase 7 D — HSM / cosign-native key formats.
- Policy engine phase (design open — regorus vs OPA→WASM).
- Phase 8 — Rego test runner (depends on policy engine).
- Phase 8 —
akua replRego half (depends on policy engine). - Phase 9 —
akua deploy,akua query,akua trace,akua policy("ship when there's demand").
Shipped on main:
- CLI contract primitives — universal args, typed exit codes, agent auto-detection, structured errors
-
akua.toml+akua.lockparsers with round-trip tests over every example -
Package.kloader viakcl_lang::API+option("input")inputs - Every verb listed under v0.1.0 above
- Raw-YAML render writer — deterministic
NNN-kind-name.yaml+ per-output sha256 - KCL plugin bridge —
plugin_agent: u64function pointer, JSON-in / JSON-out -
akua.*KCL stdlib (ctx,helm,kustomize,pkg) — typed options-schema pattern -
pkg.render()synchronous host-plugin composition - Signed + attested OCI distribution (publish / pull / pack / push / sign / verify / verify --tarball)
- Benchmarks: native vs WASI-wasm (2× overhead — see docs/performance.md)
Deliberately not shipped (violates the sandbox invariant):
-
— never existed post-Phase 0engine-helm-shellfeature -
— never existed post-Phase 0engine-kustomize-shellfeature -
Any— will never ship--unsafe-host/--engine=shellescape hatch
Removes the security escape hatch entirely. Establishes sandbox-first as the default posture.
- No
engine-helm-shell/engine-kustomize-shell/--unsafe-hostfeature exists in Cargo.toml — shell-out invariant holds at compile time - Path-traversal guard in
kcl_plugin::resolve_in_package— canonicalize + assert-under-package-dir + symlink resolution - Integration tests cover path-escape rejection
- docs/security-model.md written — threat model, what's guaranteed, what's not
- CLAUDE.md invariant text
Exit gate: ✅ akua-core builds without any shell-out feature; pkg.render({ path = "../.." }) returns a typed error; security-model.md documents what's still aspirational (Phase 4 process sandbox).
Research recommendation: revive akua's deleted fork, not vendor kclipper. Prior work used Helm v4 + direct engine.Render — proven 20 MB WASM forked, 75 MB stock.
- Restore
crates/helm-engine-wasm/from git history — cb…(Phase 1 commit) - Go build works against stock Helm v4.1.4 via
-buildmode=c-sharedon wasip1 - Taskfile target
build:helm-engine-wasm— producescrates/helm-engine-wasm/assets/helm-engine.wasm(74 MB stock) - Wasmtime host in
crates/helm-engine-wasm/src/lib.rs— loads.cwasm, renders viapkg/engine.Render - Plugin handler
crates/akua-core/src/helm.rs— sameakua.helm.Templateschema, swaps in behindengine-helmfeature -
examples/00-helm-hellorenders end-to-end via the embedded engine — verified withPATH=/nonexistent; byte-identical sha256 to prior shell-out render - Phase 1c: benchmark refreshed in
docs/performance.md. Initial embedded-engine bench showed ~120 ms cold helm — Phase 1b + Phase 1d drove it to ~57 ms, inside the 100 ms dev-loop budget. - Phase 1b:
fork/apply.sh+task build:helm-engine-wasmapply the client-go strip patch. Default build is forked (20 MB wasm, 73% smaller).task build:helm-engine-wasm:stockpreserves access to the unpatched 75 MB variant for cross-checking. - Phase 1d: thread-local
Sessionin both helm-engine-wasm + kustomize-engine-wasm. One Store + Instance + typed-func lookups reused across every plugin call for the life of the process._initializeruns exactly once per thread. Multi-helm Packages now amortize to sub-100 ms (prior each call paid full init).
Exit gate: ✅ examples/00-helm-hello renders in a sandbox. helm on PATH never consulted. All Phase 1 boxes (1a + 1b + 1c + 1d) shipped. Cold render in ~57 ms.
- Phase 1e: shared
engine-host-wasmcrate. HoldsSession+precompile+EngineSpec+thread_local_call.helm-engine-wasmandkustomize-engine-wasmare now thin shims (serde types + tar helpers + engine-specific error wrapper). Future kro/CEL/kyverno engines get the wasmtime plumbing for free.
Spec-to-code convergence. docs/package-format.md §2 and docs/lockfile-format.md already document import charts.<name> + OCI / Git / Path / Replace dep forms. The resolver is shipping in two slices.
-
chart_resolvermodule: local-path deps → canonicalized path + sha256 digest - Per-render
chartsKCL external pkg generated from resolved deps (charts/<name>.kexposespath+sha256constants) -
PackageK::render_with_chartsthreads resolved chart paths as allowed absolute roots for the plugin path-escape guard —helm.template(nginx.path, ...)survives without an--unsafe-hostescape hatch -
akua renderCLI verb auto-loads siblingakua.toml, resolves charts, passes them through -
examples/01-hello-webappvendored nginx chart + rewritten Package renders end-to-end — verified viaexamples_hello_webapp.rsintegration test
-
replace = { path = "..." }on OCI/git deps honored — pull source from the local fork while the lockfile still pins the canonical ref -
ResolvedSourceenum (Path/Oci/OciReplaced/GitReplaced) drives the lockfile writer -
chart_resolver::merge_into_lockupserts path-dep + replace entries, preserving prior cosign / attestation metadata -
AkuaLock::save/find/upsertwriter API -
akua addbest-effort updates the lockfile on every edit -
akua verifyexemptspath+file://sources from strict_signing
-
oci_fetchermodule: HTTPS GET of manifest + chart blob, anonymous bearer-token dance for public ghcr.io / docker.io / quay.io - Content-addressed cache at
$XDG_CACHE_HOME/akua/oci/sha256/<hex>— second render reuses the unpacked tree - Lockfile-pinned digest verify on pull — a drifted tag fails the render loudly with
LockDigestMismatch -
ResolverOptions { offline, cache_root, expected_digests }gate —resolve()stays offline for tests;resolve_with_options()is the network path -
akua render+akua addpass lockfile digests asexpected_digests - Integration test pulls
ghcr.io/stefanprodan/charts/podinfo:6.6.0, caches, verifies digest-mismatch rejection
-
akua render --strict: raw-string plugin paths rejected. Forces every chart to go throughakua.toml+import charts.<name>. Typed exit codeE_STRICT_UNTYPED_CHART. -
akua render --offline: OCI / git deps must cache-hit. Air-gapped CI path. -
akua verifypath-dep drift detection: re-hashes vendored charts on disk, emitsPathDigestDrift/PathMissingviolations when the tree diverged fromakua.lockor was deleted. - Git deps via
gix(pure Rust, no shell-out). Clones into$XDG_CACHE_HOME/akua/git/repos/+ checkouts undercheckouts/<sha>/. Content-addressed, lockfile-pinned by commit SHA. - Private-repo OCI auth via
~/.config/akua/auth.toml(akua-native TOML) and~/.docker/config.json(standard docker login format). Basic auth + bearer PATs supported; docker credential helpers intentionally not (shell-out). - Generated
charts.<name>module grows aValuesschema (fromvalues.schema.json) + aTemplateOptswrapper +template()lambda pre-filled withchart = path. Authors callnginx.template(nginx.TemplateOpts { values = {...} })— the "chart: str | Chart" ergonomic win, via a callable on the module rather than a schema union. -
akua removeprunes matching lockfile entries;akua treeshows[replace -> <path>]markers for fork overrides.
Exit gate: ✅ all three slices shipped. OCI chart end-to-end via akua render (cache hit on second call). Git chart via gix (no shell-out). Private-repo auth for both. --strict / --offline / path-dep drift for CI-grade guarantees. charts.<name>.template(...) gives Package authors an autocomplete-driven authoring surface.
Same pattern as Phase 1, different upstream. sigs.k8s.io/kustomize/api +
sigs.k8s.io/kustomize/kyaml Go → wasm32-wasip1. Tar.gz sent over
linear memory; guest unpacks into filesys.MakeFsInMemory() so there
are no host-side preopens to grant.
-
crates/kustomize-engine-wasm/scaffold — Cargo.toml, build.rs, src/lib.rs, go-src/main.go - Wasmtime host in
src/lib.rsexposesrender_dir/render_tarAPI -
crates/akua-core/src/kustomize.rsplugin handler — sameakua.kustomize.Buildschema, swaps in behindengine-kustomizefeature (default-on) -
examples/09-kustomize-hellorenders end-to-end — verified withPATH=/nonexistent; byte-identical sha256 to prior shell-out render - Taskfile target
build:kustomize-engine-wasm(26 MB stock wasm) - Integration test
tests/examples_kustomize_hello.rsrestored
Exit gate: ✅ examples/09-kustomize-hello renders in a sandbox. kustomize on PATH never consulted.
Sandbox becomes the default execution path for akua itself. User-invoked akua render wraps a wasip1-compiled akua-render-worker inside wasmtime. Delivers CLAUDE.md's "sandboxed by default" invariant at the process level — the precondition for cutting v0.1.0. No release before this lands.
- Spike complete 2026-04-24 (docs/spikes/kcl-wasm-feasibility.md) — compile + runtime both green on
wasm32-wasip1. Two runtime panics resolved same day:kcl-driver::get_pkg_listvia cnap-tech/kcl@akua-wasm32 fork + upstream PR kcl-lang/kcl#2086,stdlib::stdlib_rootvia cfg-guard in akua-core. -
akua-render-workerbinary targetingwasm32-wasip1— Ping + Render requests both handled. akua-core + engine-kcl compiled into the worker via the[patch]pin. - Wasmtime host harness in
akua-cli: per-render Store withStoreLimits::memory_size(256 MiB)+epoch_interruption+ background epoch ticker. Single shared Engine for worker + engine plugins (helm, kustomize) per wasmtime's "one Engine, many Stores" pattern. Plugin bridge (env::kcl_plugin_invoke_json_wasm) ferries callouts across the Store boundary; plugin panics survive the wasip1 trap boundary via out-of-band capture onHostState. - AOT-compile
.cwasmat akua's build time; embed in akua binary (include_bytes!wrapping$OUT_DIR/akua-render-worker.cwasm, Config-hash-matched to runtime). -
akua render,akua dev,akua replall dispatch through the worker — no native fallback. Plugin callouts (helm.template,kustomize.build) bridged to host handlers. - CVE-2026-34988 mitigation: pinned
wasmtime = "43"across workspace (43.0.1 min). - Benchmark regression suite: sub-100ms render budget still met (documented target, untested in the current sweep)
-
InstanceAllocationStrategy::pooling(...)+Config::consume_fuel(true)— deferred. Today's Store limits (memory + epoch) cover the invariant; fuel + pooling are optimization knobs, not correctness knobs.
Exit gate: every akua render runs inside wasmtime. Native code path no longer exists for render execution. ✅ Shipped 2026-04-24.
Compiles the render path to a browser-compatible WASM bundle so @akua-dev/sdk ships as pure JSR (no CLI install, no shell-out, no child_process). Parallel track to Phase 4's wasip1 worker — different target, different consumer.
-
crates/akua-wasm— new crate,wasm32-unknown-unknowntarget.render(package_filename, source, inputs_json)+version()exported viawasm-bindgen. - Node/browser strategy chosen:
wasm-bindgenonly,--target nodejsfor the shipped Node build +--target bundlerfor the eventual browser SDK. No N-API bridge. - KCL on
wasm32-unknown-unknown— all blockers closed:-
uuidrandom-source viauuid = { features = ["js", "v4"] }under[target.'cfg(target_arch = "wasm32")']. -
instant+chronowall-clock externs gated tojs_sys::Date.now()viainstant/wasm-bindgen+chrono/wasmbindfeatures. -
kcl-language-serverUrl::from_file_path/to_file_pathcall sites (6 in production code) hoisted to aurl_from_file_path/url_to_file_pathshim on the fork — native on host,Url::parse("file://…")on wasm32. Lives on cnap-tech/kcl@akua-wasm32; upstream PR open. -
kcl_plugin_invoke_json_wasmextern narrowed totarget_os = "wasi"with a self-contained__kcl_PanicInfo__stub on non-WASI wasm32 — unblocks the JSR bundle from needing a host-providedenv.*import.
-
-
@akua-dev/sdkinstantiates the WASM bundle internally on first call — lazyawait import('./wasm/nodejs/akua_wasm.js')inpackages/sdk/src/mod.ts. Shell-out verbs pay no load cost. No@akua-dev/sdk/wasmsubpath. - Bundle staged under
packages/sdk/wasm/nodejs/bytask build:akua-wasm:nodejs; JSR publishes it alongside the TS source viajsr.jsonpublish.include. - First WASM-backed method on
Akua—renderSource(packageFilename, source, inputs?)— 3 bun tests green; 2 e2e tests against the live binary still green. - [~] Browser target deferred to v0.2.0 per docs/spikes/engines-on-wasm32-unknown-unknown.md. The helm + kustomize Go engines on
wasm32-unknown-unknown(either viaGOOS=jsrecompile or wasmer-js-in-browser) is a multi-week effort with uncertain perf; punting preserves the v0.1.0 ship window without compromising the sandbox invariant. Browser condition inpackage.jsonexportsis additive in v0.2.0 (non-breaking). - Helm + Kustomize engines from the SDK in Node — wire the existing
wasm32-wasip1.wasmartifacts through a JS-side wasmtime host (or spawn-the-CLI-transparently fallback). Packages that importhelm.template/kustomize.buildsurface a__kcl_PanicInfo__pointing users at the CLI today; must be resolved before v0.1.0 tag so the SDK's render surface isn't lopsided. - Benchmarks: cold + warm render latency via the WASM SDK vs the CLI binary — target parity-within-2× on warm calls.
Exit gate for Phase 4B (v0.1.0 scope): bun add @akua-dev/sdk / deno add / npm install → import → render → typed result, on Node 20+ / Deno / Bun. @akua-dev/sdk@0.1.0 is Node-target only; browser ships in v0.2.0. Node KCL renders proven; helm + kustomize through the SDK remain to wire.
HTTP front end for concurrent render requests. Per-request Store with preopens + limits. Depends on Phase 4 (per-render Store semantics), which v0.1.0 already ships.
-
akua serveverb — HTTP API + worker pool sized by CPU count - Per-request preopens: tenant's Package dir (read) + output dir (write) only
- Per-request resource caps: memory, fuel, epoch deadline
-
POST /renderwith package URL/digest + inputs; returns rendered manifests + summary - Observability: histogram of render times, rejection reasons (fuel, epoch, memory, invariant), per-tenant metrics
- Docs: deployment guide for hosted render
Exit gate: single akua serve process handles N concurrent renders against isolated tenant Packages, with hard resource caps and structured rejection on violation.
-
cosignmodule: ECDSA P-256 keyed verification of simple-signing payloads, digest correlation with the fetched manifest. -
oci_fetcher::fetch_with_optspulls thesha256-<hex>.sigsidecar + payload blob when a public key is configured; surfacesCosignVerify/CosignSignatureMissingdistinctly. -
akua.toml [signing] cosign_public_key = "./keys/cosign.pub"config.ResolverOptions.cosign_public_key_pemthreads through to the fetcher.akua renderloads the key off disk. - Typed CLI code
E_COSIGN_VERIFY— agents branch on "bytes failed the supply-chain gate" separately from "couldn't resolve the chart."
- Keyless verify via sigstore-rs (Fulcio cert chain + Rekor transparency log)
- SLSA v1 predicate generation on
akua publish(shipped Phase 7 B) -
akua verifywalks the attestation chain — Package → deps (direct only today; transitive deferred to Phase 7 C follow-up) -
akua.tomlstrictSigning: truemakes the signing block mandatory on every OCI dep
Exit gate (full phase): A published Package with a charts.* dep round-trips through akua publish → akua pull → akua render → akua verify, all signatures validated. Slice A lands keyed verify; slice B closes the loop with keyless + SLSA once akua publish exists.
-
oci_transportmodule: shared HTTP + bearer-challenge auth. Fetcher + puller + pusher all funnel through it. -
oci_pushermodule: monolithic upload of blob + config + manifest under akua-specific media types (application/vnd.akua.package.content.v1.tar+gzip). -
akua publish --ref <oci://…> [--tag] [--no-sign]: deterministic workspace tarball → OCI artifact.package_tar::pack_workspaceexcludes render outputs + hidden dirs + per-consumerinputs.yaml. -
oci_pullermodule: inverse of pusher, enforces akua media type + manifest-declared digest. -
akua pull --ref <oci://…> --tag <v> --out <dir>: fetches +package_tar::unpack_tointo a target directory. - Cosign signing primitive:
cosign::build_simple_signing_payload+sign_keyed(P-256 ECDSA), round-trip proven against the verify primitive Phase 6 A shipped. -
oci_pusher::push_cosign_signature: pushes the.sigsidecar atsha256-<hex>.sigwithdev.cosignproject.cosign/signatureannotation. -
akua.toml [signing].cosign_private_key:akua publishsigns by default when set.--no-signCLI override. - Typed exit codes
E_PUBLISH_FAILED/E_PULL_FAILED.
-
slsamodule: in-toto v1 statement + SLSA v1 provenance predicate builder. Materials pulled fromakua.lock;buildType = https://akua.dev/slsa/publish/v1; builder id keyed to the akua release. -
cosign::sign_dsse/verify_dsse: DSSE v1 envelope sign + verify with PAE (Pre-Auth Encoding) bindingpayloadTypeinto the signature so cross-envelope-type substitution is rejected. -
oci_pusher::push_attestation: pushes the DSSE envelope as a.attsidecar atsha256-<hex>.attwith media typeapplication/vnd.dsse.envelope.v1+json. -
akua publishauto-attests when signing is active;--no-attestdisables independently of--no-sign.PublishOutput.attestation_tagsurfaces the sidecar ref.
-
oci_puller::pull_attestation: fetches the.attsidecar from a registry + returns the DSSE envelope bytes. 404 → Ok(None) so consumers can distinguish "publisher didn't attest" from transport errors. -
akua verifyattestation chain walk: for every OCI dep inakua.lock, when a cosign public key is configured, pulls + verifies the sidecar, asserts the SLSA subject digest matches the lockfile-pinned digest. Three new typed violations:AttestationMissing,AttestationInvalid,AttestationSubjectMismatch.
-
cosign::sign_keyed_with_passphrase+sign_dsse_with_passphrase: encrypted PKCS#8 PEM (-----BEGIN ENCRYPTED PRIVATE KEY-----) supported. Unencrypted path unchanged. -
akua publishreads$AKUA_COSIGN_PASSPHRASE. No--passphraseCLI flag — argv leaks tops. - Missing passphrase on encrypted key surfaces a clear error naming the env var.
-
akua publishresolves non-path deps + embeds each chart tree at.akua/vendor/<name>/in the tarball. Resolver failures print a loud stderr warning — no silent un-vendored publishes. - Resolver consults
<workspace>/.akua/vendor/<name>/before attempting network fetch. Offline-after-pull renders now succeed for a published Package with OCI or git deps. - End-to-end round-trip integration test: pack-with-vendor → unpack → offline resolve → assert nginx resolved from
.akua/vendor/with matching digest.
- Recursive attestation walk over transitive deps — a published Package's own deps must themselves be attested. Blocked on fixture Packages that attest their dep graph.
- HSM / cosign-native key formats (PKCS#11 / cosign-cli key ref) — targets v0.3.0.
Exit gate (full phase): Published Package round-trips akua publish → akua pull → akua render with cosign signatures validated at each hop. ✅ slice A covers the core round-trip; slice B adds SLSA + offline-render-from-published-digests on top.
Shipping incrementally alongside the core. akua test is live; the
rest ship when demand justifies the surface.
-
akua test—test_*.k/*_test.krunner. Files are evaluated via the samePackageKloaderakua renderuses; KCLassert+check:failures surface as per-file test failures. Structured JSON verdict + exit code 1 on any fail. (2026-04-22) - Rego test runner (
*_test.rego) — paired with the policy engine phase - Golden-file snapshot support for render-output tests —
akua test --goldendir-diffs everypackage.k×inputs*.yamlcombo againstsnapshots/<pkg>/<stem>/;--update-snapshotsregenerates. (2026-04-22) -
akua dev— file-watch hot-reload.notify+notify-debouncer-mini;Rendered/RenderErrorevents stream to stdout (JSONL in agent mode). Watches per kept subdir non-recursively sotarget//node_modules/monorepos don't exhaustfs.inotify.max_user_watches. Broken-pipe-aware. (2026-04-22) — apply-to-cluster deferred (needs kind driver). - [~]
akua repl— KCL half shipped (2026-04-24): accumulates submitted lines into a growing.ksource, re-evaluates viaeval_source, prints top-level YAML. Meta commands.load / .reset / .show / .help / .exit. Plain-line I/O (users wanting history wrap viarlwrap). Rego half deferred until the policy engine phase is designed.
Glue that ships alongside the author loop but targets operators rather than authors. Small, composable, agent-friendly.
-
akua cache list | clear [--oci|--git|--helm] | path— inventory + reclaim the content-addressed caches under$XDG_CACHE_HOME/akua/{oci,git,helm}thatakua add+akua renderpopulate. Discriminated JSON shape{action: list|clear|path, …}. Ephemeral CI runners and disk-pressure triage withoutrm -rfguessing. (2026-04-23) -
akua auth list | add | remove— manage$XDG_CONFIG_HOME/akua/auth.tomlwithout hand-editing TOML.add --username/--tokenreads the secret from stdin (mirrorsdocker login --password-stdin— no secret on argv, no TTY dependency).listtags each entry with source ("akua" / "docker" / "both") and auth_kind, never echoing secrets. (2026-04-23) -
akua pack— local-file sibling ofakua publish. Writes the same deterministic.tar.gzto disk instead of pushing. Unlocks air-gap transfers, offline signing, and bit-diff archival. Defaults to<workspace>/dist/<name>-<version>.tar.gz(walker-skipped subdir so re-packing is idempotent);--no-vendorskips embedding deps. Emitslayer_digestmatching the OCI layer digest the registry would assign. (2026-04-23) -
akua push --tarball <path> --ref <oci://...> --tag <t>— upload a pre-packed tarball. The push half ofakua publish, decomposed so air-gap flows complete: pack here, transfer, push there. No signing / attestation (publish remains the all-in-one). (2026-04-24) -
akua inspect --tarball <path>— triage a packed.tar.gzin-memory without unpacking. Reports{package_name, version, edition}parsed from the embeddedakua.toml,layer_digest,{compressed,uncompressed}_size_bytes,file_count, sortedvendored_deps. Completes the air-gap triad: pack → transfer → inspect → push. (2026-04-24) -
akua lock [--check]— regenerateakua.lockfromakua.toml(cargogenerate-lockfileanalogue).--checkdiffs without writing and exitsE_LOCK_DRIFTon staleness — pre-commit / CI gate to catch "author edited akua.toml but forgot to re-lock." Preserves signatures on unchanged entries viamerge_into_lock; canonical TOML byte-compare for drift detection. (2026-04-24) -
akua update [--dep <name>]— intentionally bump the lock against whatever upstream now serves. Inverse stance toakua lock: wherelockrejects OCI digest drift (security),updateaccepts it and records the new digest.--depscopes the lockfile write to one entry (cargoupdate -p fooanalogue). Output lists{updated, unchanged, skipped}so operators see exactly what moved. (2026-04-24) -
akua sign+akua push --sig— offline signing pair that completes the air-gap flow.akua sign --tarball --ref --tag [--key]computesoci_pusher::compute_publish_digests()locally (pure function, matches registry-side math post-push) and writes a.akuasigsidecar (JSON; carries{oci_ref, tag, manifest_digest, simple_signing_payload, signature_b64, akua_version}).akua push --sig <path>validates ref/tag/digest against the push target pre-upload, then pushes the.sigtag via the existing cosign push path. Sign + push hosts must pin the same akua binary (config blob embedsenv!("CARGO_PKG_VERSION")). (2026-04-24) -
akua verify --tarball <path> [--sig <path>] [--public-key <path>]— offline verify against a.akuasig, no registry round-trip. Three checks: sidecar readable, local manifest_digest matches sidecar's, ECDSA signature verifies (skipped when no public key). Falls back toakua.toml [signing].cosign_public_key. Closes the air-gap loop: pack → sign → transfer → verify → push. Full chain smoke-tested end-to-end. (2026-04-24)
-
akua attest+akua push --att— offline attestation pair symmetric toakua sign. Signs an SLSA v1 DSSE envelope bound to the tarball's manifest digest; sidecar format.akuaattmirrors.akuasig. Completes the air-gap crypto story alongside signing. - Extend
akua verify --tarballwith--att <path>— DSSE attestation verify. Lands withakua attest.
akua deploy, akua query, akua trace, akua policy — cluster-facing operational verbs. Out of scope for the sandbox-first core; ship when there's demand.
Concrete boxes to check before cutting the alpha tag. Everything under "core verbs + format" is already shipped per the phases above; this is the "are we actually ready to release?" gate.
v0.1.0 doesn't cut until CLAUDE.md's promise holds end-to-end. No caveats in release notes say otherwise.
- Phase 4 shipped — every
akua render/akua dev/akua replruns inside wasmtime. No native render path exists. - [~] Phase 4B shipped — Node-side lands (
@akua-dev/sdkloadsakua-wasmlazily, first WASM-backed method green). Browser target + engine bundling outstanding. - [~] Fuel-exhaustion — fuel not wired for v0.1.0 (see Phase 4 notes above); epoch is the active CPU cap. Covered by
epoch_cap_traps_runaway_evaluationintests/sandbox_adversarial.rs. - Adversarial test: memory-bomb allocation fails cleanly against the per-render
StoreLimitsBuilder::memory_sizecap. (memory_cap_enforced_below_minimum_instance_size+memory_cap_traps_runtime_growth_past_limit.) - Adversarial test: epoch-deadline exhaustion surfaces typed error within ~1s of deadline. (
epoch_cap_traps_runaway_evaluation.) - Adversarial test: absolute plugin path rejected with
PluginPaniccarrying theE_PATH_ESCAPEmarker. (absolute_plugin_path_rejected_at_guard.) - Adversarial test: parent-relative escape (
../../etc) rejected. (parent_relative_plugin_path_rejected_at_guard.) - Adversarial test: symlink-escape via
canonicalize— a symlink that textually looks under-dir but resolves outside still fails. (symlink_escape_rejected_through_canonicalize.) - Adversarial test: absolute
importrejected by KCL's external-pkg resolver. (absolute_import_path_rejected_by_kcl_resolver.) - Adversarial test: no
$PATHbinary reachable from a render. Running the suite withPATH=/nonexistentstill passes. (Structural — wasmtime never exposes subprocesses — but an explicit belt-and-braces test is still owed.) - Security model doc — "Operational guidance today (pre-Phase 4)" section deleted. The invariant holds.
- CI runs the adversarial suite on every PR. A failure blocks merge.
- Release notes draft — what's in (feature absences only, never invariant caveats), what comes in v0.2.0 (hosted multi-tenant via
akua serve) -
cargo test -p akua-core -p akua-cligreen on CI across Linux + macOS -
akua --versionmatches the tag - Every
examples/<name>/renders throughakua renderwithout errors - Every
examples/<name>/passesakua check && akua lint && akua test - One curated upstream Package published to a public OCI registry for
akua pullsmoke-testing
Many markdown files predate recent shipping and make claims that no longer match reality. A single focused pass before release, not phase-by-phase.
Out of scope for this sweep: CLAUDE.md. That file is read by agents every session and describes the target invariants (sandboxed by default, signed + attested by default, no shell-out ever, thirty-verb surface). Pinning it to "today's state" would rot within days as phases ship. Agents need the north star — reality lives in the human-facing docs below and the Release tracks section above.
- README.md (repo root) — reflect the shipped verb set (~27 today, not the "thirty verbs" line from CLAUDE.md's target state). Name the v0.1.0 caveats explicitly: process-sandbox is Phase 4, Rego layer is post-0.5.0, full verb roster is CLAUDE.md's target, not v0.1.0. Humans read this; agents read CLAUDE.md.
- docs/cli.md — list every shipped verb + every exit code they emit. Drop any entry for verbs that don't yet exist (
policy,deploy,query,trace,audit,infra,export). - docs/cli-contract.md — audit against what the universal args actually accept today. Every "MUST" must be true of every verb.
- docs/package-format.md — matches the
PackageKloader's current parse (no drift since last doc refresh). - docs/lockfile-format.md — matches
AkuaLock::saveoutput byte-for-byte. - docs/security-model.md — "Operational guidance today (pre-Phase 4)" section deleted; feature-status table flipped to reflect Phase 4 shipped.
- docs/embedded-engines.md — list only engines that actually ship (helm + kustomize today). Mark OPA / Regal / CEL / Kyverno / kro as future.
- docs/policy-format.md — if Rego isn't wired up yet, mark the whole doc as target-state with a prominent "NOT YET SHIPPED" banner.
- docs/agent-usage.md — lists the verbs agents are expected to invoke and the JSON shapes they should parse. Remove any verb that's not yet shipped.
- docs/performance.md — benchmark numbers refreshed from main; drop shell-out baseline references if present.
- docs/impl-plan.md — cross-check against roadmap.md; remove duplication, or collapse to a pointer if this file has drifted past usefulness.
- docs/sdk.md — if the TypeScript SDK isn't shipped, mark as target-state or remove the reference from CLAUDE.md.
-
examples/*/README.md— every example's README describes what it actually does today. Remove "shell-out" references; point to Phase 1/3 WASM engines. - Package author's README template —
akua initscaffolds a README that compiles on firstakua render.
- Air-gap flow end-to-end:
akua pack→akua sign→ transfer →akua verify --tarball→akua push --sig, runnable snippet with a freshly-generated key. - Publishing story:
akua publish+[signing]config + what a consumer sees onakua pull+akua verify. - Operational verbs crib sheet:
akua cache,akua auth,akua lock [--check],akua update.
Depends on Phase 4B. The SDK does not shell out. Consumers bun add @akua-dev/sdk (or deno/pnpm/npm equivalents) and call render / publish / verify in-process.
Foundation from PR #19 — ts-rs + schemars pipeline, error hierarchy keyed on ExitCode + StructuredError, ajv runtime validation, Standard Schema v1 adapter, JSR publish pipeline, 24 bun tests — all carries over. The shell-out transport in that PR is scaffolding; the real transport is the akua-wasm bundle loaded internally.
Ship only @akua-dev/sdk. Everything — typed class, errors, Standard Schema adapter, WASM bundle instantiation — lives behind one import. No @akua-dev/sdk/wasm, no @akua-dev/sdk/browser, no @akua-dev/sdk/node subpaths for v0.1.0.
Runtime differences (Node / Deno / Bun / browser) resolve through package.json's exports + conditions ("browser", "node", "default"), not through the consumer typing a different subpath. Convention in 2026 across Vercel AI SDK, Anthropic SDK, Zod, TensorFlow.js, etc. — consumers should never have to pick a runtime-specific import.
Reasons this matters:
- No decision for the consumer. Forcing
@akua-dev/sdk/browsermeans every consumer picks based on runtime, which is exactly what conditional exports exist to avoid. - Smaller stability surface. Every subpath is a public API. Exposing
@akua-dev/sdk/wasmlocks the raw WASM-bindings shape into the stability contract forever. A minor internal refactor becomes a breaking change. Internal-only means we can refactor freely. /wasmisn't wrong, just premature. Real use cases (web-worker isolation where the main thread only needs the class layer; tree-shaking the error hierarchy out of a tight bundle) may materialize. Add@akua-dev/sdk/wasmas a minor-version addition if and when demand shows up. Don't speculate in v0.1.0.
- Merge PR #19 (contract layer + error hierarchy + JSR pipeline — all transport-agnostic)
- Replace the shell-out
Akuaclass with WASM-backed dispatch. Method → internalWebAssembly.instantiate'd export call → typed result. No process spawn. No exposed bindings surface. -
package.json(orjsr.json)exportsresolves the runtime-specific build automatically via conditions — single"."key, no runtime subpaths. - Wrap every verb against the same typed pattern. Blocking set for v0.1.0:
init,render,check,lint,fmt,test,dev,repl,add,remove,tree,lock,update,verify,diff,inspect,pack,push,sign,pull,publish,cache,auth. - Per-verb tests mirror
whoami's shape — one payload assertion, one error-path assertion, validated against the generated JSON Schema. -
@akua-dev/sdkREADME —bun add @akua-dev/sdk, one-screen render example (same import works in Node + browser), link to everyAkuaErrorsubclass'sakua.dev/errors/<code>docs URL. - docs/sdk.md — WASM-in-process transport, single-entrypoint rationale, bundle size, lazy-init semantics, Node + Deno + Bun + browser matrix.
- Tag
sdk-v0.1.0to exercise the publish workflow — dry-run viaworkflow_dispatchfirst. - Drift guard (
task sdk:check) wired into the v0.1.0 CI matrix so generated types + the WASM bundle + committed artifacts stay in sync. - Runtime matrix in CI: smoke-test the JSR artifact on Node (current LTS), Deno (latest), Bun (pinned
.mise.tomlversion), plus a headless-browser check. Singleimport { Akua } from "@akua-dev/sdk"across all five.
- ❌ Shell-out in the render path. Invariant. No feature flag, no
--unsafe-host, no exceptions. If an engine isn't WASM-ready, the feature doesn't ship until it is. - ❌ Non-Kubernetes deploy targets (Fly / Workers / Lambda / systemd). akua is Kubernetes-native rendering; other substrates can consume the rendered YAML.
- ❌ Cluster-side controllers for akua kinds. akua renders; reconcilers (ArgoCD / Flux / kro / Crossplane) apply.
- ❌ Runtime (in-cluster) rendering. Render at CI time; git contains what runs.
- ❌ Curated central package catalog. First-party publishing by each maintainer is the model; akua provides signing + distribution infrastructure, not the shelves.
- ❌ PaaS / hosted runtime. akua is a toolkit; CNAP (and similar) is the hosted platform that consumes akua Packages.
[ ]— not started[-]— in progress[x]— shipped onmain[~]— shipped, known follow-ups outstanding
When checking a box, link the commit: [x] Task name — abc1234.