AGPL-3.0-or-later · forever.

[ INTENT AS CODE ]

Useful AI work shouldn’t disappear into chats.

Nika turns repeatable AI work into files you can run, review, diff and share: audited before a token is spent, with a verifiable receipt after every run. An agent writes the plan; you review it; the runtime enforces it.

Repeatable AI work as a file: audited before a token is spent, a receipt after every run. The runtime enforces it.

See it runtry it in your browseror in VS Code / Cursor

v0.104.0macOS · LinuxAGPL-3.0

see it run

nika: v1workflow: daily-brief# local model · your notes never leavemodel: ollama/llama3.2:3b# the file IS the blast radiuspermits:  fs: { read: [ ./notes/* ], write: [ ./brief.md ] }  tools: [ "nika:read", "nika:write" ]tasks:  - { id: notes, invoke: { tool: "nika:read", args: { path: ./notes/today.md } } }  - { id: inbox, invoke: { tool: "nika:read", args: { path: ./notes/inbox.md } } }  - { id: calendar, invoke: { tool: "nika:read", args: { path: ./notes/calendar.md } } }  - id: triage    depends_on: [inbox]    with:      inbox: ${{ tasks.inbox.output }}    infer: { prompt: "Flag what is urgent: ${{ with.inbox }}", max_tokens: 300 }  - id: agenda    depends_on: [calendar]    with:      calendar: ${{ tasks.calendar.output }}    infer: { prompt: "Plan the day around: ${{ with.calendar }}", max_tokens: 300 }  - id: draft    depends_on: [notes, triage, agenda]    with:      notes: ${{ tasks.notes.output }}      triage: ${{ tasks.triage.output }}      agenda: ${{ tasks.agenda.output }}    infer:      prompt: "Write the morning brief. Notes: ${{ with.notes }} Urgent: ${{ with.triage }} Plan: ${{ with.agenda }}"      max_tokens: 500  - id: save    depends_on: [draft]    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:write"      args: { path: ./brief.md, content: "${{ with.draft }}" }outputs:  brief: "${{ tasks.draft.output }}"
source

01

Watch the file become the run.

nika: v1workflow: daily-brief# local model · your notes never leavemodel: ollama/llama3.2:3b# the file IS the blast radiuspermits:  fs: { read: [ ./notes/* ], write: [ ./brief.md ] }  tools: [ "nika:read", "nika:write" ]tasks:  - { id: notes, invoke: { tool: "nika:read", args: { path: ./notes/today.md } } }  - { id: inbox, invoke: { tool: "nika:read", args: { path: ./notes/inbox.md } } }  - { id: calendar, invoke: { tool: "nika:read", args: { path: ./notes/calendar.md } } }  - id: triage    depends_on: [inbox]    with:      inbox: ${{ tasks.inbox.output }}    infer: { prompt: "Flag what is urgent: ${{ with.inbox }}", max_tokens: 300 }  - id: agenda    depends_on: [calendar]    with:      calendar: ${{ tasks.calendar.output }}    infer: { prompt: "Plan the day around: ${{ with.calendar }}", max_tokens: 300 }  - id: draft    depends_on: [notes, triage, agenda]    with:      notes: ${{ tasks.notes.output }}      triage: ${{ tasks.triage.output }}      agenda: ${{ tasks.agenda.output }}    infer:      prompt: "Write the morning brief. Notes: ${{ with.notes }} Urgent: ${{ with.triage }} Plan: ${{ with.agenda }}"      max_tokens: 500  - id: save    depends_on: [draft]    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:write"      args: { path: ./brief.md, content: "${{ with.draft }}" }outputs:  brief: "${{ tasks.draft.output }}"

[ 01 ]run together ×3

notesinvokenika:read✓ 3ms
inboxinvokenika:read✓ 1ms
calendarinvokenika:read✓ 1ms

[ 02 ]run together ×2

triageinferollama/llama3.2:3b✓ 29.2s
agendainferollama/llama3.2:3b✓ 27.0s

[ 03 ]then

draftinferollama/llama3.2:3b✓ 29.6s

[ 04 ]then

saveinvokenika:write✓ 14ms
daily-brief.nika.yaml
nika run daily-brief.nika.yaml
workflow daily-brief
scheduled 7 tasks · notes inbox calendar triage agenda draft save
notes invoke · nika:read
notes 3ms
inbox invoke · nika:read
inbox 1ms
calendar invoke · nika:read
calendar 1ms
triage infer · ollama/llama3.2:3b
triage 29.2s · 29 tok
agenda infer · ollama/llama3.2:3b
agenda 27.0s · 300 tok
draft infer · ollama/llama3.2:3b
draft 29.6s · 260 tok
save invoke · nika:write
save 14ms
run complete · exit 0 · 58.7s
exit 07 tasks ran58.7swrote brief.md$0.00localollama/llama3.2:3b
recorded · nothing staged

recorded from a real nika run · replayed by your scroll · nothing staged

01.1

Press play. It really ran.

The same file, executed by the engine with a local model on a real machine. What you watch is the recorded event stream.

daily-brief.nika.yaml
nika run daily-brief.nika.yaml
workflow daily-brief
scheduled 7 tasks · notes inbox calendar triage agenda draft save
notes invoke · nika:read
notes 3ms
inbox invoke · nika:read
inbox 1ms
calendar invoke · nika:read
calendar 1ms
triage infer · ollama/llama3.2:3b
triage 29.2s · 29 tok
agenda infer · ollama/llama3.2:3b
agenda 27.0s · 300 tok
draft infer · ollama/llama3.2:3b
draft 29.6s · 260 tok
save invoke · nika:write
save 14ms
run complete · exit 0 · 58.7s
exit 07 tasks ran58.7swrote brief.md$0.00 · ollama/llama3.2:3b

recorded from a real nika run · replayed at reading pace · nothing staged

01.2

One file. A plan you can read.

The engine derives the order from the wiring: a with: binding is a data edge, an after: entry a control edge. Steps with no edge between them run together. Nothing runs that is not written in the file.

[ 01 ]run together ×3

notesinvokenika:read 3ms
inboxinvokenika:read 1ms
calendarinvokenika:read 1ms

[ 02 ]run together ×2

triageinferollama/llama3.2:3b 29.2s
agendainferollama/llama3.2:3b 27.0s

[ 03 ]then

draftinferollama/llama3.2:3b 29.6s

[ 04 ]then

saveinvokenika:write 14ms

durations recorded from the real run · hover a step to trace its wires

[ THE BOUNDARY ]

02

What it's allowed to touch.

The permits: block is part of the file you review. It is the whole list. Everything not on it is denied by default, before it runs. The full reference · families, secrets, the always-on floor · lives at /boundary.

daily-brief.nika.yaml · lines 7–9
permits:  fs: { read: [ ./notes/* ], write: [ ./brief.md ] }  tools: [ "nika:read", "nika:write" ]

the same file as above · the permits block, verbatim

don't write it by hand:nika check --infer-permitsprints the tightest boundary the plan needs. Paste it in, loosen it yourself, or leave it strict.

  • fs:which files it may read and write
  • tools:which tools it may call

if a step reaches outside the list, say

write ~/.ssh/config   NIKA-SEC-004

effect outside the declared permits: capability boundary. Blocked before it runs, never logged after the fact. (An example: the recorded run above stayed in bounds, exit 0.)

03

Chats evaporate. Files compound. The prompts you perfect, the steps an agent improvises: gone with the session. Nika turns that work into a file: readable, versioned, runnable.

daily-brief.nika.yaml
nika: v1workflow: daily-brief# local model · your notes never leavemodel: ollama/llama3.2:3b# the file IS the blast radiuspermits:  fs: { read: [ ./notes/* ], write: [ ./brief.md ] }  tools: [ "nika:read", "nika:write" ]tasks:  - { id: notes, invoke: { tool: "nika:read", args: { path: ./notes/today.md } } }  - { id: inbox, invoke: { tool: "nika:read", args: { path: ./notes/inbox.md } } }  - { id: calendar, invoke: { tool: "nika:read", args: { path: ./notes/calendar.md } } }  - id: triage    depends_on: [inbox]    with:      inbox: ${{ tasks.inbox.output }}    infer: { prompt: "Flag what is urgent: ${{ with.inbox }}", max_tokens: 300 }  - id: agenda    depends_on: [calendar]    with:      calendar: ${{ tasks.calendar.output }}    infer: { prompt: "Plan the day around: ${{ with.calendar }}", max_tokens: 300 }  - id: draft    depends_on: [notes, triage, agenda]    with:      notes: ${{ tasks.notes.output }}      triage: ${{ tasks.triage.output }}      agenda: ${{ tasks.agenda.output }}    infer:      prompt: "Write the morning brief. Notes: ${{ with.notes }} Urgent: ${{ with.triage }} Plan: ${{ with.agenda }}"      max_tokens: 500  - id: save    depends_on: [draft]    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:write"      args: { path: ./brief.md, content: "${{ with.draft }}" }outputs:  brief: "${{ tasks.draft.output }}"

the session ends · the file stays

Seeing the steps is not the same as enforcing them. Here is what each black box trades away.

Chat sessions

  • Great for exploring
  • Gone when the tab closes
  • Different answer every time

A file you keep

  • Runs again tomorrow
  • Same steps, same order
  • Diff it like code

Glue scripts

  • 200 lines of Python + retries
  • One person understands it
  • Breaks when an API changes

Four verbs

  • The YAML is the logic
  • Anyone can read it
  • Engine handles retries & order

Cloud automations

  • Runs on their servers
  • Per-seat, per-run pricing
  • Your data leaves the building

Your machine

  • One binary, runs local
  • Free, AGPL forever
  • Nothing leaves unless you say so

04

The run explains itself.

One file ran once: audited before it started, storyboarded while it ran, replayable after it ended. Every frame below is real output of the real binary, captured on a terminal against committed fixtures. Offline, on the mock model, for zero dollars. How the proof works · graph, receipt, conformance · lives at /proof.

recorded from a real nika run · replayed at reading pace · nothing staged
Terminal capture of nika check on the signature workflow: the audit ladder passes with a cost floor warning and one permits hint, rc=0.
the pre-flight ladder: plan · cost · secrets · types · tools · args · schema · permits, then one hint. rc=0.

05

What an agent can do. Declared, not hidden.

Four verbs are the only native execution models. Every action explicit and typed, never improvised from a hidden prompt. Everything callable is a tool under invoke, and tools are allow-listed. Everything about ordering is the plan: which tasks wait on which. No fifth verb, ever.

5.3invoke

Use a tool. Fetch a page, write a file, call GitHub. Every tool explicit.

5.3.1 builtins5.3.2 extract modes5.3.3 MCP servers

use-a-tool.nika.yaml
nika: v1workflow: use-a-tooltasks:  - id: page    invoke:      tool: "nika:fetch"      args: { url: "https://nika.sh" }

5.4agent

Delegate. An autonomous loop, on a leash you can read.

5.4.1 tool allow-list5.4.2 max turns5.4.3 the human gate

delegate.nika.yaml
nika: v1workflow: delegatemodel: ollama/llama3.2:3btasks:  - id: audit    agent:      prompt: "Find every dead link in ./docs"      tools: [ "nika:read", "nika:fetch" ]

infer ◇ · exec ▷ · invoke ◆ · agent ✦   ·  the whole operation space, declared in one file

06

What an agent can be permitted to use.

The language stays four verbs. The standard library does the rest: 28 builtins, 17 model providers, and any of your agent tools (MCP servers) your editor already uses. All reached the same way: invoke:, and none of it runs unless the file's permits: allows it.

0128builtin tools · nothing to install
0217model providers · 5 local, 11 cloud, 1 mock
039extract modes on fetch
04MCPnativeyour agent tools (MCP) · any server, via the same verb
BuiltinsThe everyday tools. All called with invoke:.28 tools · 4 families

Files· 5

read· read a filewrite· save a fileedit· patch a fileglob· find filesgrep· search text

Data· 9

jq· transform JSONconvert· between formatsvalidate· check a schemajson_diff· what changedjson_merge_patch· merge JSONcompose· chain toolshash· fingerprint datauuid· fresh iddate· now · parse · format

Web· 1

fetch· get a page · 9 extract modes

Flow· 13

assert· check a conditiondone· end the loopwait· pauseemit· send an eventlog· say somethingnotify· ping a humanprompt· ask a humaninspect· debug a valuechart· see the docsdecide· see the docsimage_fx· see the docsimage_generate· see the docstts_generate· see the docs
ProvidersPick per task or per file. provider: ollama runs offline.17 total

Local runtimes· 5 · no cloud needed

OllamaLM Studiollama.cppLocalAIvLLM

Cloud · open-weight first· 11

MistralAnthropicOpenAIGeminiDeepSeekxAIGroqOpenRouterhuggingfacenvidiamoonshot

Test· 1

mock· deterministic · zero keys · CI-runnable
Extract modesHow fetch turns a page into typed output.9 modes
articlefeedjqlinksmarkdownmetadataselectorsitemaptext
MCPAny of your agent tools (MCP servers), reached as mcp:.native · unbounded
mcp:· the server your editor already usesstdiohttpdefault-deny· tools whitelisted in the file

every count derives from the spec's canon.yaml · never hand-typed

07

Not another agent framework. The layer underneath.

Frameworks and assistants help an agent decide and act. Nika is orthogonal: it makes what the agent does reviewable and enforceable the layer between the model wants to act and the system lets it act.

FrameworksLangGraph, n8n: they help you wire and orchestrate the steps.
Nika makes the wired steps a reviewable, enforceable file, not glue code.
AssistantsCursor, Claude Code: they help an agent decide and act in the moment.
Nika turns that intent into a plan you review and the runtime enforces.
Protocols & toolsYour agent tools (MCP servers) expose capabilities an agent can call.
Nika runs them through invoke: allow-listed, permission-bound, traced.

Run a Nika plan from any of them. It's a portable file, not a platform.

08

Real files you'd write.

Anything you'd ask an AI to do more than once belongs in a file. Every workflow below is real, projected from nika-spec, audited before it runs: plan, cost, secrets. Pick your métier, open a card, read the exact YAML that runs it.

Ship faster. Let the boring parts run themselves.

t1-standup-digest.nika.yamlwalkthrough ↗
todayinvokenika:datehistoryexecgit
digestinfer
saveinvokenika:write
yaml
nika: v1workflow: standup-digestdescription: "Read yesterday's commits, write today's standup note"model: ollama/qwen3.5:4b   # local · zero key · swap for anthropic/claude-haiku-4-5 (fast one-liner job)tasks:  # No deps between these two → the engine runs them in parallel.  - id: today    invoke:      tool: "nika:date"      args: { op: now }  - id: history    exec:      command: ["git", "log", "--since=yesterday", "--oneline", "--no-merges"]  - id: digest    depends_on: [today, history]    with:      today: ${{ tasks.today.output }}      history: ${{ tasks.history.output }}    infer:      prompt: |        Date · ${{ with.today }}        Commits since yesterday ·        ${{ with.history }}        Write my standup note · 3 bullets · done / doing / blocked.        Plain words · no fluff.  - id: save    depends_on: [digest]    with:      digest: ${{ tasks.digest.output }}    invoke:      tool: "nika:write"      args:        path: "./standup-note.md"        content: "${{ with.digest }}"outputs:  note: ${{ tasks.digest.output }}

Every morning: the note is already written. You glance, you tweak one word, you go.

26 workflows · four tiers · nika-spec/examples/showcase the plan, the typed answers and the tools, all checked before it runs

09

The file becomes a canvas. Open any .nika.yaml in VS Code or Cursor: prompts sit on the cards, a run lights the graph wave by wave, and every canvas edit lands back in the file. This miniature works: press ▶ mock.

release-notes.nika.yaml · canvashover a card; its lineage stays lit

$0.00 ceiling · local model · audited before it runs

release-notes.nika.yaml

nika: v1
workflow: release-notes
model: ollama/qwen3.5:4b

tasks:
  - id: fetch_commits
    invoke:
      tool: "nika:fetch"
      args:
        url: "https://api.github.com/repos/acme/app/commits?since=v1.4.0"

  - id: write_notes
    depends_on: [fetch_commits]
    with:
      commits: ${{ tasks.fetch_commits.output }}
    infer:
      max_tokens: 800
      prompt: "Write the release notes from ${{ with.commits }}: grouped, human, no hype."

  - id: hero_image
    depends_on: [fetch_commits]
    invoke:
      tool: "nika:image_generate"
      args:
        provider: local
        prompt: "A minimal banner for the v1.5 release"
        aspect_ratio: "16:9"
        output_dir: "media/"

  - id: publish
    depends_on: [write_notes, hero_image]
    exec:
      command: ["gh", "release", "create", "v1.5.0", "--notes-file", "notes.md"]

  • The engine's judgment, as you type. Diagnostics, completions and hovers come from nika check and the schema: codes, fixes and positions are the binary's, not the extension's. Even this demo file passes the real nika check, exit 0.
  • Run, replay, scrub. The run streams onto the graph live; any recorded run replays with a time-travel scrubber.
  • Audited before a token is spent. Cost ceiling, permits boundary and secret flows are static facts on the cards. Read them before you press Run.

10

Shipping in the open.

A workflow language earns trust by moving, and by not breaking what you wrote. Here is the recent ship log: the spec opened, the verbs locked: and the engine shipped, one brew install away.

  1. site

    nika.sh v4.18 · the site speaks the shipped grammar

    The 0.104 release flipped the language: the envelope became a scalar (workflow: my-job), tasks became a sequence (- id: …), and every edge is declared (depends_on — an undeclared tasks.X read now dies at check as NIKA-DAG-003; after: died with it). The public spec still teaches the old form, so the site now runs on TWO spec surfaces and says so out loud: /spec/v1 stays the ratified pin, /spec/shipped vendors what the released binary itself embeds (nika spec --schema and --canon, refreshed deliberately, never probed at build), and /sources renders the grammar gap as a third clock line beside builtins and providers. Everything a visitor can copy speaks the shipped grammar and is PROVEN against the released binary: every hero flagship and its served /library file, the /learn walk and its assembled weekly radar, both install hellos with transcripts re-captured verbatim, the verb chapters, the 404, the /convert proof, and every showcase room and template seed, which stay byte-pure ratified projections and convert at the door through one mechanical, idempotent, line-mapped pass — the plan choreography, the hero highlights and the inspector line anchors all re-aim through the same map, so nothing drifts from the text it animates. The playground lint learned the new world (scalar envelope, sequence tasks, duplicate ids, declared-edge DAG-003, after: refused with its fix line), its break-lesson inverted to teach the flip, and a shipped-ahead door renders text-only until the spec lands the room. Alongside the flip: the engine badge reads v0.104.0 everywhere, the tools register carries decide as ratified-not-shipped with its spec contract on the page, the types register lands its primitives read from the schema itself (the last soon chip dies), the provider catalog re-vendors at 0.104, and the hub headers stop sliding under the fixed nav.

  2. site

    nika.sh v4.17 · one constant stage, and every word wired to the hull

    The spec voyage gets its deep rebuild. THE ONE STAGE: the canvas never resizes again (the old flips reallocated the GL buffer and snapped the projection; measured storms of 200ms frames fall to zero frames over 34ms in either direction), the berth is a region of one full-screen stage, the sky drifts under the prose everywhere, and the seam lives in the shaders: the hull melts toward the reading on the site’s own ordered-dither door. The camera answers to a sovereign frame at full-screen stages (a deep link can never paint the close-up over the prose again), poses commit only when the hand calms, the yaw takes the short arc instead of unwinding whole revolutions, and the page opens on a metamorphosis: the 2D elevation fades under the rising hull while the camera glides its approach, the reverse of the close. And the close earns its ceremony: a longer runway holds the assembled flyover grand, the plates melt away so the lay-down plays unannotated, and as the hull dissolves into its own drawing a real shader supernova fires: core flash, twin chromatic-fringed shockwaves cooling from white-blue to ember orange, counter-turning rays, falling dust, the whole hull irradiated by its own light while the drawing wakes stratum by stratum beneath it. Then the birth: a burst, an expanding ring, the butterfly blooms and the nika mark forms out of letter-space. The reference is wired alive now: the read section roots a stratum-hued umbilical INTO its card (a machined joint at the word, a drifting current on the wire, the ignition spark trailing a comet), hovering any word tends an ephemeral wire to ITS block on the vessel in the same hue with a joint at both ends, hovering an index chip or a transport tick spotlights that whole station before the click, and the hover readout leads with its stratum’s dot. Around the voyage: the home film opens at the hero itself (one continuous axis, the seam between poster and runway gone, then no seam at all: the floor opens and the field crosses), the plan becomes the file’s minimap and THE LEAP flies a curve grown mid-flight, the hero strip carries the whole corpus with the selected tab reading as the file it is, /files ships the official mark with a browser strip and per-file sources, the dither glow retreats by its corner, and every exec teaching speaks the 0.103 register (argv default, shell: explicit) with transcripts re-captured from the released binary. Underneath: the runway CSS is pinned to its model, every visitor-readable YAML rides a gate, and the trunk’s drift gates caught the one desync before it shipped.

  3. site

    nika.sh v4.16 · the ship lies down in its own drawing

    THE LAY-DOWN closes the spec voyage: past the last station the vessel folds its yaw to the whole revolution, lays flat into the exact pose of its 2D engineering drawing, dissolves into it, and the nika mark closes the file over the sources rail (an e2e pin parks inside the window to prove the handoff every run). The home film front-loads its gesture: the YAML becomes the DAG in the first half of the runway, the wires close over the last landings, and the run (terminal, timeline, badges) rides the same scroll from mid-gesture; the verdict keeps its tail. The voice purge lands site-wide: about 345 authored lines rewritten for real (blog posts, llms.txt, every register and room, this ship log itself), each dash a real edit, while the truth layer keeps its own words: engine failure-lines, provider contracts, captured transcripts and quoted artifacts stay verbatim, because rewriting a citation would make the page lie. WHERE THE LANGUAGE LIVES ships one verified sources map (every URL checked against the live repos before it renders) on the rooms, the 3D berths and the spec close, and the cross-reference graph closes: errors and templates point back at every room that names them, derived by inverting the rooms’ own data. The heavy usage registries leave the initial bundle behind inline-JSON islands (their refs twins keep the graph sync), the dither field drops its framework for one raw quad, the run posters go WebP at a third of the bytes, and the language doors get their share cards. Underneath: the version gate now re-captures transcripts against the real binary on every engine wave (it caught a CLI voice change a sed would have missed), and the heal lane opens one idempotent PR that the gate itself judges.

  4. site

    nika.sh v4.15 · every word gets its room, every room its machine

    The vocabulary completes. Every builtin opens its own room: the arg contract in the binary’s own words, the tool inside a real file (a verbatim skeleton excerpt at its true line numbers, or a crafted workflow that passes check; the drift gates re-prove both on every test run), the skeletons that ship it, the check gates that name it, the family walk. The four verbs get rooms of their own, and every key the schema declares gets its register row: 51 words projected from the served contract, never prose, with an honest miss for anything else. THE PIN DRUM plays the standard library on /tools: one row per builtin, one pin per real argument (bright means required), six family blades, a fixed comb; walking prev/next between rooms turns one persistent machine exactly one notch. Then the parts catalog: every room berths its OWN machine in the ship’s ink: six family archetypes (the file cabinet’s required drawers sit pulled out at you), four verb emblems (infer’s halo carries one spoke per provider), no two parts share a silhouette, every count a catalog count, and a 2D engineering drawing of the same instance tables stands in wherever GL never mounts. The ship links back (every spec section head opens its organ’s page), and ⌘K answers the whole vocabulary now, builtins, verbs and keywords alike. The spec voyage earns its grace: the floating plate dies into the rail: one 42px machined strip whose read segment expands to speak its organ and the chapter keys, titles never break (an overflowing tail fades under a machined window; at narrow berths resting titles fold whole and hover peeks them back); hull plates stand down when the prose column would cut them, and the opening flight flies like a vessel at last: the yaw follower under a hard velocity ceiling, the hull banking into its turns, the pointer’s hand on the wheel only at rest. The sitemap learns class-aware tiers, the nav opens the new doors, and every room speaks schema.org.

Full changelog

11

The control is the proof.

No logos to borrow, no quotes to dress up. Just what the engine guarantees: review before it acts · enforced permissions · replayable trace · portable off any platform · versioned like code: verifiable in the open spec and the one binary you install.

9.14verbs · every action explicit, declared not hidden
9.228builtin tools · allow-listed, nothing to install
9.317/5 localmodel providers · any model, cloud or fully offline
9.41Rust binary · zero daemons, zero background services
Review before it acts.
The agent writes its plan as a file first: every step, tool and permission. A human reads it before a single action runs.
Enforced permissions.
The file's permits: block is the blast radius: files, hosts, programs, tools. The runtime denies anything outside it (5 local providers mean PII never has to leave).
Replayable trace.
Every run leaves a replayable record, the audit trail. Same file, same steps, same order: run it again and diff it like code.
Portable off any platform.
One binary, 17 model providers, AGPL-3.0 forever. The file outlives the vendor. It still runs the day the company that made it is gone.

A README is documentation. Nika is an executable contract.

every number derives from the spec's canon.yaml · verifiable, never hand-typed

12

Get started.

One binary, one file, one command. Install it, write a plan, run it, and watch it print the plan, check the permits, and stay within bounds.

  1. Install

    One Rust binary. Homebrew on macOS, or the install script anywhere.

    brew install supernovae-st/tap/nika
    curl -LsSf https://nika.sh/install.sh | sh
  2. Write a file

    A plan is plain YAML: the steps, the verbs, the wiring. This is the same file the page just ran, opened at its first step.

    daily-brief.nika.yaml · excerpt
    nika: v1workflow: daily-brief# local model · your notes never leavepermits:  fs: { read: [ ./notes/* ], write: [ ./brief.md ] }  tools: [ "nika:read", "nika:write" ]  - { id: notes, invoke: { tool: "nika:read", args: { path: ./notes/today.md } } }

    real lines, sliced from the file above · read-only

  3. Run it

    Point the binary at the file. It prints the plan, checks the permits, and runs within bounds. Every step traced and replayable.

    nika run daily-brief.nika.yaml

runs everywhere · same file, same result

6.0.4 In your terminal

One Rust binary. The command is the button: click it, paste it, run.

6.0.6 With your agent

nika init writes AGENTS.md · nika wire claude|cursor adds the read-only oracle (nika_check · nika_explain).

WORKS WITH YOUR STACK

Claude Code · Codex · Cursor · VS Code · Hermes · OpenCode · Zed · GitHub Actions · MCP

nika init teaches whatever agent you run. the full integrations map

Learn it in 5 minutes

13

Questions, answered straight.

The real objections, and honest answers. No overclaiming, no dunking on the tools you already use.

  • I already see the agent’s steps in Cursor or a README. Why Nika?

    Seeing the steps is not the same as enforcing the procedure. A README is documentation; a Nika file is an executable contract: versionable, replayable, permission-bound, auditable, and portable off any platform. You do not just read what it intends to do; the runtime holds it to exactly that.

  • Isn’t this just YAML, or another workflow engine?

    The syntax is plain YAML, but the point is not the syntax. The point is the reviewable, enforced plan. The 4 verbs (infer · exec · invoke · agent) are native execution models, each one explicit and typed, with a permits: block the runtime enforces before anything runs. The file is the control surface, not the formatting.

  • Why not LangGraph, n8n, or MCP (agent tools)?

    They live at a different layer. Frameworks help you orchestrate and assistants help an agent act; MCP exposes tools to call. Nika is the contract-and-control layer underneath: it makes the plan reviewable and enforceable, and it runs their tools through invoke, allow-listed and traced. It complements them rather than replacing them.

  • Does my data leave my machine?

    Local-first, any model: fully local, or any API. Run a local model and nothing leaves at all. Every plan declares its network egress in its permits: block, and it is default-deny: omit the hosts and the workflow physically cannot reach the network. The file states exactly what can leave, and the runtime enforces it. 5 of the 17 providers are local.

  • Is it production-ready? What’s the license?

    Honest answer: Nika is early. Real semver toward a 1.0 launch, currently at v0.104.0, shipping in the open. It is one Rust binary you can install and run today. The engine is AGPL-3.0-or-later; the spec is Apache-2.0. We would rather you trust the spec and the binary than a maturity claim we have not earned yet.

the noise becomes the file.

14

Put your agents on a leash you can read.

Install the binary, write the plan as a file, review what it's allowed to touch, run it. Same file, same result, enforced on your machine. Tomorrow, and the day the vendor is gone.

brew install supernovae-st/tap/nika

Do you repeat an AI task every week, in ChatGPT, Claude, Cursor, Codex, or scripts? Send it. We convert the best ones into runnable .nika.yaml examples, credited to you.

Send a workflow