changed deployment

This commit is contained in:
Daniel Noulet 2026-10-02 21:44:26 +02:00
parent ea4e057b02
commit 70396c321a
22 changed files with 391 additions and 52 deletions

View file

@ -24,16 +24,22 @@ Add the local tenant hostnames once:
```text
127.0.0.1 nmbs.guess.local ypto.guess.local
127.0.0.1 auth.dev nmbs.auth.dev ypto.auth.dev
```
Open <http://nmbs.guess.local:5173> or <http://ypto.guess.local:5173>. The
hostname's first label is the tenant ID: the frontend calls
`http://<tenant>.guess.local:8080/api` and sends the same value in the
Start the shared TLS proxy described in
`/home/dnoulet/go/infrasctruture/docs/APPLICATIONS.md`, then open
<https://nmbs.guess.local> or <https://ypto.guess.local>. The hostname's first
label is the tenant ID: the frontend calls same-origin `/api` and sends the same value in the
`X-Tenant-Id` header on JSON, multipart, file-download, and event-stream
requests. The backend uses that tenant ID as the isolated PostgreSQL schema and
builds participant invitation and meeting links from the matching tenant browser
origin instead of the static localhost fallback.
Compose mounts the infrastructure CA from
`${LOCAL_DEV_CERTS_DIR:-/home/dnoulet/go/infrasctruture/certs}` into the API so
its HTTPS authorization calls to Go Loose validate normally.
Sign in with:
- Email: `interviewer@ypto.local`
@ -49,23 +55,74 @@ make backend
make frontend
```
The API listens on port `8080`. Vite permits the documented local tenant
hostnames; the API CORS policy permits `*.guess.local` development origins and
the `X-Tenant-Id` request header. Non-local deployments keep using same-origin
`/api`. Set a different secure `JWT_SECRET` outside local development.
The API listens on port `8080` inside Docker and is exposed to browsers only
through Traefik and the frontend's same-origin `/api` proxy. Vite permits the
documented local tenant hostnames; the API CORS policy permits `*.guess.local` development origins and
the `Authorization`, `X-API-Key`, and `X-Tenant-Id` request headers. Non-local
deployments keep using same-origin `/api`. Set a different secure `JWT_SECRET`
outside local development. Protected interviewer calls also need a Go Loose API
key, documented below.
## API contract
The backend exposes its current OpenAPI 3.0 contract at
<http://localhost:8080/api/openapi.json>. The document is assembled in memory
<https://nmbs.guess.local/api/openapi.json>. The document is assembled in memory
from the same typed registrations that add routes to Chi, including request and
response schemas, status codes, authentication, tenant headers, path/query
parameters, multipart uploads, downloads, and event streams. It therefore
requires no generation command and cannot miss a newly registered route.
`/api/openapi.json` and `/api/health` are system endpoints and do not require a
tenant header. All tenant data endpoints declare `X-Tenant-Id`; protected
operations additionally declare JWT bearer authentication.
tenant header. Every other route declares required `X-Tenant-Id`. Protected
operations also declare `apiKeyAuth` (`X-API-Key`) and `bearerAuth`.
## Go Loose authorization
Interviewer routes are authorized by Go Loose before the JWT check. The tenant
is read from the request `X-Tenant-Id` header and sent to Go Loose with
application slug `guess`. It is not taken from process configuration. The client
calls `POST /api/v1/authorize` on every protected request, removes `X-API-Key`
before the handler runs, and fails closed on a missing key, a denial, or a
control-plane error. There is no allow-on-error fallback. The client timeout is
2 seconds.
`GO_LOOSE_BASE_URL` is the authorize base URL. The default is
`https://%s.auth.dev`. A single `%s` is replaced with the tenant, which must
then be one DNS label (`nmbs`, not `nmbs.auth.dev` or a host with a port). A
URL without `%s` is used unchanged, while the tenant still comes from the
header. Docker Compose passes this variable to the API. Helm sets it from
`app.goLooseBaseURL`.
These routes do not require `X-API-Key` or a JWT:
- `GET /api/health` and `GET /api/openapi.json`
- `POST /api/auth/login`
- opaque-token participant interview and meeting routes under
`/api/interviews/{token}` and `/api/participant-meetings/{token}`
Login and participant routes still require `X-Tenant-Id`. Health and OpenAPI do
not.
Issue one API key per tenant in the Go Loose console for the `guess`
application. The local installer seed creates that application for `nmbs` and
`ypto`. For a containerized frontend, pass the key at image build time:
```bash
VITE_GO_LOOSE_API_KEY='gl_replace_with_issued_secret' docker compose build web
```
For Vite outside Docker, put the same variable in `frontend/.env.local`. That
file is ignored. Vite inlines `VITE_` variables into the browser bundle, so this
key is visible to anyone who can load the interviewer application. It identifies
the Guess application to Go Loose; it does not replace the interviewer JWT or
the participant token. Do not commit it or reuse a key across tenants.
The published production image does not receive this build argument. A manual
production image can:
```bash
docker build --build-arg VITE_GO_LOOSE_API_KEY='gl_replace_with_issued_secret' -t go-guess .
```
## Quality commands
@ -126,7 +183,8 @@ docker build -t go-guess .
The image serves the frontend and API on port `8080`. It applies database
migrations and shared production fixtures before startup. PostgreSQL, RabbitMQ,
and these environment variables must be provided:
and these environment variables must be provided. `GO_LOOSE_BASE_URL` is
optional and defaults to `https://%s.auth.dev`:
```bash
docker run --rm -p 8080:8080 \
@ -134,6 +192,7 @@ docker run --rm -p 8080:8080 \
-e RABBITMQ_URL='amqp://user:password@rabbitmq:5672/' \
-e JWT_SECRET='replace-with-at-least-32-characters' \
-e FRONTEND_URL='https://go-guess.example.com' \
-e GO_LOOSE_BASE_URL='https://%s.auth.dev' \
go-guess
```
@ -160,6 +219,15 @@ For the repository's single-node Docker Rancher setup, run
installs the default persistent StorageClass documented in
`rancher-install.txt`.
The chart passes `app.goLooseBaseURL` to the API as `GO_LOOSE_BASE_URL`. The
default is `https://%s.auth.dev`. Override it when Go Loose is not reachable
at `<tenant>.auth.dev`:
```bash
helm upgrade --install go-guess deploy/helm/go-guess \
--set-string app.goLooseBaseURL='https://%s.auth.example.com'
```
Development uses demo fixtures, ephemeral database and message-broker storage,
and NodePort `30080`. Production uses production fixtures, persistent volumes,
generated secrets that are preserved across upgrades, and larger resource
@ -201,6 +269,8 @@ the registry in production when possible.
## Architecture
Requests enter the Chi router and web handlers under `backend/internal/web`.
Tenant isolation runs first. The protected interviewer group then applies Go
Loose API-key authorization and JWT authentication.
Web request/response models are translated to service models before business logic
runs. Services own validation, CV trait extraction, and candidate scoring. The
repository maps service models to GORM entities and persists them in PostgreSQL.