Flexible ingress
Receive plain UDP over IPv4 or IPv6, or authenticated encrypted UDPSEC station traffic.
AIS stream processing and routing platform
Normalize · Deduplicate · Tag · Route · Forward
Receive AIS feeds, turn receiver traffic into one controlled logical stream, and deliver it to the UDP egress targets that need it.
AIS stream processing
AISMixer keeps transport, NMEA assembly, metadata, deduplication, and delivery in one near-real-time processing path.
Receive plain UDP over IPv4 or IPv6, or authenticated encrypted UDPSEC station traffic.
Extract both !AIVDM and !AIVDO from realistic receiver and application output.
Assemble fully out-of-order fragments into complete logical messages and emit them in ordinal order.
Make one group-atomic decision for each complete multipart message, globally in broadcast mode or separately for each routing target.
Control NMEA TAG s, c, and g across the multipart lifecycle while keeping metadata separate from routing identity.
Forward accepted sentences to every configured UDP output in broadcast mode or to the named targets selected by logical routing.
Tested reference behaviour
The Python implementation defines observable event, assembly, metadata, deduplication, routing, and cleanup behaviour. Exact boundaries and edge cases remain in the normative behavioural contract.
Only an IngressEvent whose raw_line is a string enters processing. One immutable routing snapshot governs each accepted event. When it contains a routing table, one source match governs all extracted sentences; a concurrent update affects a later event.
Fragments may arrive fully out of order. An exact duplicate at an occupied ordinal is idempotent for assembly and does not refresh lifetime; conflicting content invalidates the live generation. TAG context follows the same group boundaries.
One decision covers the complete ordered multipart group. Legacy mode uses global scope; routing mode uses independent target scope. A duplicate observation does not extend the retained deduplication lifetime.
Process-local TTL lifecycles are explicit and tested. Expired state is handled before deterministic eviction of live state when a configured capacity applies.
Need the exact edge-case semantics? Read the behavioural contract.
Explicit lifecycle ownership
The reference implementation makes retained state, lifetime progress, capacity admission, cleanup, and observation explicit instead of relying on incidental container behaviour.
Identity is the exact sentence or ordered multipart tuple, in a global or per-target scope. TTL is deterministic, duplicates do not refresh retention, and expired entries are removed before the oldest live entry is evicted.
The Python object accepts optional max_entries; current service wiring leaves it at None, so this dimension is unbounded in the current service.
Fragments occupy ordinal slots. Unique progress refreshes group lifetime; an exact duplicate does not. Conflict, expiry, capacity removal, completion, and reset remain distinct lifecycle outcomes and clean related TAG context.
The Python object accepts optional max_fragments_per_group and max_pending_groups; current service wiring leaves both at None.
Handshake replay records, active sessions, and per-session nonce records are hard-bounded. Monotonic time owns local lifetime; expired state is removed before deterministic live eviction.
Sessions are ordered by recent valid activity. A successful same-address handshake replaces only that session and its nonce state. All secure state is process-local and is lost at restart.
Deduplication, assembly, and secure state expose immutable point-in-time statistics for inspection and regression testing. Reading a snapshot does not run cleanup or alter an earlier snapshot.
This is a basis for future differential conformance, not a complete monitoring or Prometheus export system.
Local by design. This state is non-durable and is not shared across processes. A multiprocessing coordinator does not exist yet.
Reference semantics and staged evolution
Documented observable behaviour allows future implementation work to proceed in stages while the current Python runtime remains the reference.
Differential tests can compare ordered output, TAG metadata, routing targets, deduplication decisions, lifecycle outcomes, and explicit no-output cases.
Explicit state ownership and cleanup boundaries support incremental extraction of processing work instead of requiring one large runtime rewrite.
No native processor exists today. No C or C++ API, ABI, or binding strategy has been selected, and this documentation update makes no performance claim.
Routing and logical zones
Static logical routing connects named ingress sources to named UDP egress targets through reusable source sets and ordered routes.
Define zones with include, union, intersection, and difference.
Zones are sets of internal source identities—not map areas, MMSI filters, vessel filters, or payload rules.
Routing mode scopes deduplication to each logical target, so one delivery path does not suppress another.
source_id is an internal routing identity. It is separate from the NMEA TAG s value emitted downstream.
Runtime routing control
An optional local Unix-domain control plane exposes concise routing operations through the globally installed aismixerctl command.
See the active generation, enabled state, logical zones, ordered routes, and target identities.
Validate a complete candidate before swapping the immutable routing snapshot, or return to legacy broadcast mode.
Optional generation checks prevent an operator from overwriting a newer routing change unintentionally.
Deliberately local and process-scoped. Runtime routes are not persisted after restart, configuration files are not rewritten, and ingress or egress adapters are not created dynamically. Unix socket permissions are the current authorization boundary; there is no application-level control token.
Network endpoint controls
Small application-level controls make IPv4 and IPv6 endpoints easier to place inside an operator’s wider firewall and routing policy.
Restrict UDP and UDPSEC listeners to literal IP addresses or CIDR networks, with explicit deny-all behavior available.
Bind an egress socket to a literal IPv4 or IPv6 source address and constrain destination resolution to the same family.
These policies complement operating-system firewall and routing rules; they do not replace them.
source_ip is source-address binding—not interface selection, routing-table selection, socket marking, or SDN.
UDPSEC lifecycle
The current nmea_sproxy and AISMixer UDPSEC path combines authenticated key agreement and encrypted traffic with explicit replay, session, and nonce ownership.
Stations authenticate with ECDSA. ECDH-derived session keys let the current proxy protect AIS and ping/pong traffic with authenticated AES-GCM encryption.
Handshake replay records, active sessions, and each session’s accepted data nonces are hard-bounded. Monotonic time governs local TTL and ordering, and expiry precedes live-state eviction.
Allowed traffic drives expiry cleanup rather than a background timer. Replay, session, and nonce state is process-local, non-durable, and lost when the service restarts.
Security boundary: UDPSEC protects transport authenticity, confidentiality, and replay handling within its implemented scope; it does not prove that an AIS payload is physically or semantically truthful.
Read the security policy, behavioural contract, nmea_sproxy guide, and Wiki for the detailed boundaries.
nmea_sproxy and physical AIS receivers
nmea_sproxy is the station-side proxy that connects one local AIS source to one configured AISMixer destination.
One local input maps to one configured output. nmea_sproxy does not mix, fan out, route, deduplicate, assemble multipart AIS, or rewrite TAG metadata.
A networked receiver can feed local UDP into nmea_sproxy or send UDP directly to AISMixer, depending on the deployment. The main AISMixer process does not read serial ports directly. Trusted plain UDP does not provide UDPSEC encryption, authentication, replay protection, or liveness.
Deployment and operations
The repository-managed deployment keeps runtime files, operator state, systemd integration, and local routing control aligned.
The service provisions /run/aismixer while AISMixer is running and supports the optional local control socket.
Privilege-aware lifecycle scripts handle root or sudo operation and maintain the deployed runtime and service units.
Normal install, update, and uninstall flows preserve operator configuration and cryptographic keys.
The installed workflow places aismixerctl in /usr/local/bin for local routing operations.
Documentation and project status
The Wiki is the detailed guide to architecture, configuration, routing, security, and operations. The Roadmap tracks future work without promising dates.