Coastal Intelligence
Issued not yet
Assistant LLM provider: LLM …

Insurers, reinsurers, brokers

Risk index and signed exports

The insurer product: the 0 to 100 index per peril, the bay aggregation and suppression rules, the CSV export, verifying its Ed25519 signature with a complete Python example, and how to cite the licences.

Last reviewed

The insurer product

The bay risk index is one value from 0 to 100 per bay, UTC day and peril, built from the signals already computed for the bay's sites. It is designed for underwriting and portfolio monitoring: regional, anonymised and reproducible.

PerilSignals read
thermalsignal:thermal (level, degree_days_14d)
bloomsignal:bloom (index)
runoffsignal:runoff (level, salinity_drop_48h_psu, rain_24h_mm)
stormsignal:operability 6h bins (hs_m, wind_kn only); event:storm (daily peak at the bay reference cell)

Per site and day each peril gets a score from 0 to 100 (the break points are in the help article How the risk index is built). The method version is risk-index/2 and travels in every row.

Bay aggregation and suppression

  1. For each bay, day and peril the index is half the worst site's score plus half the mean of the sites' scores, rounded to 0.1. Level: below 40 low, below 70 watch, below 85 stress, otherwise critical.
  2. A day and peril with fewer than 3 contributing sites is suppressed: index, site_max and site_mean are null, suppressed is true and only n_sites is kept, so a bay figure never stands for one or two identifiable farms.
  3. No site id, name, licence number or position appears in a row or an export. The worst site is a value, never a site. The storm peril reads only physical wave height and wind, never a farm's working limits; a bay storm event sets a floor under that day's storm index.

Getting the index

  • REST: GET /agency/risk-index?bay=&from=&to= (product risk_index), one row per day and peril in the envelope described on the REST API page, with method and min_sites added.
  • MCP: tool get_risk_index with bay, from_date, to_date; see the MCP server page.
  • Signed CSV: GET /bays/{bay_id}/risk-index?from=&to=&format=csv with the same key, the same checks and the same access-log row. Without format it returns the JSON envelope.
Row fieldMeaning
bay_id, date, perilThe bay, the UTC day and the peril (thermal, bloom, runoff, storm).
index0 to 100, rounded to 0.1; null when suppressed or when the bay had no data that day.
levellow (below 40), watch (below 70), stress (below 85), critical; null with the index.
n_sitesSites with a value that day. Kept even when the row is suppressed.
site_max, site_meanThe worst site's score and the mean of the sites' scores, never which site; null when suppressed.
suppressedtrue when fewer than the minimum number of sites contributed: index, site_max and site_mean are null.
confidenceThe lowest confidence among the contributing values (high, medium, low).
lead_hours0 for an analysis day, above 0 for a forecast day.
source, product, scale, licencecoastal-intel, risk_index:<peril>, regional, and the licence ids of the inputs.
inputs, methodWhich signals were read and the method version, so a figure can be reproduced later.

The CSV export

One header line, then one line per day and peril, an empty cell for null, columns in this order: bay_id, date, peril, index, level, n_sites, site_max, site_mean, suppressed, confidence, lead_hours, source, product, scale, licence, inputs, method. The file name is risk-index-<bay>-<from>_<to>.csv (Content-Disposition) and the response is marked Cache-Control: no-store.

curl -D headers.txt -o risk-index.csv -H "Authorization: Bearer $AGENCY_KEY" \
  "https://coastal-intelligence.triskelionaisystems.com/api/bays/<bay id>/risk-index?from=<YYYY-MM-DD>&to=<YYYY-MM-DD>&format=csv"

Keep the response headers with the file: they are the signature.

Verifying the signature

The bytes of every CSV export are signed with Ed25519. The response carries X-Content-SHA256, X-Signature, X-Signature-Alg, X-Signature-Key-Id. The public key is served without a key at GET https://coastal-intelligence.triskelionaisystems.com/api/exports/signing-key as {algorithm, key_id, public_key, development}, where public_key is the base64 of the raw 32-byte key. A key_id starting with dev- (and development: true) is the fixed development key: such a signature proves nothing outside development, and a verifier should refuse it.

A complete check in Python (standard library plus the cryptography package):

import base64
import hashlib
import json
import urllib.request

from cryptography.exceptions import InvalidSignature  # pip install cryptography
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

API = "https://coastal-intelligence.triskelionaisystems.com/api"
KEY = "<your key>"  # read it from the environment in real code
BAY, FROM, TO = "<bay id>", "<YYYY-MM-DD>", "<YYYY-MM-DD>"

# 1. the signed CSV and its headers
req = urllib.request.Request(
    f"{API}/bays/{BAY}/risk-index?from={FROM}&to={TO}&format=csv",
    headers={"Authorization": f"Bearer {KEY}"},
)
with urllib.request.urlopen(req) as res:
    body = res.read()
    headers = dict(res.getheaders())

# 2. the public key (no key needed)
with urllib.request.urlopen(f"{API}/exports/signing-key") as res:
    signing = json.load(res)

# 3. checks: algorithm and key id match, the digest matches, the signature verifies
assert headers["X-Signature-Alg"] == signing["algorithm"] == "ed25519"
assert headers["X-Signature-Key-Id"] == signing["key_id"]
assert hashlib.sha256(body).hexdigest() == headers["X-Content-SHA256"]
public = Ed25519PublicKey.from_public_bytes(base64.b64decode(signing["public_key"]))
try:
    public.verify(base64.b64decode(headers["X-Signature"]), body)
except InvalidSignature:
    raise SystemExit("the file was altered or is not from this server")
if signing["development"]:
    print("warning: a development signing key; this signature proves nothing outside development")

with open(f"risk-index-{BAY}-{FROM}_{TO}.csv", "wb") as f:
    f.write(body)  # keep the file and the X-Signature header together

Any Ed25519 library works the same way: decode public_key and X-Signature from base64 and verify the signature over the exact bytes of the file. Re-encoding the CSV (line endings, a spreadsheet save) breaks the signature by design.

Citing the licences and attribution

Every row's licence names the licences of its inputs, and the JSON envelope lists them with the exact attribution lines they require. When you publish or pass on a figure, a chart or a file, keep those lines and the method version. The licences this deployment can return:

The CSV has no envelope: take the attribution lines from the JSON form of the same call (or from GET /datasets) and cite them with the file. Details on the Data model and licences page.

The index is regional: model and satellite inputs, verified in the bay only where a station allowed it. Only the bloom signal has a recorded back-test so far. It is an indicator for a named activity, not food-safety advice and not a warranty of conditions: official closures (Marine Institute HAB bulletin, SFPA) and observations prevail.

Risk index and signed exports · Developers · Coastal Intelligence