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
[ INTENT AS CODE ]
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.
v0.104.0macOS · LinuxAGPL-3.0
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 }}"source01
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
[ 02 ]run together ×2
[ 03 ]then
[ 04 ]then
recorded from a real nika run · replayed by your scroll · nothing staged
01.1
The same file, executed by the engine with a local model on a real machine. What you watch is the recorded event stream.
recorded from a real nika run · replayed at reading pace · nothing staged
01.2
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
[ 02 ]run together ×2
[ 03 ]then
[ 04 ]then
durations recorded from the real run · hover a step to trace its wires
[ THE BOUNDARY ]
02
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.
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 writetools:which tools it may callif a step reaches outside the list, say
write ~/.ssh/config NIKA-SEC-004effect 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
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
A file you keep
Glue scripts
Four verbs
Cloud automations
Your machine
04
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.
nika run · replayed at reading pace · nothing staged
05
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.1infer
Think. Ask any model · local or cloud.
nika: v1workflow: thinkmodel: ollama/llama3.2:3btasks: - id: summarize infer: prompt: "Three risks in this release, ranked"5.2exec
Run. A shell command, captured and typed.
5.2.1 capture & exit codes5.2.2 retry · timeout5.2.3 permitted programs
nika: v1workflow: runtasks: - id: build exec: command: ["cargo", "build", "--release"]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
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
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
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.
fetchinvoke:.28 tools · 4 familiesFiles· 5
Data· 9
Web· 1
Flow· 13
provider: ollama runs offline.17 totalLocal runtimes· 5 · no cloud needed
Cloud · open-weight first· 11
Test· 1
fetch turns a page into typed output.9 modesmcp:.native · unboundedevery count derives from the spec's canon.yaml · never hand-typed
07
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.
Run a Nika plan from any of them. It's a portable file, not a platform.
08
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.
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
.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"]
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.10
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.
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.
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.
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.
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.
11
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.
A README is documentation. Nika is an executable contract.
every number derives from the spec's canon.yaml · verifiable, never hand-typed
12
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.
One Rust binary. Homebrew on macOS, or the install script anywhere.
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.
nika: v1workflow: daily-brief# local model · your notes never leave…permits: 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
Point the binary at the file. It prints the plan, checks the permits, and runs within bounds. Every step traced and replayable.
runs everywhere · same file, same result
One Rust binary. The command is the button: click it, paste it, run.
The nika-lang extension: the file as a content-first canvas. Run it live, replay it, audit it before a token is spent, with engine-true diagnostics and completions, in VS Code, Cursor and Windsurf.
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
13
The real objections, and honest answers. No overclaiming, no dunking on the tools you already use.
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.
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.
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.
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.
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
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.
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.