Skip to content

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