Skip to main content

Deployment

getbased is deployed on Vercel. The browser app is shipped as static files, and Vercel also runs small same-origin API routes from api/ for features that need hosted infrastructure. Vercel serves the app files directly, executes those API routes, and injects security headers.

Vercel configuration

vercel.json uses a deploy-time catalog fetch plus the legacy routes array (not rewrites or headers):
User documentation is not built from this repository anymore. It lives in the separate Mintlify docs project at docs.getbased.health; this app keeps only compatibility redirects for old app.getbased.health/docs/* links.

API routes

Vercel functions live in api/. They must stay same-origin, minimal, and avoid handling plaintext health data unless a feature explicitly requires it.

/api/share

api/share.js stores password-protected profile share links. The browser builds a single-profile export, encrypts it locally with AES-GCM using a key derived from the user-provided password, and uploads only the encrypted envelope. The password is never sent to the route and is not embedded in the link. Production requires a Vercel Blob write token configured as a sensitive environment variable. The route uses a private Blob store namespace, caps payload size, rejects shares longer than 30 days, rejects weak PBKDF2 iteration counts, rate-limits anonymous share creation, and returns no-store JSON responses. The route never has the password or plaintext profile JSON. Local development mirrors the endpoint in dev-server.js with an in-memory store so the modal and deep-link flow can be smoke-tested without Blob credentials. That local store is process-local and disappears when the dev server restarts.

/api/proxy

api/proxy.js is the controlled same-origin proxy boundary. It exists for browser flows that cannot safely call a third-party endpoint directly from app.getbased.health. Current responsibilities:
  • wearable runtime configuration and OAuth helper calls where client IDs/secrets must not be hardcoded into docs;
  • CAMS/light-data relay paths used by Sun & Light calculations;
  • Custom API proxying with public-host and allowlist checks so it cannot become a general SSRF tunnel;
  • CORS and method handling for the app’s own allowed origins.
Do not add a new proxy mode without documenting the target host, the caller allowlist, what secrets it can see, and which tests cover the branch.

/api/commit

api/commit.js returns the deployed app identity used by smoke checks, diagnostics, and “which build am I looking at?” debugging. Keep it cheap, no-store, and free of private deployment-provider details.

Domain layout

The app and landing page are deployed as two separate Vercel projects on the same domain: DNS points the root, app, and www hostnames at the Vercel projects. Keep provider-specific DNS account details out of public docs. The landing page is self-contained (all CSS/JS inline) and depends only on three icon files. CTA links point to https://app.getbased.health. A small inline script rewrites these to /app on localhost for local development.

Local dev server

node dev-server.js mirrors the production layout. If the site repo is cloned as a sibling (../get-based-site), the server routes: Without the sibling repo, / serves the app directly. Override the site path with SITE_DIR=/path/to/site node dev-server.js.

CSP headers

The Content-Security-Policy allows what the current app needs:
'unsafe-inline' is required for scripts because index.html has inline bootstrapping and legacy window-exported action hooks. 'wasm-unsafe-eval' and blob: are needed for in-browser model/vector/worker paths such as local Knowledge Base and private transports. Most JS libraries are bundled locally in vendor/, but the CSP currently permits https://cdn.jsdelivr.net for explicitly allowed browser-runtime dependencies. Run ./update-vendor.sh when bumping vendored assets, and update vercel.json whenever a provider or runtime dependency needs a new host. localhost:* and 127.0.0.1:* in connect-src allow plain HTTP Local AI servers on the same machine. The general https: source permits HTTPS LAN and remote compatible endpoints. A plain HTTP LAN endpoint such as http://192.168.x.x is blocked from the hosted HTTPS app by browser mixed-content rules; this is separate from CORS and cannot be relaxed by changing the CSP alone. An encrypted tunnel or HTTPS reverse proxy is required. If a new AI provider, wearable provider, private transport, worker runtime, or relay host is added, review connect-src, script-src, service-worker bypass rules, and api/proxy.js together. vercel.json also sets Onion-Location for Tor discovery and Link service descriptors for /.well-known/mcp.json and /.well-known/agent-skills/index.json.

Service worker

The service worker (service-worker.js) manages PWA caching. The cache name includes a version number:
When to bump the version: Any time you change an app file — JS, CSS, HTML, manifest, images. Incrementing the version busts the cache for existing users, who will download fresh files on next visit. The service worker uses three caching strategies: The API bypass is critical for streaming. If the service worker intercepts a streaming SSE response, the IPC pipe between the SW and the page buffers the chunks, breaking the streaming experience. The bypass (returning without calling event.respondWith) routes requests directly to the network. Local/private hosts are bypassed only when they are cross-origin, so same-origin app-shell requests can still be cached. Normal localhost development unregisters the SW to avoid stale module caches; use /app?dev-sw=1 for an explicit local offline smoke test.

PWA manifest

manifest.json makes the app installable as a native app on desktop and mobile:

Vendor dependencies

Chart.js, pdf.js, and Google Fonts are bundled locally in vendor/. To update:
  1. Edit the version pins at the top of update-vendor.sh
  2. Run ./update-vendor.sh
  3. Bump version.js to bust the SW cache
  4. Commit the updated vendor/ directory