5. Building block view¶
Last reviewed: 2026-11-03
The static structure: what the parts are and how they are cut.
Level 1: the products¶
flowchart LR
subgraph watch["On the wrist"]
garmin["WatchApp Garmin<br/>Monkey C"]
wear["WatchApp Wear OS<br/>Kotlin"]
field["DataField Garmin<br/>Monkey C"]
end
subgraph srv["Server (Rails)"]
web["Website, logbook, admin"]
api["Device API"]
jobs["Background jobs"]
end
data[("PostgreSQL + PostGIS<br/>Object Storage")]
tiles["Tiles<br/>Cloudflare R2 + Worker"]
sim["Track simulator<br/>(test tool)"]
garmin -->|"pairing, SLTB"| api
wear -->|"pairing, SLTB"| api
web --> data
api --> data
jobs --> data
web -->|"map tiles"| tiles
sim -.->|"test tracks"| api
| Building block | Code | Responsibility | Behaviour |
|---|---|---|---|
| WatchApp, Garmin | apps/garmin-watchapp | Record, show, sync | specs/watchapp |
| WatchApp, Wear OS | apps/android | Same, in Kotlin | same spec, mapping |
| DataField, Garmin | apps/garmin-datafield | Sailing values in Garmin's own activities | specs/datafield |
| Server | apps/server | Website, logbook, watch API, admin, jobs | specs/server |
| Track simulator | tools/tracksim | Test tracks for every watch app and the server | README |
| Infrastructure | infra | OpenTofu, tile and depth builds, worker | infra map |
Shared contracts used by several blocks: specs/shared (SLTB track sync, device API, vessel states, calculations).
Interfaces between the blocks¶
| Between | Interface | Spec |
|---|---|---|
| Watch app and server | POST /api/v1/devices/* (register, pair, poll, unpair) and POST /api/v1/tracks/segments (SLTB), Bearer device token |
device-api, track-sync |
| Browser and server | Server-rendered pages under /, /my, /admin, /t/<token>, /crew/<token>; JSON for maps and replay |
specs/server |
| Server and database | PostgreSQL with TLS, four databases (main, cache, queue, cable) | DEPLOYMENT § Postgres |
| Server and files | Active Storage on the private bucket sailever-files, uploaded through the app only |
specs/server |
| Web and jobs | Solid Queue in the queue database; jobs in the realtime, default and background queues |
ADR 011 |
| Browser and tiles | tiles.sailever.com vector tiles |
maps |
Level 2: the server¶
flowchart TB
subgraph appvm["App VM, Kamal role web"]
proxy["kamal-proxy<br/>Let's Encrypt"]
puma["Rails: Puma + Thruster"]
ctl["Controllers<br/>public site, /my logbook,<br/>/api/v1 device API, /admin"]
fb["Fallback worker<br/>realtime queue only"]
proxy --> puma --> ctl
puma --- fb
end
subgraph workervm["Worker VM, Kamal role jobs"]
sq["Solid Queue<br/>realtime, default, background"]
rec["Recurring schedule"]
builds["Tile and depth builds<br/>lowest priority"]
sq --- rec
end
subgraph dbvm["DB VM"]
pg[("PostgreSQL 16 + PostGIS<br/>main, cache, queue, cable")]
end
subgraph oci["Oracle Object Storage"]
files[("sailever-files")]
bk[("sailever-backups<br/>write-only")]
end
ctl --> pg
ctl --> files
sq --> pg
sq --> files
sq -->|"nightly, encrypted"| bk
builds --> tiles["R2 tiles"]
| Part | Responsibility | Where |
|---|---|---|
| Controllers and views | Website, logbook, trip pages, sharing, track editor, admin, device API | specs/server |
| Models and data | Users, devices, trips, tracks, track files, segments, trip days | data-model |
| Ingestion services | SLTB, FIT and GPX decoding, derived values, trip aggregates | ingestion |
| Jobs | Track processing, wind and tides, sweeps, backups, cleanups, OSM places | DEPLOYMENT § Jobs |
| Map services | Tiles, depth, places, island names | maps |
Level 2: the watch app¶
Both ports have the same parts. The consent gate sits in front of every request.
flowchart LR
gps["GPS, barometer"] --> rec["Recording<br/>sampling, vessel state"]
rec --> enc["SLTB encoder"]
enc --> q[("Local queue")]
q --> up["Upload client"]
gate{{"Consent gate"}}
up --> gate
pair["Pairing"] --> gate
gate -->|"after Yes on the watch"| net(["sailever.com"])
rec --> views["Views<br/>dashboard, stats, track, menus"]
env["Environment<br/>tides, pressure, alerts"] --> views
net -.->|"tides in the response"| env
| Part | Spec |
|---|---|
| Recording, state periods, day totals | recording, vessel-state |
| SLTB encoder, queue, upload rules | track-sync |
| Consent, pairing, unpair | sync-and-pairing |
| Views and menus | dashboard, stats, track-view, menus |
| Environment and alerts | environment, alerts-and-auto-stats |