mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 10:19:43 +00:00
130 lines
6.4 KiB
Markdown
130 lines
6.4 KiB
Markdown
# Local full-stack development
|
|
|
|
Use `scripts/dev-local.sh`; do not start a mixture of local and production data
|
|
services manually. The default stack uses conventional PostgreSQL and the Sauci
|
|
Node API. It does **not** start local Supabase. Supabase remains the hosted
|
|
identity provider, so local application processes need the public Auth settings
|
|
described by their environment examples.
|
|
|
|
| Service | Owner | Port |
|
|
|---|---|---:|
|
|
| PostgreSQL | Docker Compose | 54320 |
|
|
| Sauci API | Node / Hono | 3003 |
|
|
| Operations worker | Node / Hono domain modules | none |
|
|
| Web | Next.js | 3000 |
|
|
| Admin | Vite | 3001 |
|
|
| MCP | Hono | 3002 |
|
|
| Expo Metro | Expo | 8081 |
|
|
|
|
If another local project owns one of the application ports, add a different,
|
|
unique value to the ignored root `.env.local` before running `up`:
|
|
|
|
```text
|
|
SAUCI_WEB_PORT=3100
|
|
SAUCI_ADMIN_PORT=3101
|
|
SAUCI_MCP_PORT=3102
|
|
SAUCI_METRO_PORT=8181
|
|
```
|
|
|
|
`SAUCI_POSTGRES_PORT` and `SAUCI_API_PORT` remain available for their existing
|
|
local overrides. Every configured port must be a unique integer from 1 through
|
|
65535. Before launching managed services, the launcher checks both IPv4 and IPv6
|
|
listeners and fails closed with the owning PID, command, and working directory;
|
|
it never stops another project's process.
|
|
|
|
PostgreSQL is bound to `127.0.0.1`, is healthchecked before application startup,
|
|
and stores data in a named Docker volume. The local connection string is:
|
|
|
|
```text
|
|
postgresql://sauci:sauci_local_only@127.0.0.1:54320/sauci
|
|
```
|
|
|
|
Those credentials are for local development only. Deployed environments must
|
|
inject unique secrets; they must never consume `infra/backend/.env.example`.
|
|
|
|
## Start the stack
|
|
|
|
One command starts the local database, applies migrations and fixtures, and
|
|
launches the application services:
|
|
|
|
```bash
|
|
scripts/dev-local.sh up
|
|
```
|
|
|
|
Before the first use, install Docker Desktop or Rancher Desktop, tmux, and the
|
|
repository dependencies with `npm ci`. The launcher reads `.nvmrc` itself. When
|
|
the active Node version differs, it loads an installed Homebrew `nvm` and selects
|
|
the exact version automatically. If that version has not been installed yet, run
|
|
the command the launcher prints once, then rerun `up`.
|
|
|
|
Create the ignored root `.env.local` with the public Auth settings for the
|
|
designated non-production Supabase project (`itbzhrvlgvdmzbnhzhyx`). Set
|
|
`SUPABASE_AUTH_URL` (or `EXPO_PUBLIC_SUPABASE_URL`) and
|
|
`EXPO_PUBLIC_SUPABASE_ANON_KEY`; the launcher uses the URL for the API and both
|
|
values for Expo and the Admin login. It accepts only this designated hosted project or loopback Auth,
|
|
and explicitly rejects the production project and every other hosted project.
|
|
Environment variables exported in the shell take precedence over `.env.local`.
|
|
The launcher treats `.env.local` as data, never shell code: use unquoted
|
|
`KEY=VALUE` lines for only the documented launcher keys. Never place production
|
|
credentials or Auth issuer/JWKS, Auth service-role, RevenueCat, or other provider
|
|
secrets in `.env.local`; the launcher removes those inherited values from its
|
|
local API and worker processes. Each `up` replaces its managed tmux windows so
|
|
stale application processes cannot retain earlier configuration, while leaving
|
|
unrelated windows intact. Credentials are passed only to the local process that
|
|
needs them: MCP receives its local API token/key but never `DATABASE_URL`.
|
|
Before replacing a managed window, the launcher verifies and stops only its
|
|
current descendant processes. If those descendants do not exit promptly, it
|
|
leaves that window in place and fails rather than broadening process cleanup.
|
|
This includes a previously captured descendant that has left the pane lineage:
|
|
the launcher will not signal it again or replace the window until that captured
|
|
process identity is gone.
|
|
|
|
Authenticated mobile E2E requires a disposable, onboarding-complete user whose
|
|
subject has a matching local profile. Pass its credentials as `E2E_EMAIL` and
|
|
`E2E_PASSWORD` only to the Maestro process; do not store them in an Expo
|
|
environment file. To exercise MCP tools, also set a local
|
|
`ADMIN_API_SERVICE_TOKEN`, its fixture `ADMIN_API_SERVICE_USER_ID`, and a
|
|
separate `SAUCI_MCP_API_KEY`. `scripts/dev-local.sh` passes the service token to
|
|
MCP as `SAUCI_ADMIN_API_TOKEN`; MCP never receives a database or Supabase service
|
|
credential.
|
|
|
|
`up` is idempotent. `down` stops only the launcher's managed tmux application
|
|
windows and leaves unrelated windows and the database available. `down --all`
|
|
also stops PostgreSQL but retains its volume, and runs only after managed
|
|
application cleanup succeeds.
|
|
Use `status`, `logs <service>`, `restart <service>`, and `attach` for diagnosis.
|
|
`logs postgres` reads Compose logs; other names read their tmux panes.
|
|
The `worker` pane owns scheduled jobs, notifications, moderation classification,
|
|
and filesystem cleanup; local development disables external AI classification.
|
|
|
|
`reset` is destructive but fails closed unless the database is the dedicated
|
|
loopback instance on port 54320. It removes only the `sauci-local` Compose
|
|
volume, starts a fresh healthy database, then runs the API's `db:migrate` and
|
|
`db:seed` scripts. `seed` retains the volume and reapplies those two scripts.
|
|
The API seed is a no-op unless its documented local fixture identity is set.
|
|
|
|
The Expo window defaults to Metro with
|
|
`EXPO_PUBLIC_API_URL=http://127.0.0.1:3003`. Use `scripts/dev-local.sh ios` or
|
|
`android` to launch the native application against the same API. The native
|
|
launcher supplies a UTF-8 locale inside its sanitized environment so CocoaPods
|
|
does not depend on the calling shell's locale. Physical devices require a
|
|
reachable host URL rather than loopback.
|
|
|
|
`e2e/maestro/login-screen-smoke.yaml` verifies the native build and Auth shell
|
|
without credentials. `e2e/maestro/login-smoke.yaml` is the authenticated
|
|
standalone-API flow and must fail closed unless the non-production fixture above
|
|
is explicitly supplied. Neither local nor E2E runs may use the production Auth
|
|
project, production subscriber, or production application database.
|
|
|
|
Run browser E2E through the same launcher:
|
|
|
|
```bash
|
|
npm run verify:e2e
|
|
```
|
|
|
|
It starts the stack when needed, waits for API, web, admin, and MCP endpoints,
|
|
and derives Playwright URLs from the configured `SAUCI_*_PORT` values. Set
|
|
`SAUCI_E2E_REUSE=true` only to reuse a healthy stack managed with the same
|
|
configuration. Browser product-data fixtures are direct loopback PostgreSQL
|
|
fixtures that prove the standalone API result; they never use local Supabase.
|