Configuration and disclosure
What is set where, and what this app does not do
This browser
Your anonymous session
Owner id
0e473ffa-5b4e-48be-99fd-7c69d078b0d2
HTTP-only cookie, not a login
Sessions stored
0
scoped to that cookie
Display
Units and formats
Swell height
metres
as reported by Open-Meteo
Tide
metres above MLLW
NOAA CO-OPS station datum
Wind
m/s
10 m forecast
Peel speed
km/h
c = √(g·depth), converted
Wave power
kW/m
P = ⅟₁₆ρgHs²Tp
Time
break local time
IANA zone per break
Units are fixed rather than configurable. A surf forecast with a units toggle is a forecast people misread on the way out of the door; every number here matches its source.
Open-source AI
The model that ships in this repository
Model
all-MiniLM-L6-v2
sentence embeddings
Licence
Apache-2.0
open weights
Quantisation
int8
20 MB on disk
The weights live at /models/swellread-embed in this repository and are loaded with allowRemoteModels = false, so no model host is contacted. It runs through ONNX Runtime in the tab: WebGPU where available, WASM otherwise. The first load also fetches the ~14 MB ONNX runtime from jsDelivr, after which the browser cache serves it and the model works with the network off.
- What it does: reads the one-line note you write about a session, labels the kind of conditions you are describing, and finds your own earlier sessions with a similar vector.
- What never happens: the note is not sent anywhere, there is no API key, and there is no per-request cost. If the model fails to load, the panel says so and the rest of the app is unaffected.
- How to swap it: change the id passed to
pipeline()and drop its files into/public/models. Nothing else assumes MiniLM.
Persistence
Where your rows actually live
Adapter
Hosted Postgres (Neon serverless driver)
Production store
hosted Postgres
DATABASE_URL is set
Production refuses to start without DATABASE_URL. Selecting the embedded store in production is treated as a configuration error rather than a convenience, so a deploy can never silently lose writes on a cold start. Locally the app needs no environment variables at all.
Security model
What is actually protected
- Ownership: every read, update and delete is scoped by the owner id taken from an unguessable HTTP-only cookie. There is no path that returns another rider's row, and that is covered by tests rather than by inspection.
- Input: every body and query parameter is parsed with a schema before it reaches the service layer. Strings are length-capped, enum values are closed sets, ids are pattern-checked, and every SQL statement is parameterised — there is no string interpolation of user data into a query anywhere in the repository.
- Anonymous write abuse: in-memory sliding windows cap session creation at 20/min, updates at 40/min, rides at 30/min and deletes at 20/min per client, and JSON-RPC at 120/min. On a serverless platform each isolate keeps its own map, so this raises the cost of a hammering loop but is not a hard limit. A deployment that cares should put a platform WAF or a hosted limiter in front of the write routes.
- Upstreams: only two fixed hosts are contacted, both over HTTPS, both with a 9 second timeout and one retry. Responses are normalised and re-validated in TypeScript before use, and no upstream payload is rendered as HTML.
- Errors: API failures return a stable
{ error: { code, message } }envelope. Stack traces, environment variables and driver messages are never sent to a client. - Secrets: there are none. The app has no API keys, no tokens and no service credentials; the only environment variable in production is the database connection string.
Not implemented
Things this app does not do
- No accounts, no email, no social login, no password reset.
- No push notifications or email. Nothing leaves the device except your own requests.
- No paid API keys and no third-party model provider, so no upstream can bill you or train on your notes.
- No multi-user sharing. A brief is shareable as a link, but sessions stay private to the browser that created them.
engine swellread-engine/2026.10.1