<nil>NILScript

Develop

Building Shims

A NIL translation shim is a stateless HTTP adapter that translates NIL sentences into native API calls — letting a backend sit behind the agent plane without changing its own API. It enforces two-phase execution, idempotency, and compensation-aware reversibility.

What a shim is (and isn’t)

A shim exposes the six NIL endpoints, translates intent to native API calls, and pushes EVENT callbacks after writes. It is not a business-logic layer, not a cache, and not a NIL client — it is a thin, stateless boundary.

Three decoupled layers

  • NIL edge. HTTP, envelope validation, auth.
  • Translation core. Pure mapping functions, one per verb.
  • System client. The native API calls to your backend.

Keep the translation core pure and unit-tested; keep the edge generic and reusable across backends.

Minimal state

The shim keeps only what correctness demands:

  • An idempotency ledger: idempotency_key → terminal outcome.
  • A proposal store: proposal_id → { verb, resolved args, expiry, state }.
  • An EVENT sequence counter, monotonic per workspace.

The six endpoints

SentenceMethod + pathYour response
PROPOSEPOST /nil/v0.1/proposeEnvelope PROPOSAL (proposal or refusal)
COMMITPOST /nil/v0.1/commitEnvelope STATUS
QUERYPOST /nil/v0.1/queryBare { "data": { … } } — not an envelope
STATUSGET /nil/v0.1/status/{id}Envelope STATUS
EVENToutbound webhookYou are the sender
ROLLBACKPOST /nil/v0.1/rollbackEnvelope PROPOSAL (compensation preview)

PROPOSE — validate, never write

PROPOSE maps the verb to native validation, resolves references, and returns either a proposal or a refusal — with zero side effects. The server computes the authoritative values (amounts, tax, inventory); the caller’s hints are ignored. Persist the proposal for the later COMMIT.

PROPOSE outcomes
// success
{ "outcome": "proposal", "id": "<url-safe 8-128>", "verb": "...",
  "tier": "LOW|MEDIUM|HIGH|CRITICAL", "preview": { "ar": "…", "en": "…" },
  "resolved": { … }, "modifiable": ["…"], "expires_at": "<RFC3339>" }

// cannot satisfy
{ "outcome": "refusal", "code": "AMBIGUOUS|UNRESOLVED|INVALID_ARGS|…",
  "message": "…", "field": "party_id",
  "candidates": [ { "id": "…", "name": "…" } ] }
Refusals are data, not HTTP errors
A refusal is a 200 OK, never a 4xx. An AMBIGUOUS refusal must carry candidates (≤8).

COMMIT — idempotent execution

COMMIT receives { proposal_id, idempotency_key }. Check the ledger first: if the key is present, return the stored terminal outcome — no duplicate write. Load the stored proposal; if expired or suspended, refuse with PROPOSAL(EXPIRED|SUSPENDED). For LOW/MEDIUM, answer STATUS(executing|executed); for HIGH+, park with STATUS(pending_approval). The actual write happens at execution and is reported via EVENT, not in the HTTP response.

Idempotency is non-negotiable
A retried COMMIT with the same key must replay the exact original outcome.

ROLLBACK & reversibility

ROLLBACK is a request for a governed reversal, answered by a PROPOSAL previewing the compensation. Declare each verb’s reversibility tier. Resolve the compensation_token; if unknown or expired, refuse COMPENSATION_EXPIRED. The compensation lands only on the following COMMIT.

Unmarked verbs default to IRREVERSIBLE
An unmarked verb refuses ROLLBACK honestly — zero-touch for existing adapters, and never a phantom reversal.

EVENT — reporting the outcome

After execution, push an EVENT to the agent plane’s webhook. It must carry proposal and result.entity, be signed with HMAC-SHA256 over the raw body, and include a monotonic per-workspace sequence header.

EVENT body
{ "event": "executed", "severity": "info", "proposal": "<id>",
  "result": { "claim": "success", "changed": true, "verified": true,
              "entity": { "type": "invoice", "id": "…", "url": "…" },
              "ssot": { "system": "<your backend>", "read_after_write": true } } }

Definition of done

  • All six endpoints, behind grant/bearer auth.
  • PROPOSE produces zero side effects; invalid or ambiguous args refuse (never an HTTP error).
  • Server-authoritative fields; the caller’s hints never become facts.
  • COMMIT is idempotent and emits a signed, sequenced EVENT.
  • QUERY returns bare data; destructive verbs require an explicit grant.
  • The conformance matrix passes; the translation core is pure and unit-tested.
See also
CLI Testing drives these endpoints against the conformance corpus, and Conformance defines the guarantees they must satisfy.

NILScript is an open standard, stewarded by the Wosool project. The spec is extracted from running code.

Draft standard v0.3.0 · 0.x stage · NIL wire 0.1 · DSL 0.1