★ New · feat/feeds + feat/stream

Subscribe to live feeds. Stream to ArcGIS.

Two complementary capabilities. Feed subscriptions poll OGC SensorThings, SOS, GTFS-realtime, and Moving Features sources into a dedicated DuckDB for SQL analytics on the map. The Esri-conformant StreamServer exposes that same moving data — plus Meshtastic, TAK, and GNSS tracks — to ArcGIS Pro and the JS SDK as a native WebSocket StreamLayer.

OGC SensorThingsSOS / O&M 2.0GTFS-static & -rt Moving FeaturesEsri StreamServerWebSocketDuckDB history
Feed subscriptions

Five feed kinds, one DuckDB

Each feed type has its own adapter and writes rows into a dedicated feeds.duckdb registered under dbId = "feeds" — so feed tables flow through the existing OGC API Features / MVT / FeatureServer facades with no facade changes.

  • SensorThings — REST/JSON, Things → Datastreams → Observations.
  • SOS — SOAP/XML GetObservation, O&M 2.0.
  • GTFS-static + GTFS-realtime — zip → read_csv_auto with ST_MakeLine shapes; protobuf FeedMessage for live vehicle positions.
  • Moving Features — MF-JSON / MF-CSV → points + derived LINESTRING trajectory view.
  • Background polling with per-feed PeriodicTimer, retention trimming, and DPAPI-encrypted feeds.json profiles.
POST /api/feeds
// subscribe to a SensorThings endpoint
{
  "kind": "SensorThings",
  "url": "https://example/frost/v1.0",
  "intervalSeconds": 30,
  "retentionHours": 168
}

// rows land in feeds.sensorthings_observations
// → served at /api/spatial/feeds/collections/…
● polling · Polly-instrumented
Esri-conformant StreamServer

A real ArcGIS StreamLayer source

Raw ASP.NET Core WebSockets — not SignalR — that speak the ArcGIS SDK's own JSON protocol. Paste the catalog URL into ArcGIS Pro or a JS SDK StreamLayer and it just connects.

🔌

No negotiate handshake

Speaks the Esri JSON wire contract directly — camelCase via [JsonPropertyName], polymorphic messageType frames.

📡

Per-subscriber filters

Subscribers push {messageType:"filter"} to update their where + geometry slice of the stream.

🗄️

DuckDB-backed history

Parameterized-safe writes on a background thread so the hot path never blocks; historical query via /query.

🔄

Bidirectional ingest

Accepts either Esri {attributes,geometry} or GeoJSON FeatureCollection on /ingest.

💓

15s keep-alive

RFC 6455 ping frames keep subscribers alive through proxies and idle networks.

📊

Layer registry

Persisted to stream_layers.json with auto field discovery from the DuckDB table.

The API at a glance

Two endpoint families

FeatureEndpointNotes
FeedsGET / POST /api/feedsList / create a subscription
FeedsPOST /api/feeds/{id}/pause · /resume · /pollLifecycle control
FeedsGET /api/spatial/feeds/collections/{c}/itemsOGC API Features over feed tables
StreamGET /api/stream/{layerId}/StreamServerCatalog JSON with the ArcGIS StreamLayer URL
StreamGET /api/stream/{layerId}/subscribeThe WebSocket the SDK opens
StreamPOST /api/stream/{layerId}/ingestExternal push (Esri or GeoJSON)
StreamGET /api/stream/{layerId}/query?startMs=&endMs=Historical playback

Sources: FeedRoutes.cs / FeedPollerService.cs (commit 5401b500f) and StreamServerRoutes.cs / StreamServerBroker.cs (commit 2570a8db5). GTFS-realtime uses the canonical Google proto (Protos/gtfs-realtime.proto).

Live data in. ArcGIS StreamLayer out.

From IoT sensors and transit feeds to Meshtastic and TAK tracks — subscribe, store, and stream into the Esri clients you already run.