Skip to main content

Deployment

The current official deployment separates the static browser app from several narrow, independently deployed VPS services. Vercel still builds and serves the app, compatibility redirects, diagnostics, and bounded transition adapters, but current official clients do not send wearable/CAMS traffic or new profile shares through the static app host. Cross-device Sync and Agent Access use the dedicated relay stack.

Production build

vercel.json enables Git deployment only for main and runs:
The catalog fetch runs first. The Rolldown build then rewrites the native-module development entry to a content-hashed startup bundle while retaining feature-level lazy chunks. vercel.json gives hashed js/bundle-*.js files immutable cache headers and applies the CSP and security headers before the legacy documentation redirects and /app rewrite. Production catalog fetch variables are build-time secrets, not browser runtime configuration: With no URL, the script accepts an existing readable catalog or copies the public example stub. When a URL is present but a preview build has no token, it also uses the stub. A configured production fetch that returns invalid JSON or the wrong catalog shape fails the build rather than silently shipping it.

Official service topology

js/proxy-runtime.js routes official hosts to the integrations service and independent self-hosts to same-origin /api/proxy. js/profile-share.js routes current vps1_... links from official hosts directly to the share service while older IDs and self-hosted links remain same-origin.

Sync relay stack

The operated sync stack is deployed from immutable getbased-relay release tags. Relay v2.0.0 uses @evolu/common 8.9.0, @evolu/nodejs 3.2.0, and a Docker image pinned to Node.js 24.20.0. Evolu source is installed from the locked npm dependencies; it is not vendored into the relay. The browser app and relay use independent adapter packages and version streams. The app currently uses @evolu/common 8.7.0 with @evolu/web 3.1.0, while the relay uses the versions above. They share the Evolu 8 protocol generation; matching package numbers are neither expected nor required. The app’s Sync compatibility workflow pins the exact relay release commit and exercises both the Evolu 8 default client and the temporary Evolu 7 rollback client against a disposable real relay. The public TLS origin routes three surfaces while keeping private administration off the network: The reverse proxy must overwrite the gateway’s dedicated client-IP header from the real connection peer. It must not trust a caller-supplied value. The relay database and context store use separate persistent volumes, and the gateway mounts only the verifier socket—not the relay database. Before a relay major upgrade, record the current release and image, stop the stack, and create verified backups of both data volumes and the deployment configuration. Keep the previous images available. Rollback may require restoring the matching data backup rather than changing only the image.

Vercel routes that remain

User documentation is not built from the application repository. It lives in the separate Mintlify project.

Compatibility proxy service

The standalone entry point is server/compat-proxy-server.js; deployment assets live in deploy/compat-proxy/. The container listens on its internal port and should be published only on host loopback behind a TLS reverse proxy that exposes the exact /api/proxy path. The operated policy accepts only classified operations:
  • official Oura, Withings, Polar, and existing legacy Fitbit OAuth/API requests;
  • the exact NVIDIA NRAS GPU-attestation operation;
  • CAMS atmosphere requests with coordinates forced to a 0.1° grid;
  • bounded credential-free public-page fetches used by reviewed application features.
It is not a hosted Custom API, chat, voice, WHOOP, Ultrahuman, or Google Health proxy. Generic authenticated AI requests fail closed. Request bodies, wearable credentials, health responses, and OAuth payloads are held only long enough to forward the request and are not intentionally written or logged.

Standalone compatibility variables

Never expose the container port directly, add request-body logging, or broaden an operated allowlist without documenting the target, data visible to the service, origin policy, limits, and tests.

Profile-share service

The current official store is server/profile-share-server.js with a SQLite adapter in lib/profile-share-sqlite-store.js; deployment assets live in deploy/profile-share/. The browser compresses and encrypts a single-profile export before upload. The service accepts at most a 3.75 MB envelope and a 30-day expiry, while the reverse proxy caps the HTTP body at 4 MB. Cleanup runs at service start, hourly, after creation, and when an expired record is read. The database ceiling defaults to 512 MiB. The operated policy intentionally keeps no retained database backup, so service loss can invalidate outstanding links without affecting source profiles.

Standalone share variables

The service must run in its own container and writable volume, separate from the compatibility proxy, Evolu relay, and UV service. Do not log request bodies, query strings containing share IDs, or management material.

Legacy Blob transition

Existing Vercel Blob envelopes are not copied. api/share.js implements a bounded transition configured with: Before cutover, the adapter retains legacy behavior. During the window, it redirects new writes, serves live legacy reads/deletes from Blob, and redirects legacy misses. After the deadline, all supported operations redirect and Blob credentials can be removed. Do not turn the transition into an indefinite fallback.

Self-hosted /api/proxy

An independent deployment keeps the same-origin endpoint and its own provider credentials. Relevant variables are: Client secrets never enter runtime config. The browser receives only public client IDs and capability booleans. A missing or incomplete fail-closed provider configuration must block connect, exchange, refresh, sync, and backfill while leaving disconnect available. For CAMS, set UVDATA_UPSTREAM explicitly on an independent self-host or choose browser-direct Open-Meteo. UVDATA_BEARER is required only when that selected upstream expects it.

Local development

The production build also emits getbased-companion.mjs for the CLI-provider bootstrap. Serve that artifact alongside the app, but run Companion on each browser user’s machine, not on the public web server. Hosted discovery grants chat-only credentials; the current service deliberately reserves lifecycle controls for installation-authorized local connections. Custom page origins require an explicit Companion allowlist. See CLI agent build and security. node dev-server.js serves source modules and mirrors same-origin APIs: Use .env.local for untracked local secrets. Environment variables already present in the process take precedence. The local-only /api/deploy-catalog workflow has a separate set of maintainer variables. They are not browser configuration and are unrelated to the production build’s CATALOG_FETCH_* inputs: Missing configuration skips the corresponding operation. The development endpoint validates the deploy-hook URL shape and never exposes these values to the browser.

Content Security Policy

The current hosted policy allows self scripts, Wasm evaluation, blob workers, the approved analytics origin, jsDelivr for explicitly retained runtime needs, HTTPS/WebSocket connections, and loopback Local AI connections. It denies frames, objects, and foreign base URLs. The exact source of truth is vercel.json. When adding a provider or browser runtime, review the CSP, direct-CORS requirements, service-worker network-only list, proxy policy, and public privacy disclosure together. CSP permission does not authorize use of the compatibility relay.

Service worker

service-worker.js imports version.js and builds the cache name as labcharts-v${APP_VERSION}. Production uses that semver name. Preview and local builds append the sanitized /api/commit cache key or SHA so two deployments with the same app version do not share a cache. The install step precaches the complete app shell with bounded concurrency and retries; installation fails if a required entry cannot be fetched. Routing then follows these rules: Activation deletes older labcharts-v* caches. Local development normally unregisters the worker; use /app?dev-sw=1 only for an intentional local offline check.

PWA manifest

The current manifest contract is:
The 192 px and 512 px PNG icons use relative paths and any maskable; the SVG is an any icon. Keep the manifest, service-worker /app fallback, installed-PWA smoke test, and landing-page routes aligned.

Change-scoped release checks

Before release, run the checks related to the changed boundary: production-build validation for build or asset changes, proxy policy/server tests for integrations, profile-share transition/service tests for sharing, and service-worker/manifest checks for PWA changes. GitHub Actions owns the exhaustive browser and combined-coverage matrix.