Working offline
Coming soon: offline buffering. The desktop is scaffolded for offline-first time tracking, but the wiring isn't done, today every Start and Stop hits the API directly, and the desktop is effectively online-only.
This article describes what's actually in the app today vs. what's planned, so you know what to expect.
What's there today
When the desktop starts, it initialises a small SQLite database at ~/Library/Application Support/gboss-tab/tab-buffer.sqlite with a single pending_ops table. The schema and helper functions exist in storage.rs, enqueue, pending, drop_op, record_failure, all marked #[allow(dead_code)] // wired up from the React UI in a future polish pass.
A background thread in sync.rs fires every 30 seconds and emits a tab://sync-tick event into the React layer. The event currently has no listener that flushes anything, it's a heartbeat with no payload.
The only buffer-related command the UI actually invokes is cmd_buffer_count, which always returns 0 because nothing enqueues.
In practice that means:
- Click Start and the request goes straight to
/api/v1/timer/start. If the network is down, you get an error in the alert strip and the timer doesn't start. - Click Stop and the request goes straight to
/api/v1/timer/{id}/stop. Same, needs the network to be up. - The 30-second sync tick exists but doesn't flush anything, because no entries are queued to flush.
- The "Recent entries" list at the bottom of the desktop is pulled from the backend on each reload, not from a local cache. If the backend can't be reached, the list stays empty (or stale from the last successful load).
What's planned
Once the wiring lands (it's a Phase 1E polish item per the comments in storage.rs), the desktop will behave like this:
- Every Start / Stop writes a
pending_opsrow first, with a client-generated UUID. - The 30-second sync tick drains the queue to
/v1/timer/sync, which is already idempotent against the client-generated UUID (there's a partial unique index on(user_id, client_op_id)in the schema). - The backend dedupes by client_op_id, so retries on a flaky connection don't double up.
- A status strip will show "Synced a few seconds ago" / "Synced 14 minutes ago" so the user can tell when they've gone offline.
The DTO for sync ops (SyncOp) and the resource endpoint (POST /v1/timer/sync) already exist in the backend. The remaining work is the React side: enqueueing on Start/Stop, listening to tab://sync-tick, draining, handling failures.
In the meantime
Two practical implications today:
- The desktop is an online-only client. Network drops break starting and stopping timers, just like in the web app.
- Don't trust the "buffer" terminology if you see it in source / Rust logs, it's there for the future feature, not in use today.
If you regularly work in places with bad connectivity (hotel Wi-Fi, trains, client sites with locked-down networks), expect the same intermittent behaviour you'd get from the web app: requests fail until connectivity returns.
Why entries won't double up (when offline lands)
The dedup story is the most thought-through part of the design, so it's worth knowing: every queued op carries a UUID generated on the device, and the server's /v1/timer/sync resource has a partial unique index that rejects a second op with the same (user_id, client_op_id). Retries during a flaky connection will be ignored after the first acceptance.
That guarantee is real today at the API layer, it just isn't being exercised, because nothing enqueues.