Skip to main content
nika:image_fx is the deterministic sibling of nika:image_generate: a pure pixel transform — no provider, no network, no clock. Style a PNG through an ordered pipeline of effects and get an artifact that is byte-identical forever for identical (input, args). That determinism is the point, not a detail. The artifact’s sha256 joins the run’s hash-chained trace, re-rendering the recipe is the tamper check, and the full recipe (contract tag image_fx/v1 · input sha256 · seed · ops) travels inside the PNG as a nika tEXt chunk — no timestamp, ever. Your workflow draws its own receipts.

The 30-second pipeline

Run it twice: the second run reports skipped_existing: true and the artifact hash never moves. Mutate one byte of the PNG and a re-render no longer matches — that mismatch is the tamper detection.

The op vocabulary (closed · v1)

ops: is an ordered list of single-key maps — a linear pipeline by design. Branching and fan-out belong to the workflow (with:/after: edges · for_each), never inside the builtin. Palettes: presets bw · gray4 · gameboy · cga · okabe_ito, or an inline list of 2–256 colors. Unknown op parameters are rejected loudly (NIKA-BUILTIN-IMAGE_FX-001) — a typo’d knob never silently lies about the style.
Input is PNG in v1 (depth 8 · no interlace). Non-PNG fails with a typed hint: produce PNG upstream (image_generate with format: png) or convert once via exec:. Decode is budget-gated from the header before any decompression — hostile dimension claims are rejected, never allocated.

Why deterministic styling matters

Every incumbent styling path is nondeterministic by default — image tools timestamp their outputs, and generative models can’t reproduce bytes across hardware at all. That makes styled assets un-verifiable: you can sign them, but you can’t re-derive them. image_fx inverts that: integer/fixed-point pixel math, a seeded noise stream, zero wall-clock. So the artifact can be:
  • chained — its sha256 lands in the trace (nika trace verify covers it);
  • re-rendered — the same input + the recipe reproduces the exact bytes;
  • carried — the recipe rides the artifact itself, so any holder of the input can verify the styling.
One recipe, one hash, forever. Batch-style a hundred campaign images with for_each and every one of them is a receipt.

Output contract

Artifact bytes never ride outputs (the disk law). Text artifacts (ascii with emit: text|ansi) land as .txt/.ans, report width/height 0, and are sha256’d like any other artifact. Errors are typed NIKA-BUILTIN-IMAGE_FX-001..006 — invalid args · input read · unsupported format · decode · pixel budget · save — plus the boundary NIKA-SEC-004 when a path resolves outside permits.fs.