AGPL-3.0-or-later · forever.

[ NIKA · CHECKED WORKFLOWS FOR AI ]

Intent as Code.

No key. No account. No server to keep running. Install the binary, then run the command it prints as next. On v0.114.0 that is nika try 01-hello. It finishes in a second, and says out loud that it was a rehearsal.

No key. No account. No server. Install, then nika try 01-hello.

nika try 01-hello
✔  greet  infer · mock/echo  0ms
rehearsal · a mock model echoed the prompt — not a real answer

v0.114.0macOS · LinuxAGPL-3.0

nika: 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:assert", "nika:write" ]tasks:  notes: { invoke: { tool: "nika:read", args: { path: ./notes/today.md } } }  inbox: { invoke: { tool: "nika:read", args: { path: ./notes/inbox.md } } }  calendar: { invoke: { tool: "nika:read", args: { path: ./notes/calendar.md } } }  triage:    with:      inbox: ${{ tasks.inbox.output }}    infer: { prompt: "Urgent item, 10 words max: ${{ with.inbox }}", max_tokens: 2048 }  agenda:    with:      calendar: ${{ tasks.calendar.output }}    infer: { prompt: "Three bullets, 8 words max: ${{ with.calendar }}", max_tokens: 2048 }  draft:    with:      notes: ${{ tasks.notes.output }}      triage: ${{ tasks.triage.output }}      agenda: ${{ tasks.agenda.output }}    infer:      prompt: "Three bullets, 8 words max. No preface. Notes: ${{ with.notes }} Urgent: ${{ with.triage }} Plan: ${{ with.agenda }}"      max_tokens: 2048  verify:    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:assert"      args:        condition: "${{ size(with.draft) > 0 }}"        message: "Empty brief. Increase max_tokens or change model."  save:    after: { verify: success }    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:write"      args: { path: ./brief.md, content: "${{ with.draft }}" }outputs:  brief: "${{ tasks.draft.output }}"
source

audited · 8 tasks · 5 waves · permits declared · 2 hintsnika 0.114.0

FIG.01source → checked topology → bounded run → receipt
One file. One inspectable run.

The interface follows the contract.

Nika does not hide an agent behind chat. The source, permissions, live stages and final receipt stay visible in one operational frame.

Nika execution geometry
A checked plan has shape.
Constraint, trajectory and proof stay visible before the interface asks for trust.
Image
Permitted surfaceDeclared capabilities bound the possible run.
01 · constraintsPermitted surface

Declared capabilities bound the possible run.

Execution traceEvery step remains inspectable in order.
02 · causalityExecution trace

Every step remains inspectable in order.

Durable receiptEvidence closes around what actually happened.
03 · proofDurable receipt

Evidence closes around what actually happened.

One source · one bounded path · one replayable receipt.
Nika Studiorelease-review.nika.yaml live · 019fc7e2
release-reviewdefault-denybudget ≤ $0.50
sourcechecked
nika: release-review
permits:
  fs.write:
    - out/receipt.md
  tools:
    - nika:read
    - nika:write
tasks:
  review:
    agent: bounded
execution3 / 4
01inferclassify intent1.82s
02invokeread repository184ms
03agentreview boundaryactive
04execwrite receiptqueued
policy heldnetwork · deniedwrite · out/receipt.mdreceipt pending
4 tasks · 3 edges · 2 permitssha256:a73f…e8a2a
A real Nika product composition: source, checked topology, bounded execution and retained proof.
SPNDS MeasuredScaffold + ProductFrame · Nika owns only product semantics and composition.

[ THE READING ]

01

The file, read back.

An agent’s draft, two keystrokes early. Step through what the checker saw before a token was spent, then the repair: applied by the engine itself, not by hand.

two keystrokes · 3 findings · zero tokens spent

error:CONFORMNIKA-DAG-002

unknown dependency: task judge depends on dif, which does not exist — did you mean diff? · → nika explain NIKA-DAG-002

begin snippet for pr-review.broken.nika.yaml at line 19, column 16

      changes: ${{ tasks.dif.output }}

helpnika explain NIKA-DAG-002

the first keystroke: a reference to a task that does not exist, and the checker names what you meant

01

Watch the file become the run.

nika: 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:assert", "nika:write" ]tasks:  notes: { invoke: { tool: "nika:read", args: { path: ./notes/today.md } } }  inbox: { invoke: { tool: "nika:read", args: { path: ./notes/inbox.md } } }  calendar: { invoke: { tool: "nika:read", args: { path: ./notes/calendar.md } } }  triage:    with:      inbox: ${{ tasks.inbox.output }}    infer: { prompt: "Urgent item, 10 words max: ${{ with.inbox }}", max_tokens: 2048 }  agenda:    with:      calendar: ${{ tasks.calendar.output }}    infer: { prompt: "Three bullets, 8 words max: ${{ with.calendar }}", max_tokens: 2048 }  draft:    with:      notes: ${{ tasks.notes.output }}      triage: ${{ tasks.triage.output }}      agenda: ${{ tasks.agenda.output }}    infer:      prompt: "Three bullets, 8 words max. No preface. Notes: ${{ with.notes }} Urgent: ${{ with.triage }} Plan: ${{ with.agenda }}"      max_tokens: 2048  verify:    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:assert"      args:        condition: "${{ size(with.draft) > 0 }}"        message: "Empty brief. Increase max_tokens or change model."  save:    after: { verify: success }    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:write"      args: { path: ./brief.md, content: "${{ with.draft }}" }outputs:  brief: "${{ tasks.draft.output }}"

audited · 8 tasks · 5 waves · permits declared · 2 hintsnika 0.114.0

[ 01 ]run together ×3

notesinvokenika:read✓ 2ms
inboxinvokenika:read✓ 0ms
calendarinvokenika:read✓ 1ms

[ 02 ]run together ×2

triageinferollama/llama3.2:3b✓ 1.1s
agendainferollama/llama3.2:3b✓ 860ms

[ 03 ]then

draftinferollama/llama3.2:3b✓ 745ms

[ 04 ]then

verifyinvokenika:assert✓ 0ms

[ 05 ]then

saveinvokenika:write✓ 1ms
daily-brief.nika.yaml
nika run daily-brief.nika.yaml
workflow daily-brief
scheduled 8 tasks · notes inbox calendar triage agenda draft verify save
notes invoke · nika:read
notes 2ms
inbox invoke · nika:read
inbox 0ms
calendar invoke · nika:read
calendar 1ms
triage infer · ollama/llama3.2:3b
triage 1.1s · 12 tok
agenda infer · ollama/llama3.2:3b
agenda 860ms · 35 tok
draft infer · ollama/llama3.2:3b
draft 745ms · 31 tok
verify invoke · nika:assert
verify 0ms
save invoke · nika:write
save 1ms
run complete · exit 0 · 2.0s
✓ exit 08 tasks ran2.0swrote 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 8 tasks · notes inbox calendar triage agenda draft verify save
notes invoke · nika:read
notes 2ms
inbox invoke · nika:read
inbox 0ms
calendar invoke · nika:read
calendar 1ms
triage infer · ollama/llama3.2:3b
triage 1.1s · 12 tok
agenda infer · ollama/llama3.2:3b
agenda 860ms · 35 tok
draft infer · ollama/llama3.2:3b
draft 745ms · 31 tok
verify invoke · nika:assert
verify 0ms
save invoke · nika:write
save 1ms
run complete · exit 0 · 2.0s
✓ exit 08 tasks ran2.0swrote 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✓ 2ms
inboxinvokenika:read✓ 0ms
calendarinvokenika:read✓ 1ms

[ 02 ]run together ×2

triageinferollama/llama3.2:3b✓ 1.1s
agendainferollama/llama3.2:3b✓ 860ms

[ 03 ]then

draftinferollama/llama3.2:3b✓ 745ms

[ 04 ]then

verifyinvokenika:assert✓ 0ms

[ 05 ]then

saveinvokenika:write✓ 1ms

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 6–8
permits:  fs: { read: [ ./notes/* ], write: [ ./brief.md ] }  tools: [ "nika:read", "nika:assert", "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.)

the written law · numbered, public, reviewable

  • NEP-0003an absent permits: block declares zero authority
  • NEP-0004untrusted values re-gate under permits, at the effect
  • NEP-0005a child’s environment is composed, never inherited
  • NEP-0006a fetch of a code-bearing artifact is never innocent
  • NEP-0007the trace carries a permit witness · check and run agree
  • NEP-0008the egress proxy is the permit’s exact projection
  • NEP-0009a path grant names a path identity, re-judged at dispatch

the full set lives in the spec’s governance register · proposals, numbered and versioned, before they bind the engine

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.yamlsource
nika: 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:assert", "nika:write" ]tasks:  notes: { invoke: { tool: "nika:read", args: { path: ./notes/today.md } } }  inbox: { invoke: { tool: "nika:read", args: { path: ./notes/inbox.md } } }  calendar: { invoke: { tool: "nika:read", args: { path: ./notes/calendar.md } } }  triage:    with:      inbox: ${{ tasks.inbox.output }}    infer: { prompt: "Urgent item, 10 words max: ${{ with.inbox }}", max_tokens: 2048 }  agenda:    with:      calendar: ${{ tasks.calendar.output }}    infer: { prompt: "Three bullets, 8 words max: ${{ with.calendar }}", max_tokens: 2048 }  draft:    with:      notes: ${{ tasks.notes.output }}      triage: ${{ tasks.triage.output }}      agenda: ${{ tasks.agenda.output }}    infer:      prompt: "Three bullets, 8 words max. No preface. Notes: ${{ with.notes }} Urgent: ${{ with.triage }} Plan: ${{ with.agenda }}"      max_tokens: 2048  verify:    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:assert"      args:        condition: "${{ size(with.draft) > 0 }}"        message: "Empty brief. Increase max_tokens or change model."  save:    after: { verify: success }    with:      draft: ${{ tasks.draft.output }}    invoke:      tool: "nika:write"      args: { path: ./brief.md, content: "${{ with.draft }}" }outputs:  brief: "${{ tasks.draft.output }}"

audited · 8 tasks · 5 waves · permits declared · 2 hintsnika 0.114.0

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.yamlsource
# the invoke verb alone · one tool call under the two-part grantnika: use-a-toolpermits:  tools: ["nika:fetch"]  net: { http: ["nika.sh"] }tasks:  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.yamlsource
# the agent verb alone · goal + tool whitelist, loop boundednika: delegatemodel: ollama/llama3.2:3bpermits:  tools: ["nika:read", "nika:fetch"]  net: { http: ["nika.sh"] }  fs:    read: ["./docs/*"]tasks:  audit:    agent:      prompt: "Find every dead link in ./docs"      tools: [ "nika:read", "nika:fetch" ]      max_tokens_total: 8192outputs:  report: ${{ tasks.audit.output }}

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

06

What an agent can be permitted to use.

The first run needs none of this. After that, the language stays four verbs. The standard library does the rest: 28 builtins, the seats nika catalog lists, and any agent tools 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

08

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.

09

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.

standup-digest.nika.yamlwalkthrough ↗
todayinvokenika:datehistoryexecgit
digestinfer
saveinvokenika:write
yamlsource
nika: standup-digestmodel: ollama/qwen3.5:4b   # local · zero key · swap for groq/llama-3.3-70b (a fast one-liner job)permits:  exec: ["git"]              # the ONE program this workflow may run  tools: ["nika:date", "nika:write"]  fs: { write: ["out/standup-note.md"] }tasks:  # No deps between these two → the engine runs them in parallel.  today:    invoke:      tool: "nika:date"      args: { op: now }  history:    exec:      command: ["git", "log", "--since=yesterday", "--oneline", "--no-merges"]  digest:    with:      today: ${{ tasks.today.output }}      history: ${{ tasks.history.output }}    infer:      max_tokens: 400          # a standup note is short · the ceiling says so      prompt: |        Date · ${{ with.today }}        Commits since yesterday ·        ${{ with.history }}        Write my standup note · 3 bullets · done / doing / blocked.        Plain words · no fluff. If there are no commits, say so in one line.  save:    with:      digest: ${{ tasks.digest.output }}    invoke:      tool: "nika:write"      args:        path: out/standup-note.md        create_dirs: true        content: "${{ with.digest }}"outputs:  note: ${{ tasks.digest.output }}

audited · 4 tasks · 3 waves · permits declarednika 0.114.0

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

27 workflows · four tiers · nika-spec/examples 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: release-notes
model: ollama/qwen3.5:4b

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

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

  hero_image:
    after:
      fetch_commits: success
    invoke:
      tool: "nika:image_generate"
      args:
        provider: local
        prompt: "A minimal banner for the v1.5 release"
        aspect_ratio: "16:9"
        output_dir: "media/"

  publish:
    after:
      write_notes: success
      hero_image: success
    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.

11

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. release

    v0.114.0 · loopback HTTP and a named project

    Default nika serve stays the resident firer. --bind with --workflows and --token-file opens authenticated loopback HTTP: jobs, SSE and OpenAPI. Cancel, artifacts and POST /v1/run stay 404. A project file names itself (nika: my-project); nika: v1 on nika.yaml is the retired schema tag.

  2. site

    nika.sh v4.23 · the origin becomes one story

    The six origin records now read as one continuous arc, from the first objection to the name. The series page replaces its generic card grid with a dated ledger that shows reading order, source receipts and the evidence boundary before a reader opens a chapter.

  3. site

    nika.sh v4.22 · the honest origin ledger

    Four retrospective field notes fill the public story from November through February without pretending they were published then. Story dates and publication dates are now separate across the journal, feeds and structured metadata, and six records form one origin series from the first note to the name.

  4. site

    nika.sh v4.21 · the SDK control room

    The SDK now connects nika.yaml project setup, local runs, runtime proof and the resident firer, separate from the future HTTP service.

Full changelog

12

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 · hash-chained trace · priced before it spends · crash-resume without a server · portable off any platform: 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.31Rust 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. A local seat means PII never has to leave.
Replayable trace.
Every run leaves a hash-chained NDJSON trace: every task, and every agent tool call, in the same chain · nika trace verify walks it and names the first broken link. Same file, same steps, same order: run it again and diff it like code.
Portable off any platform.
One binary, the seat you pick, AGPL-3.0 forever. The file outlives the vendor. It still runs the day the company that made it is gone.
Priced before it spends.
nika check prints the plan’s cost floor before a token is spent, and --max-cost-usd is a hard ceiling: a plan whose floor exceeds it never starts.
Crash-resume without a server.
The trace a run already writes is the checkpoint · --resume is a reader of a file, not a client of a run-store: no supervisor, no database. Two hashes decide skip-or-rerun, recorded work replays instead of re-calling the model, and a cache hit is never silent.

A README is documentation. Nika is an executable contract.

every number derives from the spec's canon.yaml · verifiable, never hand-typed · every repo file declares its provenance in estate.yaml · authored or generated, hashed

13

Get started.

No key, no account, and nothing left running in the background. Install the binary, run the command it prints as next: nika try 01-hello. Then keep a file. It prints the plan, checks the permits, and stays 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

    Installed? See a workflow work before anything else · offline, zero keys, nothing written:

    nika try 01-hello
  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 · excerptsource
    nika: daily-brief# local model · your notes never leavemodel: ollama/llama3.2:3bpermits:  fs: { read: [ ./notes/* ], write: [ ./brief.md ] }  tools: [ "nika:read", "nika:assert", "nika:write" ]  notes: { invoke: { tool: "nika:read", args: { path: ./notes/today.md } } }

    audited · 8 tasks · 5 waves · permits declared · 2 hintsnika 0.114.0

    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

14

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.114.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.

Image

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