CoatingPassport schema, field by field.
The canonical JSON object Hullproof emits for every inspection. Not a report. A versioned, machine-readable asset condition object that persists across inspections. Every other output (PDF, Cognite CDF, OSDU, RDF, BIMCO) is a renderer of this.
1. Top-level identity
{
"passport_id": "uuid-v4",
"tenant_id": "uuid-v4",
...
}passport_id is the stable identifier for this asset's condition object across all inspections. tenant_id scopes the passport to a single customer organization. Multi-tenant from day one — every passport carries this, even when only one customer exists. Retrofit cost is brutal.
2. Asset
"asset": {
"asset_id": "...",
"asset_type": "vessel_hull | offshore_platform_jacket | wind_monopile | ...",
"material": "steel | coated_steel | concrete | composite | ...",
"metadata": { "imo": "...", "asset_name": "...", "location": "..." }
}asset_type is parameterized, not hardcoded. The core engine never knows about “vessel hulls” specifically — domain logic lives in src/lib/domains/<asset_type>. Adding a new vertical is a directory + a registry entry; no central refactor.
metadata holds asset-type- specific fields that don't generalize. IMO for vessels, wind park ID for monopiles, road segment for bridges. The universal-vs-specific test: if every asset_type needs the field, it goes top-level; otherwise it goes here.
3. Inspection
"inspection": {
"inspection_id": "uuid",
"timestamp_utc": "ISO8601",
"footage_source": {
"type": "rov | drone | handheld | fixed_camera | satellite | crawler | auv",
"device_metadata": {}
},
"operator": { "id": "...", "tier": "self_serve | enterprise_api | ..." }
}Footage source is captured for lineage + filtering, never for hardware coupling. The same passport schema works whether the footage came from a $50k subsea ROV or a smartphone in a drydock.
4. Findings
"findings": [
{
"finding_id": "uuid",
"category": "corrosion | coating_breakdown | crack | biofouling | ...",
"severity": "minor | moderate | severe | critical",
"confidence": 0.87,
"n_frames_supporting": 12,
"image_quality_score": 0.91,
"location_on_asset": { "coordinate_system": "...", "x": 0, "y": 0, "z": 0 },
"source_frames": ["frame_uri_1", "frame_uri_2"],
"ai_model_version": "hullproof-vlm-v2.1.3",
"human_reviewed": false,
"review_metadata": null
}
]This is where the rubber meets the regulation. Every finding carries the lineage trail EU AI Act, GDPR Art. 10, and NIS2 ask for. Anti-pattern #11 in our internal contract: never emit a finding without confidence + frame count + AI model version. Stripping these for performance is forbidden.
severity is calibrated per domain. “Critical” on a wind monopile is not the same metric as “critical” on a vessel hull. Severity rules live in the domain definition.
5. Compliance
"compliance": {
"eu_ai_act_class": "high_risk_ai_system",
"model_card_uri": "...",
"data_lineage_uri": "...",
"review_workflow_completed": false,
"applicable_standards": ["NORSOK N-005", "DNV-RP-C203", ...]
}applicable_standards is populated per asset type (DNV-RP-C203 for offshore jackets, IMO MEPC.207(62) for vessel hulls, Håndbok V441 for Norwegian bridges). The verifier knows what to check against without per-customer translation.
6. History
"history": [
{ "passport_version": 1, "timestamp": "...", "change_summary": "..." }
]CoatingPassport is versioned. Drift across inspections is observable. A severity that escalates from moderate to severe across two passports is visible in the history array — no need to fetch two reports and diff them by hand.
7. Interoperability
"interoperability": {
"cognite_cdf_compatible": true,
"osdu_asset_mapping": "...",
"veracity_export_uri": null,
"knowledge_graph_uri": null
}Flags + URIs for the downstream systems this passport is already exported into. Lets agentic stacks discover that the passport is OSDU-mapped without re-running the transformation.
8. Why this shape, not another
Output is structured data first, presentation second. Every renderer (PDF, CDF, OSDU, RDF, BIMCO, EU ETS bundle) derives from this object. The object is the contract; the renderers are conveniences. Operators who want a PDF still get one. Operators who want JSON streaming into Atlas AI get that without a PDF parsing step.
This is the single most consequential design choice in Hullproof. PDF-first AI tools become legacy when the next agentic stack lands. Structured-data-first tools carry forward.
See it live
JSON Schema, OpenAPI spec, and live demo passports — every field above, rendered from a real demo asset.