Architecture
This page is generated from the Open Location Hub source documentation and should not be edited in the website repository.
Layers
cmd/hub: process bootstrap and wiringinternal/config: environment-driven configurationinternal/httpapi: API surface and handlersinternal/ws: OMLOX WebSocket wrapper protocol, subscriptions, and fan-outinternal/storage/postgres: durable storeinternal/mqtt: MQTT topic mapping and broker integrationinternal/auth: token verification middlewareinternal/rpc: local-method dispatch, MQTT RPC bridging, announcements, and aggregationinternal/hub: shared CRUD, ingest, derived event generation, collision evaluation, and internal event bus emission
Metadata And Hot State
- Postgres is the durable source of truth for hub metadata, zones, fences, trackables, and location providers.
- The runtime resolves the singleton hub metadata row before the service starts so one stable
hub_idand label are available for startup validation, internal event provenance, and identify responses. - The hub loads those resources into an immutable in-memory metadata snapshot before it accepts traffic.
- Successful CRUD writes update Postgres first, then update the in-memory snapshot, invalidate any affected derived metadata such as zone transforms, and emit a
metadata_changesbus event. - A background reconcile loop reloads durable metadata periodically and emits the same
metadata_changesnotifications when it detects out-of-band create, update, or delete drift. - Decision-critical ingest state is kept in process memory:
- dedup windows
- latest provider-source locations
- latest trackable locations and WGS84 motions
- proximity hysteresis state
- fence membership state
- collision pair state
Event Fan-Out
- REST, MQTT, or WebSocket ingest enters the shared hub service.
- The hub validates, normalizes, deduplicates, and updates in-memory transient state on the ingest path.
- A buffered native-publication stage emits native location and motion events without blocking ingest on downstream fan-out.
- A second buffered decision stage is the insertion point for future filtered or smoothed track processing and currently drives alternate-CRS publication, geofence evaluation, and optional collision evaluation.
- MQTT and WebSocket consume the resulting internal event stream and publish transport-specific payloads in batches.
- When any non-critical queue fills, the hub drops newer work on that path rather than backpressuring raw ingest.
Implications:
- ingest logic is shared across REST, MQTT, and WebSocket
- MQTT is no longer the only downstream publication path
- the internal event seam decouples downstream publication from MQTT-specific topics
- location ingest latency is protected from slower transport fan-out, geofence work, or collision work
- the decision-stage queue is the intended insertion point for future filtered or smoothed track processing before fence/collision decisions
- WebSocket fan-out coalesces multiple internal events into fewer wrapper messages and drops outbound payloads for slow subscribers instead of tearing the connection down immediately
- hub-issued UUIDs for REST-managed resources, derived fence/collision events, and RPC caller IDs now use UUIDv7 so emitted identifiers are time-sortable
- internal hub events carry the persisted
origin_hub_idso downstream transports preserve source provenance
Observability Boundaries
internal/observabilityowns OpenTelemetry resource setup, OTLP exporters, lifecycle management, and the small internal instrumentation API used by the rest of the runtime.- OTLP export is collector-first: metrics, traces, and logs go to an OpenTelemetry collector such as SigNoz rather than to a hub-owned scrape endpoint.
- Transport handlers attach ingest transport context at entry so REST, MQTT, and WebSocket traffic create one shared root ingest span shape before entering
internal/hub. - The hot path records low-cardinality metrics for accepted, deduplicated, rejected, and failed ingest, queue depth and wait time, stage latency, event-bus fan-out, MQTT publish, WebSocket dispatch, RPC execution, and end-to-end processing time.
- Child spans isolate proximity resolution, native publication, decision processing, fence evaluation, collision evaluation, metadata reconcile, auth, and runtime dependency work so slow stages can be inspected directly.
- Asset-, provider-, zone-, and fence-centric identifiers belong on traces and structured logs only. They are intentionally excluded from normal metric labels so dashboards remain queryable under sustained ingest volume.
- Zap remains the application logging API. When OTLP logs are enabled, the logger tees into the OpenTelemetry bridge so the same structured events still appear locally while also being exported to the collector.
- Runtime gauges for queue occupancy, event-bus subscribers, and WebSocket connections are exposed from
internal/hubthrough observable instruments so the e2e stack can dashboard degraded states without adding lock-heavy bookkeeping to the ingest path.
RPC Control Plane
- A client calls
GET /v2/rpc/availableorPUT /v2/rpcover HTTP. - REST auth verifies the bearer token and route-level access.
- The RPC bridge applies method-level authorization for discovery or invocation.
- The bridge looks up the method in a unified registry containing:
- hub-owned local methods
- MQTT-discovered external handlers
- The bridge either:
- handles the method locally
- forwards it to MQTT
- or does both and aggregates responses
- The bridge returns a JSON-RPC result or JSON-RPC error payload to the HTTP caller.
Built-in identify behavior:
com.omlox.identifyreturns the persisted hub label as itsnamecom.omlox.identifyalso returns the stable persistedhub_id
Trust boundaries:
- HTTP clients should talk to the hub, not directly to MQTT devices
- MQTT should be restricted to the hub and trusted device/adaptor components
- the hub is the policy, audit, and handler-selection boundary for control-plane actions
Proximity Resolution Path
- A REST, WebSocket, or MQTT
Proximityupdate enters the shared hub service. - The hub resolves the referenced zone by
zone.idorzone.foreign_id. - Only proximity-oriented zones are accepted for this path (
rfidandibeacon). - The hub loads transient per-provider proximity state from the in-memory processing state.
- The resolver applies hub defaults plus any
Zone.properties.proximity_resolutionoverrides. - Hysteresis rules decide whether to stay in the current zone or switch to the new candidate zone.
- The hub emits a derived local
Locationusing the resolved zone position and then continues through the normal location pipeline.
Resolver notes:
- durable configuration lives in Postgres as part of the zone resource
- transient proximity membership state lives in the in-memory processing state
- derived location metadata includes hub extension fields such as
resolution_method,resolved_zone_id, andsticky
Resolver scope:
- the resolver emits the configured zone position as the derived point
- proximity resolution supports static proximity zones
- resolution policy is driven by hub defaults and zone-specific overrides
Contract-first flow
- Update OpenAPI spec.
- Regenerate generated server/types.
- Implement handler behavior.
- Validate with tests and check pipeline.
WebSocket Notes
GET /v2/ws/socketis implemented outside the REST OpenAPI contract because it is a protocol companion surface rather than a generated REST endpoint.- When auth is enabled, WebSocket messages authenticate with
params.tokenand apply dedicated topic publish/subscribe authorization. collision_eventsis a known topic but remains configuration-gated byCOLLISIONS_ENABLED.metadata_changesis a subscribe-only topic that carries lightweight metadata replication notifications shaped as{id,type,operation,timestamp}.