Skip to content

Profile document

A profile is the portable boundary between generation and rendering. It identifies its contract version, model, publication, site, optional provider semantics, and every chronological forecast hour. Day windows and local time remain consumer choices, with the site’s timezone echoed for new documents.

profile
├── schemaVersion + model
├── run { referenceTime, generatedAt, members? }
├── site { identity, coordinates, model elevation, timeZone? }
├── semantics? { gust?, precipitation?, smoke? }
└── hours[]
├── validAt
├── surface
├── levels[]
├── derived
└── smoke? { surfaceUgm3, columnMgm2, aot }

On the wire

FactValue
Published at<model>/sites/<site>.json — and each history archive line is the same document
ParseparseSiteForecastJson (raw text) / parseSiteForecast (parsed value) from @azohra/meteo.briefing/contract
Zod authoritysiteForecastSchema, pinning SITE_FORECAST_SCHEMA_VERSION (2 — wire v2)
JSON Schemaprofile.schema.json
Discoverythe model catalogue’s models array names every model that publishes profiles

Hour blocks

BlockRepresentative valuesContract rule
surfacepressure, temperature, moisture, winds, heat fluxes, precipitation, and declared optional science fieldsOptional capability fields are absent, never filled with zero
levelsheight, temperature, dew point, wind, and optional omega/cloud fractionEntries are ascending by height; pressure is the isobaric coordinate
derivedboundary-layer top, thermal velocity, cloud base, usable-lift topComputed by the forecast engine from the full required inputs

Run, site, and semantics

run.referenceTime is model initialization; run.generatedAt identifies the publication. Ensemble documents also declare total membership once at run.members.

The site block is sample provenance: where the atmosphere was sampled (id, name, latitude, longitude, optional timeZone) and what the model thinks the ground there is (site.modelElevationM — the plot floor and physics reference). It carries no launch elevation: the launch-decoupling redesign removed altitudeM, because a baked-in launch silently bound a grid forecast to one launch. The measured launch elevation lives in site-context.json’s elevation pick, and consumers pass it at render time as MeteogramOptions.launch — one document serves every launch its cell covers. Documents published before the removal may still carry the field; current parsers ignore it.

The v0.4 publication wave added optional site.timeZone, echoed from the site catalogue’s required IANA zone. New documents therefore carry their own local-time context without a catalogue join. The field remains optional so older schema-1 profiles stay valid; absence means the document predates the echo — never that the launch uses UTC — and requires a caller-owned timezone choice. This paragraph is the semantics’ one home; the contract guide lists each API’s documented fallback behaviour.

The optional semantics block lets a stored document retain gust, precipitation, and smoke meaning without joining the catalogue. Its absence creates no default. Where a model declares smoke (radiativelyCoupled or passive), each hour may also carry an optional smoke block (surfaceUgm3, columnMgm2, aot — all three required within it); the smoke document reference covers the semantics.

Deterministic and ensemble values

Every numeric position can contain a number or an ensemble percentile object. Consumers switch on the value shape, not a model name.

read-wind.ts
import { isEnsembleDropout, isEnsembleValue, type SiteForecast } from "@azohra/meteo.briefing/contract";
export function firstWindSpeed(profile: SiteForecast): number | null | undefined {
const speed = profile.hours[0]?.surface.windSpeedMps;
return speed === undefined
? undefined
: isEnsembleValue(speed)
? isEnsembleDropout(speed) ? null : speed.p50
: speed;
}

Per-position contributor counts and censoring are explained in Ensemble values.

Validate before use

Use parseSiteForecastJson for raw stored text or parseSiteForecast for an already-parsed value. Both return null on a rejected boundary. The package schemas and generated JSON Schema define every field constraint.

Discover capabilities

models.json is the machine-readable catalogue. It declares which models exist, their cadence and levels, and the semantics of optional capabilities. A consumer should render those declarations instead of assuming every model supplies the same fields.

See the contract guide’s unit boundary map for the integration crosswalk. The full field-by-field contract and generated JSON Schema live with the @azohra/meteo.briefing package.