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.
The universal path is raw Monte Carlo samples — one annual loss per iteration. If your engine can dump those, it can emit open-lec.
# 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))
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.
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.
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
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.
One renderer, three delivery modes: interactive in the browser, a React component for dashboards, and headless SVG for anything that isn't a browser.
<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.
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.
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.
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.
The validator's exit codes are CI-friendly, so conformance becomes a check, not a review comment.
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
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.
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.
Everything that speaks JSON Schema draft 2020-12 — form generators, contract tests, IDE validation — can consume openlec.schema.json directly.