> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nika.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# nika:image_generate

> Usage, declared contract and source links for this Nika builtin.

export const Ecosystem = () => <CardGroup cols={3}>
    <Card title="Documentation" icon="book" href="/introduction">
      Guides, reference contracts and working examples.
    </Card>
    <Card title="Nika Lab" icon="diagram-project" href="https://github.com/supernovae-st/nika-lab">
      Internal workspace and graph projections of the spec, documentation and code.
    </Card>
    <Card title="Language spec" icon="file-contract" href="https://github.com/supernovae-st/nika-spec">
      the nine-key envelope, Apache-2.0. The contract these docs explain.
    </Card>
    <Card title="Engine source" icon="gem" href="https://github.com/supernovae-st/nika">
      Rust, AGPL-3.0-or-later. Every commit public.
    </Card>
    <Card title="TypeScript SDK" icon="code" href="/sdk/overview">
      Use one typed lifecycle locally or against authenticated nika serve HTTP.
    </Card>
    <Card title="Editor extension" icon="puzzle-piece" href="https://marketplace.visualstudio.com/items?itemName=supernovae.nika-lang">
      VS Code · Cursor · Windsurf: check-as-you-type, DAG view, trace replay.
    </Card>
    <Card title="Homebrew tap" icon="beer-mug-empty" href="https://github.com/supernovae-st/homebrew-tap">
      `brew install supernovae-st/tap/nika` for the latest tagged CLI release.
    </Card>
    <Card title="SuperNovae" icon="star" href="https://supernovae.studio">
      The Paris studio crafting Nika. 🦋
    </Card>
  </CardGroup>;

[Builtins catalog](/reference/builtins) / `nika:image_generate`

<CardGroup cols={2}>
  <Card title="Media" icon="layer-group">Tool family from the canonical registry.</Card>
  <Card title="invoke" icon="code" href="/reference/language/words/invoke">Call this builtin through the invoke verb.</Card>
</CardGroup>

## Read this contract

This reference preserves the specification at the revision linked below. Source examples are **fragments**: their surrounding tasks, inputs and permissions are not supplied here. Consult [engine status](/reference/status) and `nika catalog --tools --json` for the installed implementation; a registry declaration is not a runtime qualification.

## Usage and behavior

```yaml illustration theme={"system"}
invoke:
  tool: "nika:image_generate"
  args:
    provider: mock                # mock (offline) | local (sovereign) | gemini | openai | xai — inferable from model: (local excepted)
    prompt: "OG hero — a monarch butterfly over a nebula, editorial photo"
    aspect_ratio: "16:9"
    output_dir: "./assets/og"
    filename_prefix: "launch-hero"
    metadata: { campaign: "spring", page_slug: "qr-menu" }
```

Text-to-image generation as an *asset pipeline*, not a blob pipe: images are
**saved to `output_dir:`** and the output carries `paths + dimensions +
sha256 (+ a provenance manifest)` — **image bytes NEVER ride workflow
outputs** (no base64 in `tasks.X.output`, logs, or traces · normative).

| Arg                | Notes                                                                                                                                                                                                                                                                                                                                                            |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `provider`         | `local` · `openai` · `gemini` · `xai` · `mock` — optional when inferable from `model:` (`gpt-image*`→openai · `gemini-*`→gemini · `grok*`→xai · `mock*`→mock · `local` is NEVER inferred: its model names are server-specific)                                                                                                                                   |
| `model`            | per-provider default (reference engine 2026-07: `stablediffusion` for local — the LocalAI convention · SD-family servers also honor the `positive \| negative` split INSIDE `prompt:` (LocalAI pipe syntax) — no separate arg needed · `gpt-image-2` · `gemini-3.1-flash-image` · `grok-imagine-image` — the `-quality` tier is the model knob · `mock-image-1`) |
| `prompt`           | **required** · the creative brief · may use `${{ … }}`                                                                                                                                                                                                                                                                                                           |
| `mode`             | `generate` (default · text→image) · `edit` (source image(s) + instruction → image · M2.2 · 2026-07-06)                                                                                                                                                                                                                                                           |
| `image` / `images` | mode:edit source · one path (`image:`) XOR many (`images:` · capped per provider: openai 16 · gemini 14 · xai 3) · **read + permit-gated** (`permits.fs.read` must cover them — the mirror of the save boundary)                                                                                                                                                 |
| `mask`             | mode:edit optional pixel-mask path · openai/local only — a mask on an instruction-only provider (gemini/xai) is REFUSED loudly, never silently dropped (the output would be wrong outside the region)                                                                                                                                                            |
| `n`                | 1..=10 variants (engines MAY satisfy n via sequential provider calls · documented per adapter)                                                                                                                                                                                                                                                                   |
| `aspect_ratio`     | closed set `1:1 · 16:9 · 9:16 · 4:3 · 3:4 · 3:2 · 2:3 · 21:9`                                                                                                                                                                                                                                                                                                    |
| `size`             | exact `WIDTHxHEIGHT` or `auto` · an exact size WINS over `aspect_ratio:` (with a warning) · providers that render size CLASSES fold it (loudly)                                                                                                                                                                                                                  |
| `quality`          | `auto · low · medium · high · ultra` — folded per provider capability, never silently                                                                                                                                                                                                                                                                            |
| `format`           | `png` (default) · `jpeg` · `webp` — **magic bytes are the authority**: what actually landed decides mime/extension, a mismatch is a warning                                                                                                                                                                                                                      |
| `compression`      | 0..=100 · jpeg/webp only                                                                                                                                                                                                                                                                                                                                         |
| `background`       | `auto · transparent · opaque` · transparent REQUIRES an alpha-capable format (png/webp) and a supporting provider/model                                                                                                                                                                                                                                          |
| `seed`             | best-effort (providers without seed support warn + drop)                                                                                                                                                                                                                                                                                                         |
| `reference_images` | RESERVED (rejected loudly in v0.1 · media roadmap)                                                                                                                                                                                                                                                                                                               |
| `provider_options` | vetted pass-through (unknown keys warn, never crash) · openai `{moderation, user}` · gemini `{thinking_level, image_size}` · xai `{user, resolution: 1k\|2k}`                                                                                                                                                                                                    |
| `output_dir`       | **required** · rides the declared `permits.fs` boundary (`NIKA-SEC-004` · gated per final path BEFORE any I/O)                                                                                                                                                                                                                                                   |
| `filename_prefix`  | filename stem (else `metadata.page_slug`, else `image`) — sanitized `[a-z0-9._-]`, traversal-free by construction                                                                                                                                                                                                                                                |
| `save`             | `true` (v0.1 contract · `save: false` is RESERVED — rejected loudly)                                                                                                                                                                                                                                                                                             |
| `manifest`         | write the provenance manifest JSON beside the assets (default `true`)                                                                                                                                                                                                                                                                                            |
| `metadata`         | free provenance object (campaign · page\_slug · locale · …) echoed into output + manifest                                                                                                                                                                                                                                                                        |
| `timeout_ms`       | per-request deadline · default 180000 (local: 300000 — CPU renders run minutes) · 1000..=600000                                                                                                                                                                                                                                                                  |
| `debug`            | echo the sanitized raw provider response (base64 payloads STRIPPED · headers never included)                                                                                                                                                                                                                                                                     |

**Filenames (normative grammar)** ·
`{stem}-{provider}-{modelslug}-{index}-{sha8}.{ext}` · every component
sanitized to `[a-z0-9._-]` with no separators; name collisions probe
`-2..-99`; an IDENTICAL payload already on disk is an idempotent re-run
(no duplicate). The manifest lands beside the assets as
`{stem}-{provider}-{modelslug}-{batchsha8}.manifest.json`
(`manifest_version: 1` · resolved request echo · per-image
paths/dimensions/sha256 · usage · warnings · caller `metadata:` — and
NEVER a credential: keys live a composition layer away by construction).

**Output (normalized · every provider + mock)** · `&#123; provider, model, mode,
prompt, revised_prompt, provider_text, created_at, count, images: [&#123; index,
path, filename, mime_type, format, width, height, size_bytes, sha256,
provider, model, seed, variant_id, warnings, metadata &#125;], usage:
&#123; input_tokens, output_tokens, total_tokens, thoughts_tokens &#125;, cost_usd,
warnings, manifest_path, output_dir &#125;` — absent usage axes are `null`,
never zero-that-looks-real. `cost_usd` is the render's REAL spend when
the provider reports it exactly (xai bills `cost_in_usd_ticks` in the
response · 1 cent = 10⁸ ticks) and `null` otherwise — never an estimate
dressed as truth. **Invoke-cost metering (normative)** · a tool whose
structured output carries a top-level numeric `cost_usd` reports real
spend; engines SHOULD meter it into the run's cost ledger through the
same honest-spend channel `infer:` rides.

**Security (engine MUST)** · provider endpoints are ENGINE-FIXED constants
(never workflow data — the provider egress is engine transport, exactly like
`infer:`; `permits.net.http` does not govern it, `permits.tools` +
`permits.fs` DO; the `local` base URL is ENGINE CONFIG resolved at the
composition root — `NIKA_IMAGE_LOCAL_URL`, default LocalAI's
`http://localhost:8080` — still never workflow data) · decode validation is HEADER-ONLY (magic bytes + PNG
IHDR / JPEG SOF / WebP VP8-VP8L-VP8X dimensions · no pixel decode → no
decompression-bomb surface · declared-vs-actual mismatch is a warning, a
non-image payload is a hard error) · atomic writes (temp+rename) · API keys
are engine-configured (env/config at the composition root), never workflow
args, never logged, never echoed.

**Warnings (normative shape)** · every tolerated-but-lossy mapping lands a
stable `code: message` warning string (`size_conflict:` ·
`compression_ignored:` · `seed_unsupported:` · `quality_folded:` ·
`format_mismatch:` · `gemini_size_class:` · `xai_size_class:` ·
`aspect_remapped:` · `count_shortfall:` · `revised_prompt_clamped:` ·
`provider_option_unknown:` · `provider_text_clamped:` · …) in `warnings` — silent degradation is
non-conformant.

**`provider_text`/`revised_prompt` are captions, not payload channels
(normative)** · a provider's accompanying text MUST be bounded by the
engine (the reference engine clamps at 2 000 chars with a
`provider_text_clamped:` warning) — a multimodal response interleaving
megabytes of text (or base64-shaped junk) must never ride workflow
outputs, the manifest, or the `debug:` echo unbounded.

**Result URLs are never fetched (normative)** · engines request
`response_format: b64_json` on url-capable wires (openai-compat · xai) and
MUST refuse a url-only response with an actionable error — fetching a
provider-supplied result URL would reopen the SSRF/net-boundary surface
the const-endpoint design closed. The provenance manifest and output
carry `endpoint_host` (which server actually rendered the asset — load-
bearing for `local`, where the endpoint is configurable).

**Content credentials: detect-and-PRESERVE (normative)** · upstream
generators sign their returned bytes (OpenAI images and Google's media
models emit C2PA manifests as of mid-2026 · carriage: PNG `caBX` chunk ·
JPEG APP11 JUMBF · `WebP`/WAV RIFF `C2PA` · MP3 `ID3v2` GEOB
`application/c2pa`), and C2PA hard bindings hash the asset's byte
ranges — **any engine-side insert into a signed payload converts valid
credentials into « present but tampered », which is worse than
stripping them**. Engines MUST detect these signals before any in-file
write, MUST stand their own embed down when credentials are present,
and SHOULD surface presence as `content_credentials` in output +
manifest (values are DETECTION labels — an engine that has not
cryptographically validated the manifest must never say « verified »).
Provider-declared watermarking (e.g. `SynthID` — detectable only by the
vendor) MAY be surfaced as a `watermark_declared` manifest fact, marked
as declared, never as verified. Preserving machine-readable marks is
also the EU AI Act Article 50 robustness expectation (in force
2026-08-02).

**Provenance travels IN the file (PNG)** · engines SHOULD embed the
deterministic provenance core (tool · engine version · provider · model ·
clamped prompt · seed — no timestamp, so byte-determinism holds) as a
`nika` tEXt chunk in saved PNG renders — the ComfyUI/InvokeAI
interchange practice: a sidecar manifest answers « where does this come
from? » only until the file is copied without it. JPEG/WebP have no
equally-universal text container and are documented as manifest-only,
never silently faked.

Throws · `NIKA-BUILTIN-IMAGE_GENERATE-001` invalid arguments (incl. the v0.1
RESERVED options · `validation_error`) · `-002` provider unavailable
(missing credentials / image plane unwired · `validation_error`) · `-003`
provider request failed (`network_error` · `transient: true` for
5xx/408/429 + timeout/connection · `details.status_code`) · `-004` no
image / malformed response (`tool_error`) · `-005` content policy block
(moderation / safety finish reasons · `security_error` · never transient ·
`details {finish_reason? · moderation_details?}`) · `-006` save/manifest
write failed (`tool_error`) · `-007` image validation failed (magic
mismatch · dimension/byte bounds · `tool_error`). Plus the boundary
`NIKA-SEC-004` (an `output_dir:` outside `permits.fs.write`).

***

## Related concepts

<CardGroup cols={2}>
  <Card title="Arguments" icon="sliders" href="/reference/language/words/args">Pass the values required by the tool contract.</Card>
  <Card title="Permissions" icon="shield" href="/concepts/security">Understand authority before granting effects.</Card>
  <Card title="Errors and recovery" icon="rotate" href="/reference/error-codes">Read diagnostics and choose a recovery policy.</Card>
  <Card title="Workflow templates" icon="file-code" href="/guides/templates">Put the fragment inside a complete workflow.</Card>
</CardGroup>

## Contract provenance

[Read the pinned specification](https://github.com/supernovae-st/nika-spec/blob/f8c5df2127c7867b2e51a2cd5712fa9d8461e73e/stdlib/builtins-v0.1.md#L532) · [Canonical registry](https://github.com/supernovae-st/nika-spec/blob/f8c5df2127c7867b2e51a2cd5712fa9d8461e73e/canon/builtins.yaml).

Tool identity: `nika:image_generate`. Specification revision: `f8c5df2127c7`.
The Lab language identity is `language:tool:image_generate`; the registry view is `ecosystem:tool/nika:image_generate`. They refer to this contract without merging their graph identities.

<Ecosystem />
