inferCall a modelCall a model. Any of the providers; structured output when you give it a schema.
- id: research infer: prompt: "Research ${{ vars.topic }}"FIG S · the language reference
A .nika.yaml is the plan, written down: one envelope, four verbs, a typed task shape, and a permits block that declares — and bounds — everything it's allowed to touch. You review it, the runtime enforces it, then it runs. This is the friendly map; the nika-spec repository is the canonical, normative source.
tl;dr · 5 pillars
nika: v1infer · exec · invoke · agentdepends_on${{ }}NIKA-*full text: github.com/supernovae-st/nika-spec ↗ · machine contract: workflow.json
Every file opens with nika: v1 — one version marker, pinned for the v1 lifetime. No v1.0, no v2 migration. Three keys are required; the rest are optional.
nikarequiredthe version marker · exactly v1, foreverworkflowrequiredthe workflow id · kebab-case · unique in the filetasksrequiredthe DAG · one or more nodes, each binding one verbdescriptionoptionala human note · free textmodeloptionalthe default model · provider/name (e.g. ollama/llama3.1)varsoptionaltyped inputs · ${{ vars.X }} · with required / defaultenvoptionalnon-sensitive runtime config · ${{ env.X }}secretsoptionalvault-backed references · never inline literalspermitsoptionalthe capability boundary · default-deny once presentoutputsoptionalthe return value · ${{ tasks.X.output }} · symmetric to varsA real file · standup-digest
nika: v1workflow: standup-digestdescription: "Read yesterday's commits, write today's standup note"model: mock/echo # deterministic · swap for ollama/llama3.1 (local · zero key)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] infer: prompt: | Date · ${{ tasks.today.output }} Commits since yesterday · ${{ tasks.history.output }} Write my standup note · 3 bullets · done / doing / blocked. Plain words · no fluff. - id: save depends_on: [digest] invoke: tool: "nika:write" args: path: "./standup-note.md" content: "${{ tasks.digest.output }}"outputs: note: ${{ tasks.digest.output }}A verb is a distinct native execution model. A task binds exactly one. That is the whole operation space — fetch, recall, db and files are tools reached under invoke:, not verbs.
inferCall a modelCall a model. Any of the providers; structured output when you give it a schema.
- id: research infer: prompt: "Research ${{ vars.topic }}"execRun a processRun a real process. stdout becomes the output; a non-zero exit becomes an error.
- id: build exec: command: "cargo build --release"invokeCall a toolCall a tool — a nika: builtin or an mcp: server. Default-deny, args schema-checked.
- id: read_config invoke: tool: "nika:read" args: path: ./config.yamlagentDrive a loopDrive an autonomous tool-use loop, bounded by max_turns and a whitelist of tools.
- id: research agent: prompt: "Research ${{ vars.topic }}" tools: ["nika:fetch"]A task is a DAG node. id is the only required field and exactly one verb binds; everything else is an optional structural control.
required coreoptional controls
idreqsnake_case · CEL-safe · unique in the workflow‹verb›reqexactly one of infer · exec · invoke · agentdepends_onoptthe edges · ids this task waits onwhenopta CEL boolean gate (or true/false) — replaces the success gatefor_eachoptmap the task over a collectionmax_paralleloptcap concurrent for_each iterations · 1 = sequentialtimeoutopta quoted Go-duration · e.g. "30s" "5m" "1h30m" · max 24hretryoptattempts + backoff on a transient failureon_erroroptfallback · recover from another task · or continueThe capability boundary — the contract an agent must satisfy before it acts. Once permits: is present, every category is default-deny: which files it can read, which it can write (read XOR write), which hosts it can reach, which programs it can run, which tools it may call. The runtime enforces it — out of bounds is denied, not logged after the fact.
fs.read / fs.writenet.httpexectoolsfs.read / fs.writenet.httpexectoolsdenied, before it runs
See it felt, not told — toggle a permit and watch the runtime obey.
23 builtin tools, all reached with invoke: — nothing to install. Grouped by what they touch.
Files· 5
readwriteeditglobgrepData· 9
jqconvertvalidatejson_diffjson_merge_patchcomposehashuuiddateWeb· 1
fetchFlow· 8
assertdonewaitemitlognotifypromptinspectPick per task or per file. Local-first: provider: ollama runs offline, no key. Cloud is open-weight-first (Mistral leads).
Local runtimes· 5 · offline
OllamaLM Studiollama.cppLocalAIvLLMCloud · open-weight first· 8
MistralAnthropicOpenAIGeminiDeepSeekxAIGroqOpenRouterTest· 1 · deterministic
mockHow nika:fetch turns a page into typed output — from raw text to a parsed article, feed or jq projection.
articlefeedjqlinksmarkdownmetadataselectorsitemaptextEvery failure carries a typed code — NIKA-‹NS›-NNN — across 14 namespaces, with a category and a transient flag. The full registry lives in errors/catalog.json.
NIKA-AGENTagent: the loop budgetNIKA-BUILTINbuiltin tool argument contractsNIKA-CANCELtask or workflow cancellationNIKA-DAGDAG topology · cycles · invalid depsNIKA-EXECexec: the local processNIKA-IMPLengine-internal errorsNIKA-INFERinfer: the model callNIKA-INVOKEinvoke: the tool callNIKA-MCPMCP client + transportNIKA-PARSEYAML parse + envelope validationNIKA-PROVIDERprovider adapter failuresNIKA-SECsecurity policy · SSRF · whitelist · permitsNIKA-TIMEOUTtask or step timeoutsNIKA-VARvariable + expression resolutionThe contract you can count on — the parts that never change.
S.8aReal semver toward 1.0
The engine ships on real semver — currently v0.91.0 toward a 1.0 launch. The language envelope stays nika: v1, frozen forever.
S.8bFour verbs, locked
infer · exec · invoke · agent — a closed set, locked forever. New capability arrives as a tool, never a fifth verb.
S.8cThe spec is Apache-2.0
The language spec is permissive — adopt it, build a runtime against it, with a patent grant. The standard for the workflow file.
S.8dThe engine is AGPL-3.0-or-later
Copyleft on the engine — a hosted fork shares its source. Anti-extraction by construction.
4 verbs · 23 builtins · 14 providers · 9 extract modes · 14 error namespaces — every count derives from the spec's canon.yaml, never hand-typed