Docs/Concepts

Concepts

The typed primitives that make every agent run inspectable, evaluable, and provable.

The operational graph

Every instrumented agent run becomes a typed graph. A handful of primitives carry everything you need to debug, evaluate, and guardrail:

  • Run — a single execution. Has a name, status, and timing.
  • Node — one action inside a run: a tool call, LLM step, sub-agent invocation, or custom event.
  • Session — an ordered, hash-chained sequence of receipts produced by a run.
  • Finding — a signal emitted by a monitor against a run or node (e.g. send_email not in allowlist).

Eyes captures these primitives. DNA stores them as a typed substrate. Cortex answers questions over them — every answer cited to the node that proves it.

Runs

A run is the unit of work the SDK captures. Wrap any handler withruns.start and every action inside becomes a node attached to the run.

runs.tstypescript
import { Invariance } from '@invariance/sdk';

const inv = Invariance.init({ apiKey: process.env.INVARIANCE_API_KEY! });

await inv.runs.start({ name: 'discharge-summary' }, async (run) => {
  await run.log('intake', { patientId: 'p-123' });
  await run.tool('chart.read', { patientId: 'p-123' }, async () => ({ /* … */ }));
  await run.tool('policy.check', { policy: 'discharge' }, async () => ({ ok: true }));
});

Nodes

Every action inside a run produces a node. Each node has a type (tool_call, llm_step, log, …), an input/output payload, timing, and a parent pointer that builds the run's tree.

node.tstypescript
interface Node {
  id: string;            // ULID
  runId: string;
  parentId: string | null;
  agentId: string;
  actionType: string;    // 'tool_call' | 'llm_step' | 'log' | ...
  input: unknown;
  output?: unknown;
  startedAt: number;
  endedAt?: number;
  anomalyScore?: number; // 0..1, set by monitors
}

Sessions

A session groups the receipts produced by a run into an ordered, hash-chained sequence. Sessions are how you verify a run wasn't tampered with after the fact.

session.tstypescript
const session = inv.session();           // lazy
const session = await inv.createSession(); // eager

await session.record('tool_call', {
  input: { tool: 'chart.read', patientId: 'p-123' },
  output: { /* … */ },
});

await session.close();

Sessions have three statuses:

  • open — accepting new receipts.
  • closed — finalized with a close hash over the entire chain.
  • tampered — verification detected a broken hash or invalid signature.

Findings & monitors

A monitor is a rule that runs against runs or nodes. When the rule fires it emits a finding — a typed signal with severity, the node(s) that produced it, and an optional review queue entry.

  • Severity — info, warn, or critical.
  • Citations — the node IDs that triggered the finding.
  • Review — surfaces in the dashboard for human triage; outcomes flow back as guardrails.

Evals use the same primitive: pass/fail of an eval case is derived from the findings the run produces.

The proof chain

Under every session is a hash-chained, Ed25519-signed sequence of receipts. Each receipt commits to a SHA-256 hash of its action data plus the previous receipt's hash. The first receipt anchors with "0".

text
Receipt 1: hash = SHA256(data₁ + "0")
Receipt 2: hash = SHA256(data₂ + hash₁)
Receipt 3: hash = SHA256(data₃ + hash₂)

Every receipt hash is signed with the agent's Ed25519 private key via @noble/ed25519. Keys are produced by Invariance.generateKeypair() — a 32-byte private key plus its 32-byte public key.

keypair.tstypescript
import { Invariance } from '@invariance/sdk';

const { privateKey, publicKey } = Invariance.generateKeypair();
// privateKey: 64-char hex (32 bytes)
// publicKey:  64-char hex (32 bytes)

Tamper evidence

verifyChain() walks the receipts in order, recomputes each hash from the canonical action data plus the previous hash, and verifies the Ed25519 signature against the agent's public key. A single failure anywhere marks the entire session as tampered.

verify.tstypescript
import { verifyChain } from '@invariance/sdk';

const result = await verifyChain(receipts, publicKey);

if (result.valid) {
  console.log('Chain intact — all hashes and signatures verified');
} else {
  console.error('Tamper detected at receipt:', result.brokenAt);
  console.error('Reason:', result.reason);
}
On this page
The operational graphRunsNodesSessionsFindings & monitorsThe proof chainTamper evidence