This document describes how to consume AIS data from the AIS Relay Server. The relay provides authenticated access to raw AIS NMEA messages and database snapshots. All external AIS feeds are secured using TLS and exposed locally via a TCP relay.
The upstream AIS data source requires a TLS-encrypted connection. To simplify client access, stunnel is used to terminate TLS locally and expose the AIS stream as a plain TCP socket.
Once stunnel is running, AIS data will be available locally on:
127.0.0.1:5000
client = yes
foreground = yes
debug = info
[secure-service-client]
accept = 127.0.0.1:5000
connect = ais-data-relay.streams.sunet.se:5000
checkHost = ais-data-relay.streams.sunet.se
cert = ./client.crt
key = ./client.key
CAfile = ./ca.crt
verifyChain = yes
verifyPeer = no
Required files in the stunnel directory:
client.crt – client certificateclient.key – private key (must be kept secure)ca.crt – trusted CA certificateStart the stunnel client using:
sudo stunnel client-stunnel.conf
All applications should connect only to the local TCP port. They do not need to handle TLS directly.
The TCP relay provides raw AIS NMEA sentences over a plain TCP connection. The relay maintains a single connection to the upstream AIS source while allowing multiple authenticated clients to connect simultaneously.
import asyncio
async def tcp_client(USERNAME="admin", PASSWORD="1234"):
reader, writer = await asyncio.open_connection("localhost", 5000)
prompt = await reader.readline()
print(prompt.decode().strip())
writer.write((USERNAME + "\n").encode())
await writer.drain()
prompt = await reader.readline()
print(prompt.decode().strip())
writer.write((PASSWORD + "\n").encode())
await writer.drain()
result = await reader.readline()
print(result.decode().strip())
try:
while True:
line = await reader.readline()
if not line:
break
print(line.decode().strip())
except KeyboardInterrupt:
writer.close()
await writer.wait_closed()
asyncio.run(tcp_client())
The relay maintains a local SQLite database containing the latest AIS state. A snapshot of this database can be downloaded via an authenticated HTTP endpoint.
import requests
from requests.auth import HTTPBasicAuth
url = "http://localhost:8000/db/snapshot"
response = requests.get(url, auth=HTTPBasicAuth("admin", "1234"))
with open("ais_snapshot.db", "wb") as f:
f.write(response.content)
curl -u admin:1234 http://localhost:8000/db/snapshot -o ais_snapshot.db
Authentication Notes:
• TCP clients authenticate using a username/password prompt.
• HTTP and WebSocket clients must use HTTP Basic Authentication headers.