Skip to content

v0.7.0 — Maritime Norway

Choose a tag to compare

@iamkm1 iamkm1 released this 25 Jul 23:05
0532c18

Live AIS streaming, the vessel and aquaculture registers, a cross-provider vessel profile, and the reusable OAuth2 and NDJSON infrastructure they needed.

Two things to know before you start: BarentsWatch needs two separate credentials, and absent AIS data never means an absent vessel.

New namespaces

  • sdk.ais — BarentsWatch AIS. Vessel positions and identity (getVesselSnapshot, getVesselSnapshots, getLatestPositions), recorded tracks (getTrackLast24Hours, getTrack), area and name search (getMmsiInArea, searchVessels), the published coverage area (getCoverageArea), and live streaming (streamPositions, streamMessages) as a plain AsyncIterable with cancellation.
  • sdk.marine — BarentsWatch wave and sea-current point forecasts.
  • sdk.fisheries — Fiskeridirektoratet's fishing-vessel register and public aquaculture register, both open and needing no credentials.
  • profiles.vessel() — joins AIS to the fishing-vessel register on the radio call sign, plus MET conditions and the nearest Kartverket place name.

Infrastructure

Reusable OAuth2 client-credentials support with in-memory tokens, refresh ahead of expiry, single-flight concurrent refresh and abort support. Tokens are never persisted and never appear in errors or logs. A web-standard NDJSON parser returning AsyncIterable<T> with per-record validation, chunk-boundary reassembly, a configurable buffered-record limit and reader cleanup on early exit — no automatic reconnection in this release. Shared Coordinates and BoundingBox primitives.

Two credentials, not one

BarentsWatch issues separate AIS-clients and API-clients, and a secret registered for one is rejected by the other. Configure them independently as credentials["barentswatch-ais"] and credentials.barentswatch.

Coverage limits

AIS coverage excludes fishing vessels under 15 m and leisure vessels under 45 m, is limited to Norwegian waters, and retains 14 days. An empty track or a no-recent-data profile means only that BarentsWatch had nothing to publish — never that a vessel does not exist. ais.getCoverageArea() tells absence of data from absence of coverage.

Privacy

The vessel register's private-owner names, postal codes and towns are suppressed in the runtime schema, so raw payloads and cached values are covered too.

Verification

Verified end to end against the live BarentsWatch services with real credentials: both scopes, real tokens, 812 live positions, 403 snapshots, 54 track points, 40 streamed messages and all three marine endpoints validating with no undeclared fields. That live run caught four defects no offline test could reach — the met/hydro broadcast dropping 28 of its 43 fields, getWaveForecastSeries() mishandling both forms of "no coverage", message labels matched against a spelling the service does not use, and validated fields discarded in normalization.

1024 tests across 35 files; 96.55% lines, 81.21% branches, 99.18% functions. No breaking changes.