Integration guide

Add structural sensing
to your CRE workflow.

Chainlink CRE lets you build and deploy autonomous workflows on a DON. Invarians adds one thing CRE doesn't have natively: a live read of the infrastructure state your workflow is about to execute on.

TypeScript SDK Go SDK HTTPClient runInNodeMode Cron trigger

One extra step before execution.

The integration is a single HTTP call inserted between your trigger and your execution logic. Your workflow asks Invarians for the current regime: then decides based on that.

Step 1
Trigger fires
Cron, HTTP, or EVM log starts your workflow execution
Step 2: Invarians
Query sensing layer
Fetch regime context via HTTPClient: L1 × L2 × Bridge state
Step 3
Gate on regime
Proceed if S1D1, defer if S2D1 or BS2, handle STALE
Step 4
Execute
Your business logic: onchain write, cross-chain, computation
Why runInNodeMode?: External API calls in CRE are non-deterministic at the DON level (different nodes may receive slightly different timestamps or network latency). Wrapping the Invarians call in runInNodeMode with mode aggregation ensures all DON nodes reach BFT consensus on the regime value before your callback uses it.

Invarians sensing in a CRE workflow.

Drop this pattern into any existing CRE workflow. The sensing call adds one HTTP request per trigger fire: negligible overhead, complete infrastructure observability.

my-workflow/main.ts TypeScript
import {
  CronCapability, HTTPClient, handler,
  consensusStringModeAggregation, Runner,
  type NodeRuntime, type Runtime
} from "@chainlink/cre-sdk"

// ── Config (from config.staging.json) ──────────────────────────────
type Config = {
  schedule:         string
  inVariansApiKey:  string   // your inv_* API key
  targetChain:      string   // "arbitrum" | "ethereum" | "base" ...
}

// ── Invarians response type ─────────────────────────────────────────
type InVariansContext = {
  oracle_status: "FRESH" | "STALE" | "OFFLINE"
  regime:        "S1D1" | "S1D2" | "S2D1" | "S2D2"
  l2_regime:     string
  bridge_state:  "BS1" | "BS2"
}

// ── Step 1: Fetch Invarians context (runs on every DON node) ────────
const fetchInVariansContext = (
  nodeRuntime: NodeRuntime<Config>
): InVariansContext => {
  const http = new HTTPClient()
  const chain = nodeRuntime.config.targetChain

  const resp = http.sendRequest(nodeRuntime, {
    url:    `https://sdpilypwumxsyyipceew.supabase.co/functions/v1/attestation/${chain}`,
    method: "GET" as const,
    headers: {
      "x-api-key": nodeRuntime.config.inVariansApiKey,
    }
  }).result()

  const data = JSON.parse(
    new TextDecoder().decode(resp.body)
  )

  return {
    oracle_status: data.oracle_status,
    regime:        data.regime,
    l2_regime:     data.proof_of_execution_context?.l2_regime ?? "N/A",
    bridge_state:  data.proof_of_execution_context?.bridge_state ?? "BS1",
  }
}

// ── Step 2: Main callback ────────────────────────────────────────────
const onTrigger = (runtime: Runtime<Config>): string => {

  // Query Invarians: BFT consensus across DON nodes on regime string
  const ctx = runtime.runInNodeMode(
    fetchInVariansContext,
    consensusStringModeAggregation()   // mode = most common value across nodes
  )().result()

  runtime.log(`Invarians: ${ctx.oracle_status}: L1:${ctx.regime} L2:${ctx.l2_regime} Bridge:${ctx.bridge_state}`)

  // ── Gate 1: Oracle freshness ─────────────────────────────────────
  if (ctx.oracle_status !== "FRESH") {
    runtime.log("Oracle STALE: deferring execution")
    return "deferred:oracle_stale"
  }

  // ── Gate 2: L1 structural stress ─────────────────────────────────
  if (ctx.regime === "S2D1" || ctx.regime === "S2D2") {
    runtime.log(`Structural stress detected (${ctx.regime}): deferring`)
    return `deferred:structural_stress:${ctx.regime}`
  }

  // ── Gate 3: Bridge state (cross-chain workflows) ──────────────────
  if (ctx.bridge_state === "BS2") {
    runtime.log("Bridge degraded: deferring cross-chain execution")
    return "deferred:bridge_degraded"
  }

  // ── Infrastructure nominal: proceed ──────────────────────────────
  runtime.log(`Nominal (${ctx.regime}): executing`)

  // ... your business logic here (onchain write, CCIP, computation)

  return `executed:${ctx.regime}`
}

// ── Workflow entry point ─────────────────────────────────────────────
const initWorkflow = (config: Config) => {
  const cron = new CronCapability()
  return [handler(cron.trigger({ schedule: config.schedule }), onTrigger)]
}

export async function main() {
  const runner = await Runner.newRunner<Config>()
  await runner.run(initWorkflow)
}
config.staging.json JSON
{
  "schedule":        "*/30 * * * * *",
  "inVariansApiKey": "inv_your_key_here",
  "targetChain":     "arbitrum"
}

What your workflow checks before executing.

Each gate handles a distinct failure mode. None of these are visible to fee monitors or accessible without a dedicated sensing layer.

oracle_status !== "FRESH"
Oracle freshness
Invarians data older than 3,600 seconds triggers STALE. Executing on stale context is executing blind: defer and retry at the next trigger fire.
regime === "S2D1" or "S2D2"
Structural stress
S2 means infrastructure rhythm has deviated from baseline. This is the invisible regime: no fee signature, no gas spike. Detected only by Invarians τ axis measurement.
bridge_state === "BS2"
Bridge degraded
BS2 signals abnormal batch posting latency to L1. For cross-chain workflows, this means the bridge is not operating at normal cadence. Applicable from Phase 2C (~April 22, 2026).

What changes when you add sensing.

CRE workflow without Invarians
Executes without knowing if L2 is in S2D1
No structural information post-EIP-4844: fee monitors only
Cross-chain execution during bridge posting gap (BS2)
Arbitrum June 20, 2024: executes at ×1,649 nominal basefee
CRE workflow with Invarians sensing
Regime-aware: defers on S2D1, S2D2
Structural context signed and verifiable: not just gas price
Bridge state gate: cross-chain deferred when BS2
Arbitrum June 20, 2024: detects S1D2 × BS2: defers

Get started in 4 steps.

01
Set up Chainlink CRE
Follow the official getting started guide at docs.chain.link/cre. CRE is currently in Early Access: request access from Chainlink directly.
02
Get your Invarians API key
Sign up at invarians.com: free tier available (20 req/day for testing). Your key starts with inv_.
03
Add the sensing pattern to your workflow
Copy the TypeScript workflow above into your project's main.ts. Add your Invarians API key and target chain to config.staging.json.
04
Test and deploy
Simulate locally: verify the Invarians regime appears in your workflow logs. Deploy following the CRE documentation.

Build with sensing.

Invarians API is live. L1 + L2 + Bridge across 7 networks. Signed attestations. Free tier for development.