integrations · emit → render → automate

Meet your tools where they are

open-lec describes results, not models — so adoption is an output format, not a migration. These recipes cover the three moments where the format earns its keep: getting curves out of engines, getting charts into surfaces, and keeping both honest in CI.


01  Emit

From your engine to a document

The universal path is raw Monte Carlo samples — one annual loss per iteration. If your engine can dump those, it can emit open-lec.

python · numpyno library needed — the format is just JSON
emit_from_numpy.py
# Any simulation engine works — all you need is per-iteration annual losses.
import json
import numpy as np

losses = run_your_monte_carlo()      # shape (n_iterations,)
grid = np.logspace(np.log10(max(losses.min(), 1.0)),
                   np.log10(losses.max()), 24)

points = [{"loss": float(x), "p": float((losses >= x).mean())} for x in grid]

doc = {
    "openlec": "0.2",
    "metadata": {"currency": "USD", "title": "Ransomware exposure",
                 "iterations": int(losses.size)},
    "scenarios": [{"id": "baseline",
                   "curve": {"points": points,
                             "interpolation": "log-loss"}}],  # log-spaced grid → log-loss profile
}
print(json.dumps(doc))
✓ tip

Want an uncertainty band? Put a Wilson score interval on each estimated p̂ — the repository's examples/emit_from_numpy.py shows the exact math, and it is what openlec from samples does for you.

netflix riskquantscenario CSV → full document

riskquant's scenario format (identifier,name,probability,low_loss,high_loss) carries model inputs, not curves — so the adapter re-runs its Poisson × lognormal model with a seeded Monte Carlo and emits per-scenario curves, sampling bands, and an optional portfolio aggregate.

$openlec from riskquant scenarios.csv --title "FY26 portfolio" --aggregate > lec.json
scenarios.csv
identifier,name,probability,low_loss,high_loss
ransomware,Ransomware outbreak,0.35,250000,8000000
bec,Business email compromise,1.4,40000,900000
insider,Malicious insider,0.08,500000,20000000
csv & raw samplesspreadsheets, FAIR workbooks, anything
$openlec from csv curve.csv > lec.json # loss,p rows you already have
$openlec from samples losses.json > lec.json # one annual loss per iteration
$openlec from samples sim.csv --band 0.95 | openlec stats -

Export a column of simulated annual losses from a FAIR spreadsheet, a CyberQRM run, or any GRC platform, and the samples adapter produces the curve, a Wilson sampling band at your chosen level, and per-scenario statistics. Writing a first-class adapter for your engine is typically under 50 lines with scenarioFromSamples + makeDocument from open-lec/adapters.

02  Render

From a document to every surface

One renderer, three delivery modes: interactive in the browser, a React component for dashboards, and headless SVG for anything that isn't a browser.

web page
embed.html
<div id="chart"></div>
<script type="module">
  import { render, validate } from "open-lec";

  const doc = await fetch("/lec.json").then((r) => r.json());
  if (validate(doc).valid) {
    render(document.querySelector("#chart"), doc, { logX: true });
  }
</script>

The chart is a responsive SVG with hover crosshair, return periods, and compact currency ticks. Pass a theme to match your product — this site feeds it Hearth tokens and re-renders on theme switch.

react dashboard
Exposure.jsx
import OpenLEC from "open-lec/react";

export default function Exposure({ doc }) {
  return (
    <OpenLEC
      doc={doc}
      options={{ logX: true, showBands: true }}
      onValidation={(r) => !r.valid && report(r.errors)}
    />
  );
}

Validates on every doc change, renders only conforming documents, and cleans up on unmount — drop it next to your existing charts.

decks & reportsheadless svg — no browser anywhere
$openlec render lec.json --width 1200 -o chart.svg
$npx @resvg/resvg-cli chart.svg # → PNG for the deck

Or from code: import { renderToSvgString } from "open-lec/headless". The headless path shares all drawing code with the browser renderer, so the chart in the quarterly report is pixel-for-pixel the chart in the dashboard. PNG conversion stays out of the package to keep it at zero dependencies.

no-code sharingthe playground is an integration too
◆ note

Playground share links encode the whole document in the URL fragment — nothing is uploaded. Paste a curve, copy the link, and anyone can open the live chart, inspect the JSON, and export SVG without installing anything.

03  Automate

Keep every curve conforming

The validator's exit codes are CI-friendly, so conformance becomes a check, not a review comment.

.github/workflows/validate-lec.yml
name: validate-lec
on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm install github:sh4t/open-lec
      - run: npx openlec validate reports/*.lec.json
exit 0 | 1
Fail the build, name the path

Non-conforming documents fail with the exact JSONPath and rule — $.scenarios[0].curve.points[3].loss — losses must be strictly ascending — so the fix never needs a maintainer.

stats --json
Wire numbers into anything

openlec stats lec.json --json emits derived EAL and 1-in-N losses as JSON — pipe it into dashboards, tickets, or a diff against last quarter's run.

2020-12
Schema-first toolchains

Everything that speaks JSON Schema draft 2020-12 — form generators, contract tests, IDE validation — can consume openlec.schema.json directly.