Skip to content

6. Runtime view

Last reviewed: 2026-11-03

How the blocks work together in the important scenarios. Each scenario states the flow and links to the spec that holds the details.

The consent question comes first. Without Yes the watch sends nothing, so none of the steps below start (privacy § 1).

sequenceDiagram
    actor U as Skipper
    participant W as Watch app
    participant S as Server
    participant B as Browser (/pair)
    U->>W: opens Pair device
    W->>U: "Send your trips to sailever.com?"
    U->>W: Yes (stored on the watch only)
    W->>S: POST /api/v1/devices/pairing_requests
    S-->>W: 201 six-digit code + poll token, valid 10 min
    W->>U: shows the code
    loop every 5 s
        W->>S: GET pairing_requests/poll
        S-->>W: 202 pending
    end
    U->>B: enters the code (signed in)
    B->>S: POST /pair
    S->>S: device linked to the user, code consumed, old token cleared
    W->>S: GET pairing_requests/poll
    S-->>W: 200 paired + device token (delivered once)
    W->>W: stores the token

Details: device-api, sync-and-pairing, ADR 003.

2. From a recording to a trip

sequenceDiagram
    participant W as Watch app
    participant A as API controller
    participant Q as Solid Queue
    participant I as TrackSegmentIngestJob
    participant R as TrackRecomputeJob
    participant D as DB and track file
    participant B as Browser
    W->>W: segment closes, written to the local queue
    W->>A: POST /api/v1/tracks/segments (Bearer token)
    A->>A: check envelope, header, CRC
    A->>D: store raw segment
    A-->>W: 201 (tides for a paired watch)
    W->>W: delete the file from the queue
    A->>Q: queue ingest job
    Q->>I: run (one per track at a time)
    I->>D: decode, replace this segment's points on the 5 s grid, find or create track and trip
    I->>Q: queue recompute
    Q->>R: run
    R->>D: metrics, events, map line, names, trip aggregates
    R-->>B: Turbo refresh of open trip pages

Details: track-sync, ingestion. Reprocessing is safe: a rerun replaces exactly its own points.

3. When something goes wrong with an upload

flowchart TB
    up["Segment upload"] --> chk{"Server: header and CRC ok?"}
    chk -->|"no"| qz["202, segment quarantined"]
    chk -->|"yes"| st["Stored, 201, job queued"]
    st --> dec{"Job: decodes? no FIT collision?"}
    dec -->|"no"| qz
    dec -->|"yes"| ok["Points stored, track recomputed"]
    qz --> adm["Admin: Quarantined segments<br/>payload kept, Reprocess"]
    adm --> st
    st -.->|"job lost or failed: sweep every 15 min"| st
    up --> net{"Watch: 2xx?"}
    net -->|"yes"| del["File deleted from the queue"]
    net -->|"other, or offline"| keep["File stays queued until the next save point"]
    net -->|"401"| tok["Token deleted, files kept"]

Details: ingestion § Quarantine, track-sync § Upload rules.

4. Sharing a trip and opening it

sequenceDiagram
    actor O as Owner or skipper
    actor V as Visitor
    participant S as Server
    O->>S: opens Share (click)
    S->>S: creates a public link, token of 22 characters, 30 days
    O-->>V: sends /t/token
    V->>S: GET /t/token
    alt link works
        S-->>V: 200 trip page, no ids, noindex
        V->>S: opens the map: /t/token/replay
        S-->>V: replay page, then the points endpoint by token
    else ended or expired
        S-->>V: 410 "This link has expired"
    end

Details: sharing, trip-replay, ADR 010, ADR 016.

5. Background jobs and the nightly backup

flowchart LR
    subgraph web["App VM"]
        fb["Fallback worker<br/>realtime only"]
    end
    subgraph worker["Worker VM"]
        rt["realtime<br/>ingest, recompute,<br/>FIT import, erasure"]
        df["default<br/>wind, tides, sweep"]
        bg["background<br/>OSM places, backup,<br/>cleanups"]
        rec["Recurring schedule<br/>jobs role only"]
        tb["Tile and depth builds<br/>128 CPU shares"]
    end
    rec -->|"every 15 min"| df
    rec -->|"nightly 03:00 UTC"| bg
    bg -->|"age-encrypted dump"| bk[("sailever-backups")]
    fb -.->|"takes over realtime while the worker VM is down"| rt
    rt --- df --- bg

Priority: realtime before default before background; the tile builds always yield to the Rails jobs (DEPLOYMENT § Jobs, ADR 011). If the worker VM is down for long, only the realtime queue moves; the others catch up afterwards.