Skip to content

Tenor provides official SDKs in three languages: TypeScript, Python, and Go. Each SDK supports two modes of operation: local evaluation (contract runs in your process) and remote API calls (contract runs on the hosted platform). The SDKs share a conformance test suite that guarantees identical evaluation behavior across all three.

Available SDKs

SDKPackageLocal RuntimeRemote API
TypeScript@tenor/sdkWASM (browser + Node)HTTP client
Pythontenor-sdkPyO3 native bindingsHTTP client
Gogithub.com/riverline-labs/tenor/sdks/gowazero WASM bridgeHTTP client

Two Modes: Local Evaluation vs Remote API

Local Evaluation

The evaluator runs inside your process. You load a compiled contract, provide facts and entity states, and get back verdicts and an action space. No network calls. No platform dependency.

Local evaluation is a pure function: same contract + same facts + same entity states = same verdicts, every time. The evaluator never modifies state, never contacts external systems, and has no side effects.

When to use local evaluation:

  • Unit and integration tests. Assert that specific fact combinations produce expected verdicts. No test infrastructure needed beyond the SDK.
  • CI/CD pipelines. Validate contracts and run contract-level tests as part of your build.
  • Client-side preview. Show users what actions are available before they commit (TypeScript WASM runs in the browser).
  • Edge evaluation. Run contract logic in serverless functions, Lambda@Edge, or Cloudflare Workers.
  • Offline support. Evaluate contracts when the network is unavailable.
  • Performance-critical paths. Local evaluation takes microseconds, not network round-trip milliseconds.
typescript
// TypeScript — local evaluation, no network
import { TenorEvaluator } from "@tenor/sdk";

const evaluator = await TenorEvaluator.load("./escrow.tenor.wasm");

const result = evaluator.evaluate({
  facts: { payment_received: true, payment_amount: "5000.00" },
  entities: { Order: { order_001: "pending" } },
});

console.log(result.verdicts);
// { payment_ok: { value: true, stratum: 0 }, ... }

Remote API

The SDK calls the Tenor platform at api.tenor.run. The platform loads entity state from its managed database, evaluates the contract, and (for execution) applies effects atomically with provenance recording.

When to use the remote API:

  • Production execution. State changes must go through the executor for atomicity, provenance, and the full E1-E20 obligation set.
  • Shared state. Multiple services need to read and write the same entity state consistently.
  • Audit and compliance. Provenance chains are only recorded by the executor, not by local evaluation.
  • No local artifact. Your application does not have the compiled contract file --- it calls the platform by org/contract name.
typescript
// TypeScript — remote API, platform-managed state
import { TenorClient } from "@tenor/sdk";

const client = new TenorClient({
  org: "acme",
  apiKey: "tk_live_abc123",
});

const result = await client.evaluate("escrow", {
  facts: { payment_received: true },
});

const execution = await client.executeFlow("escrow", "release_flow", {
  persona: "escrow_agent",
  facts: { release_approved: true },
  entityBindings: { Order: "order_001" },
});

Cross-SDK Conformance

All three SDKs share a single conformance test suite: a set of contracts, fact inputs, entity states, and expected outputs. The test suite covers:

  • Verdict correctness. Every rule fires (or does not fire) as expected for the given facts.
  • Stratum ordering. Higher-stratum rules see only lower-stratum verdicts.
  • Action space completeness. Every legal operation for every persona is included. No illegal operations appear.
  • Type coercion rejection. Invalid fact types are rejected, not silently coerced.
  • Edge cases. Empty fact sets, all-terminal entities, flows with parallel branches, multi-stratum chains.

The conformance suite runs in CI for every SDK release. If a new version of any SDK produces different verdicts from the reference implementation, the release is blocked.

This means you can mix SDKs freely in your architecture. A Python service can evaluate locally, a TypeScript frontend can preview action spaces in the browser, and a Go service can execute flows via the remote API --- all against the same contract, producing the same verdicts for the same inputs.

Type Generation

The tenor generate command produces typed bindings from your contract, so your application code is checked against the contract schema at compile time:

bash
# Generate TypeScript types
tenor generate typescript escrow.tenor --output ./src/generated/escrow.ts

# Generate Python types
tenor generate python escrow.tenor --output ./src/generated/escrow.py

# Generate Go types
tenor generate go escrow.tenor --output ./internal/generated/escrow.go

Generated types include:

  • Fact types. A typed struct/interface for the facts your contract declares.
  • Entity state enums. All states for each entity as a union/enum type.
  • Verdict types. Names and payload types for each verdict the contract produces.
  • Operation names. String literal types for every operation.
  • Persona names. String literal types for every persona.

Using generated types turns contract violations into compile-time errors:

typescript
// Generated type ensures you cannot misspell a fact name or pass the wrong type
import { EscrowFacts, EscrowPersona } from "./generated/escrow";

const facts: EscrowFacts = {
  payment_received: true,
  payment_amount: "5000.00",
  // typo: paymet_received — compile error
  // wrong type: payment_received: "yes" — compile error
};

Choosing Your SDK

ScenarioRecommended SDKMode
Node.js backend serviceTypeScriptRemote (execute) or Local (evaluate)
React/Next.js frontendTypeScriptLocal (WASM in browser)
Django/FastAPI backendPythonRemote (execute) or Local (evaluate)
Data pipeline / JupyterPythonLocal (evaluate)
Go microserviceGoRemote (execute) or Local (evaluate)
CI/CD contract testingAnyLocal (evaluate)
Serverless / edge functionTypeScript or GoLocal (evaluate)

The right choice depends on your stack and your needs. All three SDKs produce identical results. Pick the one that fits your language and runtime.