# Routing map (minimal) This repo ships a **single host** experience that typically serves: - **UI** (Flutter web) under `/app/**` - **API** (Spring Boot) under `/api/v1/**` - **MCP** (Node MCP server) under `/mcp` (agents; not a subdomain) - **Docs** (static) under `/docs/**` plus a short alias `/doc` The goal is a **clear split** between marketing/entry (`/`), the console UI (`/app/**`), and static documentation (`/docs/**`). ## Production (Firebase Hosting, www.secure-flows.com) Source of truth: `safeHook/firebase.json` (`hosting` block). Static output is `safeHook/build/web` after `npm run build:web` (includes `web/docs`). Trailing-slash normalization (e.g. `/docs` → `/docs/`) is required so directory `docs/index.html` resolves the same as in local `serve.json`. ## Production (Docker/nginx bundle) Source of truth: `docker/nginx-frontend.conf` and root `Dockerfile`. Custom domain traffic (`www.secure-flows.com`) is proxied through **Cloudflare** in front of Render. After a deploy, stale UI usually means the CDN (or browser) kept an old **`main.dart.js`** — not a failed Render rollout. **Slash redirects:** nginx listens on Render’s `$PORT` (often `10000`) behind TLS termination. The server block sets `absolute_redirect off` and `port_in_redirect off` so `return 302 /docs/` (and similar) emit a relative `Location` — otherwise clients (notably Facebook’s in-app browser) follow `http://www.secure-flows.com:10000/...` and fail. Prefer sharing URLs with a trailing slash (`/docs/`) until a deploy with that nginx fix is live. ### CDN cache headers (nginx) `docker/nginx-frontend.conf` sets explicit cache policy: | Path | Policy | Why | |------|--------|-----| | `index.html`, `/app/**` (SPA fallback), `main.dart.js`, `flutter_bootstrap.js`, `flutter_service_worker.js` | `Cache-Control: no-cache, must-revalidate` + `CDN-Cache-Control: no-store` | Stable filenames change every deploy; must not sit at Cloudflare edge for hours | | `/assets/FontManifest.json`, `/assets/AssetManifest*` | `Cache-Control: no-cache, must-revalidate` + `CDN-Cache-Control: no-store` | Stable names rewritten each deploy (MaterialIcons hash in FontManifest). Year-immutable here leaves browsers pointing at deleted `.otf` files → all icons missing | | `/assets/fonts/**` | `font/otf` (etc.) + `public, max-age=3600` + `Access-Control-Allow-Origin: *` | MaterialIcons is tree-shaken; path may change via build cache-bust. Real font MIME matters for Safari/WebKit FontFace. Avoid year-long immutable | | `/assets/**` (other), `/canvaskit/**` | `public, max-age=31536000, immutable` | Prefer content-hashed paths; safe to cache long-term | | `/mcp`, `/mcp/health` | `no-store` (+ `CDN-Cache-Control: no-store`) | MCP Streamable HTTP — never cache at edge | | `/docs/**`, `/` (landing) | `public, max-age=3600` | Static docs/marketing; moderate TTL is fine | **Cloudflare dashboard** (zone `secure-flows`): **Caching → Configuration → Browser Cache TTL** should be **Respect Existing Headers** so nginx controls TTL. Avoid Page Rules / Cache Rules that **Cache Everything** on `/` or `*.js` — they override origin headers and cause the “old Console home after deploy” problem. If headers were wrong on a previous deploy, **Purge Cache** once after rolling out this nginx change; later deploys should not require manual purges. ### `/` - `GET /` → static landing page (`/landing.html`) The landing page is authored in-repo at `safeHook/web/landing.html` and copied into the nginx web root by the Flutter build. ### `/assets/**` Static files placed under `safeHook/web/assets/**` are served at `/assets/**`. Example: the landing page logo is served at `/assets/secureflows.png` (source file copied from `safeHook/assets/secureflows.png`). **MaterialIcons caveat:** Flutter release builds tree-shake icons into `assets/fonts/MaterialIcons-Regular.otf` (stable basename by default). If that file is cached as `immutable`, newly added `Icons.*` glyphs can be invisible in production while still working locally — hover may still show the IconButton hit target. The web build runs `npm run cache-bust-material-icons` to rename the font with a content hash and update `FontManifest.json`. **`FontManifest.json` itself must also revalidate** (not year-immutable under `/assets/**`): a stale manifest points at a deleted hashed `.otf` and *every* icon renders as a missing-glyph box. ### `/app/**` - `GET /app/…` → Flutter SPA (`/usr/share/nginx/html/index.html`) with `try_files` fallback. Client-side routes live under `/app` (see `safeHook/lib/app_routes.dart`). ### `/api/v1/**` - `GET/POST /api/v1/**` → proxied to Spring Boot on port 8081. Spring controllers are mounted under `server.servlet.context-path=/api/v1` (`backend/src/main/resources/application.properties`). ### `/mcp` - `POST /mcp` → proxied to the Node MCP server on `127.0.0.1:8787` (Streamable HTTP). - `GET /mcp/health` → MCP process health (`{"ok":true}`). Same origin as the product (`https://www.secure-flows.com/mcp`) — **not** `mcp.secure-flows.com`. nginx sets `Cache-Control` / `CDN-Cache-Control: no-store`. Coding agents configure their MCP client with that URL (HTTP transport). The MCP process is started by `docker/entrypoint.sh` alongside Java; see `mcp-server/README.md`. ### `/docs/**` and `/doc` - `GET /doc` → **302** `/docs/openapi/` (short link to the API hub) - `GET /docs` → **302** `/docs/` (marketing + guide index; avoids “no trailing slash” 404s) - `GET /docs/openapi` → **302** `/docs/openapi/` (normalize trailing slash) - `GET /docs/openapi/user` → **302** `/docs/openapi/user/` - `GET /docs/openapi/session` → **302** `/docs/openapi/session/` - `GET /docs/**` → static files copied into nginx html at build time. Docs are copied by the root `Dockerfile`: - `COPY docs/ /usr/share/nginx/html/docs/` ## Local dev (Flutter `serve` / Chrome dev server) When running SafeHook on `http://localhost:8082`, we use `safeHook/serve.json`: - `/app/**` → `/index.html` (SPA fallback) - `/` → `/landing.html` (static homepage) Note: `safeHook/serve.json` does **not** use an HTTP redirect for `/docs` → `/docs/` because `serve-handler` applies redirects before static files and a `/docs` glob also matches `/docs/`, which would cause **ERR_TOO_MANY_REDIRECTS**. Local preview relies on **rewrites** (`/docs` and `/docs/` → `docs/index.html`) instead. Production Firebase Hosting runs **static files before redirects**, so `firebase.json` can safely use a `/docs` → `/docs/` redirect. ## Quick link list - Console UI: `/app/` - Self-service: `/app/self` - Workspace management: `/app/workspaces` - OpenAPI hub: `/docs/openapi/` - Examples hub: `/docs/examples/` - Recipe Book story: `/docs/examples/recipe-book-multi-user/` - API example (Postman, CI-validated): `/docs/examples/postman/` - What is secureFlows? (Introduction): `/docs/introduction/what-is-secureflows/` - The secureFlows MCP server (Introduction): `/docs/introduction/mcp-server/` - Video tutorials (Hebrew) (Introduction): `/docs/introduction/videos/` - Integration walkthrough: `/docs/integration/walkthrough/` - Integrating your own billing: `/docs/integration/billing-integration/` - From a local-only app (Introduction): `/docs/introduction/from-local-app/` — titled “Integration from a local-only app”; web or mobile on-device data, no central accounts - From an app with its own backend (Introduction): `/docs/introduction/from-existing-backend/` — titled “Integration from an app with its own backend”; existing platform/self-rolled auth + database being replaced by secureFlows - Security infrastructure (Introduction): `/docs/general/security-infrastructure/` - Differentiation (positioning): `/docs/differentiation/` - Platform architecture (Introduction): `/docs/general/platform-architecture/` - End-to-end user management (Architecture reference): `/docs/technical/end-to-end-user-management/` - Authentication & Authorization (Architecture reference): `/docs/technical/authentication-authorization/` - System components (Architecture reference): `/docs/technical/system-components/` - System architecture (Architecture reference): `/docs/technical/system-architecture/` - Go Beyond The Code (Workspace Manager overview): `/docs/go-beyond-the-code/` - Finding the correct redirect URL (Getting started): `/docs/getting-started/redirect-url/` - Workspace Management — General: `/docs/workspace-management/general/` - Workspace Management — Applications: `/docs/workspace-management/applications/` - Workspace Management — Invites: `/docs/workspace-management/invites/` - Workspace Management — Sessions: `/docs/workspace-management/sessions/` - Workspace Management — Users: `/docs/workspace-management/users/` - Workspace Management — Analytics: `/docs/workspace-management/analytics/` - Workspace Management — Security: `/docs/workspace-management/security/` - Pricing (Billing & Usage): `/docs/workspace-management/billing/` — `/docs/pricing/` redirects here - Documentation search: `/docs/search/` - Legal (hub): `/docs/legal/` - Terms & Conditions: `/docs/legal/terms-of-service/` - Privacy Policy: `/docs/legal/privacy-policy/` - Short docs URL: `/doc`