A proxy in front of TREK (Cloudflare, nginx, Caddy, Traefik, HAProxy) can let in only devices that show a client certificate. The apps could not, so such a server was unusable from them. - Address screen: the server check runs natively and notices when the server asks for a certificate. Instead of "no server answered" it says so and offers to add a .p12/.pfx file; a wrong password asks again, an expired, not yet valid or keyless file is named as such. - The certificate is shown only to the host and port it was added for. iOS keeps it in the keychain (this device only), Android seals the key with an Android Keystore key. The file's password is never stored. - The web view answers the server's certificate request on both platforms; WebSocket, service worker and API requests go through with it. The offline copy, watch requests and the check use it too. - When the server stops letting the app in (certificate removed, expired or no longer trusted) while the page still comes from the service worker or the offline copy, a native check sends the app to the address screen, which offers to add another. A server that only asks and lets everyone in is left alone. - Cloudflare-style 403 and nginx-style 400 refusals without a certificate lead to the same screen instead of staying up as an error page. - Settings, Account: a Client certificate card (only with one stored) shows subject, issuer, server and expiry, with Replace and Remove. - Wiki: Native-Apps "Client certificates (mTLS)" with Cloudflare, nginx, Caddy and Traefik setup and how to make a certificate for the app. - The app icon on the unreachable screen is black on the light surface.
@trek/shared
Single source of truth for TREK's API contracts, expressed as Zod schemas and consumed by both the server (request validation + inferred DTO types) and the client (typed requests/responses).
This package is part of the incremental NestJS + React 19 migration (see the "Brownfield Rewrite" board). It is intentionally dormant until modules start importing it — adding it changes nothing for users.
Rules
- One folder per domain:
src/<domain>/<domain>.schema.ts(+.spec.ts). - Domain-agnostic building blocks live in
src/common/. - A route is only considered migrated once its contract lives here.
- Schemas are the source of truth; server DTOs and client types are inferred from them
(
z.infer<typeof schema>), never hand-duplicated.
Consumption (dev)
Both apps resolve @trek/shared to this package's TypeScript source:
- Server (
tsx): viapathsinserver/tsconfig.json. - Client (
vite): viaresolve.aliasinclient/vite.config.ts(+pathsfor the type-checker).
Production packaging (Docker / workspace wiring) is introduced in card F2, when the server first depends on this package at runtime. Until then prod builds are untouched.
Not yet here
The canonical error envelope is finalised in card F5 (it must match TREK's current Express error responses byte-for-byte), so it is deliberately not invented in F1.