Regulatory Compliance Reference

The Regulatory Compliance Reference is the layer that turns maritime regulatory mandates into structured, validated, machine-checkable data — SOLAS Verified Gross Mass, the IMO FAL standardized forms, and ISPS security levels — so port-authority and terminal systems consume compliance as typed records rather than as scanned paperwork and email attachments. Three mandates dominate every export container and every port call: SOLAS Chapter VI Regulation 2 requires a signed Verified Gross Mass (VGM) before a box is loaded; the IMO FAL Convention standardizes the seven arrival-and-departure declarations (FAL 1 through FAL 7); and the ISPS Code sets the security posture (levels 1, 2, and 3) that governs what a facility may accept. This reference sits inside the Core Maritime Architecture & Taxonomy framework and owns one job: model each mandate as an immutable, typed pydantic record with a replayable audit trail, so a port authority team never argues with a customs inspector about what was declared, when, and by whom.

Three maritime regulatory regimes mapped into one compliance data and audit layer Three regulatory regimes on the left — SOLAS VGM under Chapter VI Regulation 2, the IMO FAL Convention forms 1 through 7, and the ISPS Code security levels 1 to 3 — each map to a typed pydantic model: VgmDeclaration, FalForm, and IspsRecord. All three models feed a single compliance data and audit layer that validates and versions them, which emits an immutable audit trail keyed on a correlation id and a sha256 content hash. The validated compliance event fans out to three consumers: the terminal operating system, the customs gateway, and the port authority. map map map emit SOLAS VGM Ch VI Reg 2 IMO FAL FAL 1–7 forms ISPS Code levels 1 / 2 / 3 VgmDeclaration Decimal kg · signed FalForm[1–7] declaration models IspsRecord level · DoS Compliance data & audit layer typed · validated · versioned Immutable audit trail correlation_id · sha256 Terminal OS (TOS) stowage · yard Customs gateway declarations Port authority PSC · FAL clearance
Each regulatory regime maps to a typed model, all three flow through one validated compliance and audit layer, and the immutable audit trail feeds the terminal operating system, customs, and the port authority.

Ingestion Boundary & Protocol Handling

Compliance data does not arrive on one channel or in one shape, and the reference treats that heterogeneity as the first design constraint rather than a downstream surprise. A Verified Gross Mass reaches a port community system as a UN/EDIFACT VERMAS message over an AS2 or SFTP link, as a REST webhook from a weighbridge integrator, or — still, at some terminals — as a keyed value from a portal form. The FAL declarations arrive as a Maritime Single Window submission ahead of a port call, increasingly as structured XML or JSON rather than the paper forms the Convention originally standardized. ISPS security-level changes and Declaration of Security records arrive as directives from a flag administration or a Port Facility Security Officer, often out of band from cargo traffic entirely. The boundary’s contract is uniform regardless of transport: decode the payload, resolve which mandate and which version it declares, and refuse to let it cross until it matches an explicit model.

Version resolution is the load-bearing check here, exactly as it is one layer up in the Schema Validation Frameworks gate. SOLAS Chapter VI Regulation 2 has been in force with a stable VGM contract since July 2016, but the VERMAS directory version (D.16A, D.19B) still drifts between carriers, and the FAL forms carry their own revision lineage as the Convention is amended. The boundary inspects the message-type and version header — the UNH composite for EDIFACT, an API version tag for REST, a schema URI for a Single Window XML envelope — and routes each payload to the correct typed model before any field is read. The segment-level envelope checks that gate an EDIFACT interchange happen upstream; this reference assumes a decoded candidate record and concerns itself with the regulatory meaning of the fields inside it.

Header Example Purpose
mandate SOLAS_VGM / IMO_FAL / ISPS Selects the regulatory model family applied at the boundary
record_type VERMAS / FAL2 / DECLARATION_OF_SECURITY Resolves the exact typed model within the family
schema_version D.16A / fal_2019 / isps_v2 Routes to the correct field-rule revision
party_ref MAEU / port facility GISIS id Shipper or facility identity for signature and authorization checks
raw_payload_sha256 c41b… Content-addressed receipt anchoring the immutable audit record

Python Data Structure Mapping

Every mandate becomes a typed pydantic model, and nothing crosses the boundary as a loose dict. The rule is identical to the one the parent Bill of Lading Schema Mapping layer enforces for commercial documents: a malformed regulatory record must fail at construction, where it can be classified and audited, not three hops later inside a customs submission or a stowage plan. Weights are decimal.Decimal in kilograms, never float, because a rounding error on a VGM is a vessel-stability defect and not a display quirk; timestamps are timezone-aware UTC; enumerated fields such as the ISPS security level are real Enum types so an out-of-range value cannot be represented at all.

The three model families map cleanly onto the three regimes, and each has a dedicated reference page in this collection. SOLAS VGM declarations resolve into a signed weight model detailed in modeling SOLAS VGM declarations as pydantic schemas; the FAL forms resolve into a set of declaration models covered in generating IMO FAL forms as structured data; and the ISPS security posture resolves into a port-call attribute described in encoding ISPS security levels in port-call records.

Mandate Source field Python model / field Constraint
SOLAS VGM VERMAS MEA+AAE+VGM VgmDeclaration.verified_gross_mass_kg: Decimal Field(gt=0), kilograms, method SM1/SM2
SOLAS VGM shipper authorization VgmDeclaration.responsible_party: str Non-empty; signatory name recorded for audit
IMO FAL 1 General Declaration GeneralDeclaration model Vessel IMO number, call sign, voyage ref
IMO FAL 2 Cargo Declaration CargoDeclaration model Per-item HS prefix, marks, package counts
IMO FAL 7 Dangerous Goods DangerousGoodsForm model UN number, IMDG class, packing group
ISPS active security level IspsRecord.security_level: SecurityLevel Enum in {1, 2, 3}
ISPS Declaration of Security IspsRecord.dos: DeclarationOfSecurity Required when ship/facility levels differ

The seven FAL forms — FAL 1 General Declaration, FAL 2 Cargo Declaration, FAL 3 Ship’s Stores Declaration, FAL 4 Crew’s Effects Declaration, FAL 5 Crew List, FAL 6 Passenger List, and FAL 7 Dangerous Goods — are not free-form documents but a fixed vocabulary, which is precisely why they model well. A representative VGM declaration illustrates the pattern the whole reference follows:

from __future__ import annotations

from datetime import datetime
from decimal import Decimal
from enum import Enum

import structlog
from pydantic import BaseModel, Field, field_validator

log = structlog.get_logger("maritime.compliance")


class WeighingMethod(str, Enum):
    """SOLAS Chapter VI Regulation 2 weighing methods."""

    METHOD_1 = "SM1"  # weigh the whole packed container
    METHOD_2 = "SM2"  # weigh contents, add verified tare


class VgmDeclaration(BaseModel):
    """Typed contract for a Verified Gross Mass declaration."""

    container_id: str = Field(..., min_length=11, max_length=11)
    verified_gross_mass_kg: Decimal = Field(..., gt=0)
    weighing_method: WeighingMethod
    responsible_party: str = Field(..., min_length=1)
    declared_at: datetime

    @field_validator("declared_at")
    @classmethod
    def enforce_utc(cls, v: datetime) -> datetime:
        if v.tzinfo is None:
            raise ValueError("VGM timestamp must be timezone-aware (UTC required)")
        return v

Schema definitions are version-controlled alongside pipeline code and tagged, so a change to a FAL field rule or a VGM tolerance is always attributable to a commit and reversible without disrupting a live port-call cycle.

Validation, Quarantine & Compliance Auditing

Validation runs in three ordered tiers, and the tier a record fails in decides where it is routed — the same structural, semantic, regulatory ladder the whole architecture uses, applied here to regulatory content specifically. Classification is what keeps the port call moving.

  1. Structural — a record that will not deserialize into its typed model: a VERMAS missing its MEA weight composite, a FAL submission with a malformed envelope, an ISPS directive with no security level. This raises a pydantic.ValidationError and is transmission corruption a human cannot fix without engineering; it fails fast to the dead-letter queue.
  2. Semantic — a record that parsed but carries an inconsistent or unresolvable value: a container ID whose ISO 6346 check digit drifts, a FAL 2 line whose HS prefix does not resolve, a VGM declared in tonnes where kilograms were assumed. These are recoverable business exceptions and route to quarantine for ops reconciliation, never to the dead-letter queue.
  3. Regulatory — a record that is well-formed and internally consistent but violates the mandate itself: a VGM below the container’s tare weight, an export box presented for loading with no signed VGM at all, a Declaration of Security absent when the ship and facility declare different ISPS levels, a FAL 7 dangerous-goods entry whose IMDG segregation conflicts with the stow. This is a compliance conflict that routes to review — it must not be silently accepted.

Every one of those verdicts writes an immutable audit record. This is the non-negotiable heart of the reference: SOLAS, the ISPS Code, and the FAL Convention all presume that a port state control officer or a customs inspector can later reconstruct exactly what was declared, by whom, under which rule version, and what the system decided. An append-only audit event keyed on the record’s correlation ID and content hash makes that reconstruction a replay rather than an investigation:

{
  "correlation_id": "vgm_2f8c1a7e-6b90-4d21-9f3a-8c1e2b7d0a45",
  "timestamp": "2026-07-14T06:12:44.117Z",
  "mandate": "SOLAS_VGM",
  "record_type": "VERMAS",
  "container_id": "MSKU1234565",
  "verdict": "COMPLIANCE_REVIEW",
  "rule": "VGM_BELOW_TARE",
  "rule_version": "solas_ch6_reg2",
  "responsible_party": "MAEU",
  "payload_sha256": "c41b9f2c7d8e0a13",
  "pipeline_stage": "regulatory_tier"
}

Records ship as structured JSON via structlog to the observability stack, with alert thresholds tied to verdict frequency — a spike in VGM_MISSING verdicts against one carrier almost always means an upstream weighbridge integration broke, not that dozens of shippers simultaneously stopped weighing boxes. The immutability of the trail is what gives it evidentiary weight: an audit record is written once and never mutated, and a correction produces a new record that references the prior one rather than overwriting it.

Downstream Integration

A regulatory record that clears all three tiers does not become a file — it becomes a validated compliance event other systems can act on without re-checking. A cleared VGM releases a container to the stowage planner and updates the terminal operating system’s load list; a submitted and validated FAL set clears a vessel for the arrival formalities the port authority runs; a confirmed ISPS level and, where required, a Declaration of Security flows into the access-control decisions made at the facility perimeter. Each of these consumers trusts the event precisely because the audit trail lets it prove, later, why it acted.

The ISPS security level is where this reference hands off most directly to the rest of the taxonomy. An active level is not a passive annotation on a port-call record — it must actively tighten what endpoints and manifests are reachable, and that enforcement is implemented in the Maritime Security Boundary Setup zero-trust controls. The lifecycle transitions a cleared FAL set and a released VGM authorize are consumed by the Port Call Workflow Design state machine, where a VGM release gates the load milestone and a FAL clearance gates arrival formalities. Container-level regulatory fields — the box a VGM binds to, the equipment a dangerous-goods entry segregates — resolve into the Container Hierarchy Data Models topology so the whole platform reasons about one equipment structure rather than several.

Fallback Chains & Uptime Guarantees

Regulatory data has a hard property that ordinary traffic does not: it is often legally time-boxed. A VGM must be communicated before the vessel’s cut-off; FAL submissions have Single Window deadlines ahead of arrival; an ISPS level change takes effect the moment it is declared, not when a system happens to process it. The reference therefore treats registry drift and dependency failure as expected, logged modes and degrades toward quarantine rather than toward silent loss.

  • Exponential backoff with jitter — retry transient 429/503 responses from a Single Window, a customs gateway, or a GISIS facility lookup with a capped delay and per-attempt jitter, capping at three attempts before falling back, so a degraded authority API cannot pin the compliance workers.
  • Circuit breakers — open per external endpoint after consecutive failures and serve a cached snapshot of the last-known-good registry (HS/HTS tariff tables, UN/LOCODE, IMDG segregation rules) rather than hammering a failing service, so VGM release survives a port-authority API outage.
  • Never drop a time-boxed record — a VGM or FAL submission that cannot be fully validated because a registry is unreachable degrades to quarantine with an explicit reason and a preserved timestamp, so the legal moment of declaration is never lost even when downstream confirmation is delayed.
  • Fail closed on security, fail open on paperwork — an ISPS level-3 declaration that cannot be confirmed tightens access by default, because the safe failure for a security control is to deny; a FAL form awaiting a registry lookup holds in quarantine rather than blocking the vessel, because the safe failure for a facilitation document is to reconcile.

CPU-bound pydantic validation is offloaded to a process pool so it never blocks the event loop, while I/O-bound registry lookups run concurrently; a record that exhausts its retries increments a failure counter and routes to quarantine with its declaration timestamp intact.

Step-by-step Implementation Guide

The steps below wire a mandate-aware compliance gate any worker can call. Each is runnable in isolation and uses full type annotations with structlog for structured JSON logging.

Step 1 — Resolve the mandate and select the typed model

Route each record to the correct model family and version before applying any field rule, so a D.16A VERMAS is never judged against a FAL contract.

from pydantic import BaseModel

COMPLIANCE_MODELS: dict[tuple[str, str], type[BaseModel]] = {
    ("SOLAS_VGM", "VERMAS"): VgmDeclaration,
    ("IMO_FAL", "FAL2"): CargoDeclaration,
    ("ISPS", "DECLARATION_OF_SECURITY"): IspsRecord,
}


def select_model(headers: dict[str, str]) -> type[BaseModel]:
    key = (headers["mandate"], headers["record_type"])
    model = COMPLIANCE_MODELS.get(key)
    if model is None:
        log.error("unknown_mandate", mandate=key)
        raise KeyError(f"no compliance model for {key}")
    return model

Step 2 — Model and validate the SOLAS VGM declaration

Deserialize the VERMAS weight into a Decimal kilogram model and enforce Chapter VI Regulation 2: a signed responsible party and a positive mass above tare.

from decimal import Decimal
from pydantic import ValidationError


def validate_vgm(raw: bytes, *, tare_kg: Decimal) -> str:
    try:
        vgm = VgmDeclaration.model_validate_json(raw)
    except ValidationError as exc:
        log.error("vgm_structural_reject", errors=exc.errors())
        return "DLQ"
    if vgm.verified_gross_mass_kg <= tare_kg:
        log.warning("vgm_below_tare", value=str(vgm.verified_gross_mass_kg))
        return "COMPLIANCE_REVIEW"
    log.info("vgm_validated", container=vgm.container_id, method=vgm.weighing_method.value)
    return "PASS"

Step 3 — Emit the IMO FAL form set as structured data

Assemble the arrival declarations into typed models keyed by form number, so a Single Window submission is generated from validated data rather than hand-keyed.

def build_fal_set(port_call: PortCall) -> dict[str, BaseModel]:
    forms: dict[str, BaseModel] = {
        "FAL1": GeneralDeclaration.from_port_call(port_call),
        "FAL2": CargoDeclaration.from_manifest(port_call.manifest),
        "FAL5": CrewList.from_roster(port_call.crew),
    }
    if port_call.dangerous_goods:
        forms["FAL7"] = DangerousGoodsForm.from_dg(port_call.dangerous_goods)
    log.info("fal_set_built", call=port_call.ref, forms=sorted(forms))
    return forms

Step 4 — Encode the ISPS security level and Declaration of Security

Set the active level as an enumerated attribute and require a Declaration of Security whenever the ship and facility levels differ.

def encode_isps(ship_level: SecurityLevel, facility_level: SecurityLevel,
                *, dos: DeclarationOfSecurity | None) -> IspsRecord:
    if ship_level != facility_level and dos is None:
        log.warning("dos_required", ship=ship_level.value, facility=facility_level.value)
        raise ValueError("Declaration of Security required when levels differ")
    record = IspsRecord(security_level=max(ship_level, facility_level), dos=dos)
    log.info("isps_encoded", level=record.security_level.value)
    return record

Step 5 — Write the immutable audit record and route the verdict

Every record, cleared or held, writes exactly one audit entry keyed on its correlation ID and payload hash so the decision is replayable.

def finalize(record: BaseModel, verdict: str, *, correlation_id: str, sha256: str) -> None:
    audit.append(
        correlation_id=correlation_id,
        mandate=type(record).__name__,
        verdict=verdict,
        payload_sha256=sha256,
    )
    log.info("compliance_finalized", correlation_id=correlation_id, verdict=verdict)

A ValidationError in Step 2 is a structural defect for the dead-letter queue; an inconsistency in Step 3 or 4 is a recoverable exception for quarantine; a COMPLIANCE_REVIEW verdict is a regulatory conflict for a human. Keeping those paths distinct is what lets a time-boxed VGM or FAL submission reconcile automatically once a registry recovers, without ever discarding the declaration.

Troubleshooting Common Failures

Symptom Root cause Fix
VGM rejected on a legitimate load SOLAS threshold applied before unit normalization Coerce MEA to Decimal kilograms first, then compare against tare in one unit
Container presented with no VGM at cut-off Shipper omitted the signed VGM or the VERMAS was lost Route to COMPLIANCE_REVIEW, never to the stowage planner; loading an unverified box breaches SOLAS
FAL 2 line fails HS resolution Stale tariff registry or a newly gazetted code Quarantine, refresh the cached HS/HTS snapshot, reconcile — do not hard-reject
ISPS access decision ignores level change Level stored as a passive annotation, not an enforced attribute Feed the enumerated level into the security boundary so a level-3 declaration tightens access
Declaration of Security missing Ship and facility declared different ISPS levels Require a DeclarationOfSecurity; hold the port call until it is supplied
Same VGM declared twice Non-idempotent gate under AS2 retransmission Key the audit record on (container_id, responsible_party, sha256) so replays no-op
FAL 7 dangerous-goods entry conflicts with stow IMDG segregation rule violated for the assigned slot Route to compliance review; resolve against the container hierarchy before loading

Frequently Asked Questions

Why model regulatory mandates as typed data instead of storing the forms?

Because a scanned form is evidence a human must read, while a typed record is a contract a system can enforce and replay. SOLAS VGM, the IMO FAL declarations, and ISPS security levels each carry a fixed vocabulary of fields, so each maps cleanly onto a pydantic model that fails at construction when a mandatory value is missing or out of range. Modeling them as data lets the platform reject a non-compliant record at the boundary, emit an immutable audit trail, and prove to a port state control inspector exactly what was declared and decided — none of which a stored PDF supports.

What happens to an export container that arrives without a Verified Gross Mass?

Under SOLAS Chapter VI Regulation 2, no packed export container may be loaded without a signed VGM, so a record missing one, or carrying a mass below the container’s tare weight, routes to compliance review and never to the stowage planner. The reference treats a missing VGM as a regulatory conflict rather than a structural error: the record parsed cleanly, it simply violates the mandate. It can rejoin the pipeline the moment a corrected, signed VGM is supplied, and the audit trail records both the rejection and the eventual clearance.

When is a Declaration of Security required under the ISPS Code?

A Declaration of Security formalizes the security responsibilities agreed between a ship and a port facility, and it is required whenever their declared ISPS security levels differ — for example when a facility operates at level 1 but a vessel arrives at level 2 — as well as in other higher-risk circumstances a flag administration may specify. The reference encodes the active level as an enumerated attribute and refuses to construct an ISPS record with mismatched levels and no Declaration of Security, so the requirement is enforced structurally rather than left to an operator to remember.

How does the reference keep a time-boxed VGM or FAL submission from being lost during an outage?

Regulatory records are often legally time-boxed — a VGM before cut-off, a FAL submission before a Single Window deadline — so the reference never drops one when a downstream registry is unreachable. Instead it degrades the record to quarantine with an explicit reason and a preserved declaration timestamp, so the legal moment of declaration survives even when final confirmation is delayed. External lookups sit behind circuit breakers with cached fallback snapshots, and quarantined records reconcile automatically once the authoritative source recovers.

Up: Core Maritime Architecture & Taxonomy — the parent framework governing data models, state machines, and compliance controls.