Skip to main content
@supernovae-st/nika lets a TypeScript application run .nika workflows and prove what they did. It is a thin, typed client: the Nika engine parses, checks, runs and records every workflow, and the SDK carries the engine’s answers to your code. It never parses a workflow, re-implements a check or computes a proof itself.

Two doors, one client

One class, Nika, reaches the engine through one of two transports. The constructor chooses; every method keeps its name and meaning on both.
  • Local engine (new Nika()): the SDK starts the engine that npm installed with the package (nika run <file> --json, argv only, never through a shell), in your working directory. Nothing else runs on your machine.
  • Your server (new Nika({ url, token })): the SDK talks to a nika serve --bind you operate. The server holds durable jobs and schedules, so a run survives your process restarting.

The run lifecycle

run() returns only after the engine has admitted the workflow. Everything after that is observation of one admitted run.
  1. Admission. The engine checks the file (the same audit nika check prints), the inputs and the spend cap. A refusal makes run() throw a NikaOperationError carrying the engine’s code; no run, event, trace or receipt exists.
  2. Observation. run.events() streams lifecycle events in one vocabulary on both doors. Events are presentation: optional to render, never the source of truth.
  3. Settlement. run.result() resolves once with the engine’s result: succeeded, failed, paused, cancelled or interrupted. An admitted failure resolves as data; it does not throw.
  4. Proof. The result carries an engine-issued receipt. traceVerify(receipt) asks the engine to check the run’s hash-chained record against it.

The lifecycle on both doors, as an illustration with captured content. new Nika() and new Nika({ url, token }) take the same methods; two lines of demo.mts, the quick start in the package README, change between them: line 3, the constructor, takes url, token and allowInsecureHttp: true, which a plain http:// URL to a loopback host needs, and line 6, the run options, takes an idempotencyKey in place of maxCostUsd, which a server run refuses. Admission: the engine refuses list-files.nika with NIKA-SEC-004, run() throws NikaOperationError and no run exists, while hello.nika is admitted. Observation: six local events, or two from nika serve. Settlement: run.result() reads succeeded. Proof: six hash-chained journal lines end in run_sealed, traceVerify(receipt) answers verified locally and sealed from the server, and one changed byte makes the verifier report BROKEN at line 5. An illustration: every code line, event, verdict, hash and version on screen was captured from @supernovae-st/nika 0.123.0, its bundled engine nika 0.123.0 (0bfde3765) and nika serve 0.123.0 in a mock/echo rehearsal; the cards, lanes, packets and pacing are drawn, and a real run takes milliseconds.

Who owns what

The words the pages use

What differs between the doors

Measured with @supernovae-st/nika@0.123.0 and its bundled engine, and nika serve 0.123.0:

Your first run, either door

The quickstart runs one workflow on the local engine in five minutes; the server quickstart starts nika serve and runs the same workflow over HTTP. The methods are the same, and two lines change over HTTP: the constructor takes url and token (with allowInsecureHttp: true for a plain http:// URL to a loopback host), and run() takes an idempotencyKey in place of maxCostUsd, which a server run refuses. On the local engine:

Quickstart

Install, run, read the typed result and verify the receipt.

Server quickstart

Start nika serve and drive it with the same client.

Results

Statuses, settlement, outputs and exit codes.

Errors

What throws, what resolves, and why the difference matters.