
Goable is a multi-tenant B2B REST API that returns 0-100 suitability scores for outdoor activities across four families — water (kitesurf, surf, windsurf, sail, scuba), snow (ski-touring, freeride, snowboard), air (paragliding, hang-gliding), and land (trekking, trail-running, climbing) — based on real-time weather and multi-domain physics. The platform spans a Turborepo monorepo with four applications (apps/api, apps/admin, apps/website, apps/worker-lambda), 23 shared packages, and a Python ML calibration pipeline, unified under TypeScript Node 22 ESM with pnpm 10 workspaces.
apps/api — Hono 4.6, port 4000)@goable-io/physics, @goable-io/scoring, @goable-io/intelligence, @goable-io/decision-agent, @goable-io/counterfactual, @goable-io/drift-monitor) injected at the composition root.@goable-io/physics): 21 namespaces — wind, wave, tide, astronomy, bathymetry, atmosphere, thermal, comfort, swell, visibility, snow, lightning, precipitation, airQuality, marine, hydrology, alpine, coastal, time, surface. Pure math, no I/O, every module carries a citation header.src/routes/ (public score variants, intelligence, projections, observations, admin calibration/drift/verification/decision, billing); 33 use case files under src/usecases/. Routes must not contain business logic — validate → call one use case → errorResponse(). Shared utilities: lib/httpErrors.ts (errorResponse + ErrorCodes), lib/schemas.ts (GeoPointSchema, TimeWindowStringSchema), middleware/planGate.ts (requirePlanAtLeast, requireScope, requireAuth).src/compose.ts (46 K LOC). Drizzle + Postgres for 20+ entity types; Redis for session/rate-limit (daily window per plan: free=100, starter=1 k, pro=10 k, scale=∞); DynamoDB KV cache (optional peer dep); in-memory fallbacks for dev.lightning_proximity ≥ 0.85 or AQI category = hazardous → verdict=unsafe, regardless of profile.@hono/zod-openapi for schema generation; Scalar UI served at /docs. SDK generated from the same schema via openapi-typescript.infra/migrations/ (PostGIS geography(POINT, 4326), tstzrange custom types, Row-Level Security pseudonym tables). Drizzle bindings in @goable-io/tenants mirror the migrations but do not generate the schema.apps/admin — Next.js 16, port 4001)ops/ (platform staff — all tenants, calibration, drift, verification, audit, provider-skill, recommendation-runs) and portal/ (customer — settings, API keys, usage, outcomes, webhooks, billing), both gated by JWT HS256 cookies (goable_console_session, 8h TTL) via jose on the server layer. Console server authenticates to the API via X-Goable-Key header.src/features/: each folder exports via index.ts and isolates server/dal.ts (all API calls), server/keys.ts (TanStack Query keys), server/schemas.ts (Zod), and ui/ (React components). No cross-feature deep imports./api routes), nuqs 2 for URL-persistent filter/pagination state.@tanstack/react-form with type-safe field paths + Zod validators surfaced via shadcn FormMessage. Toasts via Sonner 2.apps/website — Next.js 16, port 4002)/catalog (activity profile browser), /score (interactive scoring demo), /explain, /decision, /recommend, /adaptation, /forecast-verification, /sustainability-index, /underwriting, /spc-charter, /provider-skill, /personalize, /spot-discovery, /docs (27 nested routes), /research, /science, plus legal /legal/[doc] dynamic routing./sealect (water sports vertical), /spc-charter (snow sports / Lloyd's syndicate SPC charter), /partner-with-us.@gsap/react 2.1 for hero parallax, tab pill, and transition effects (same pattern as the robot-site). Embla Carousel 8.6 for card carousels.react-markdown + remark-gfm for doc pages; @goable-io/sdk (workspace) used in interactive demos to call the live API.@goable-io/profiles-catalog (external repo goable-io/profiles-catalog, CC BY 4.0, v2.3.4 on npm): 18+ base activity profiles in YAML across four categories, with regional and spot variants. Adding an activity is a PR to that repo, not a code change in this monorepo. The Zod schema is byte-identical across both repos — packages/profiles/test/schemaSync.test.ts enforces the hash at CI time.@goable-io/sdk (v0.2.0, published to npm): zero-runtime-dep TypeScript client for all public endpoints — score, scoreSeries, scoreMulti, scoreHistorical, scorePortfolio, explainCounterfactual, decision, deleteUserData, explain, briefing, projections, quote, health. Works in Node ≥18 and browser. Typed errors: GoableApiError (422 + Zod issues array), GoableNetworkError (timeout/parse). Config: apiKey, baseUrl, timeoutMs, fetch override.packages/calibration/): weekly L3 calibration cron (.github/workflows/calibration-weekly.yml) — triple-method ensemble (sklearn isotonic, GaussianProcess, statsmodels Logit × 500 bootstrap) + Heckman IPW + Brier Skill Score gate. Emits YAML curve replacements + PR body for operator review.apps/worker-lambda): Stripe webhook events via SQS → processStripeWebhookEvent use case → Drizzle tenant usage update. Bundled with esbuild (ESM, Node 22), deployed via Terraform.infra/terraform/): bootstrap + per-environment (env/prod/) modules for AWS infrastructure.calibration-weekly.yml, drift-daily.yml (04:00 UTC CUSUM scan), decision-agent-nightly.yml (03:00 UTC MLP warm-start retrain)./v1/health/ready readiness probe (gates on skill-lookup hydration); SIGTERM graceful shutdown (10s in-flight budget).@goable-io/emails — React Email templates (@react-email/components, @react-email/render).@goable-io/sdk 0.2.0 (zero-dep API client), @goable-io/profiles-catalog 2.3.4 (public YAML profiles), @hono/zod-openapi (OpenAPI-first routes), drizzle-orm 0.38 (type-safe Postgres), @tanstack/react-query 5 + react-table 8 + react-form 1.28 (admin data layer), maplibre-gl 5 + react-map-gl 8 (geospatial UI), pdfkit 0.18 (SPC charter PDF generation), recharts 3 (admin dashboards), jose 5.9 (JWT HS256 session crypto), pino 9.5 (structured logging), vitest 2.1 (unit + integration tests), stripe 22 (API) + 17 (Lambda)src/ files, double quotes, no semicolons, trailing commas, organize-imports)app.request() — no real HTTP server; makeMockProvider(samples) pattern for weather mocks; RUN_PG_INTEGRATION=1 gates Drizzle store integration tests; pnpm turbo lint typecheck test must stay green before every commitsdk-release.yml)Realized for Goable