Merge pull request 'Phase 0 — Foundation: backend, data model, local auth, frontend, deploy + CI' (#1) from phase-0-foundation into main
This commit was merged in pull request #1.
This commit is contained in:
+18
-3
@@ -79,6 +79,7 @@ Async throughout (FastAPI + async SQLAlchemy). Anything that can be slow or can
|
||||
- **Authoring UI** is client-side, talking to the REST API. A generated TypeScript client keeps it in sync with the OpenAPI contract.
|
||||
- **Mobile-first.** Layouts are responsive and touch-first; no separate mobile codebase. Feature parity with desktop is a requirement, not a nice-to-have.
|
||||
- **Design system:** Tailwind + shadcn/ui for a consistent, accessible (WCAG 2.2 AA target) component layer.
|
||||
- *Status:* the scaffold has landed — Next.js (App Router) + Tailwind + shadcn-style primitives, a typed client generated from the backend OpenAPI spec (`openapi-typescript` + `openapi-fetch`), and auth + tree/person views. Auth rides the same-origin HttpOnly session cookie (Caddy proxies `/api/*` to the backend). Built as a standalone container; Caddy routes `/` to it.
|
||||
|
||||
## 5. Data model
|
||||
|
||||
@@ -93,7 +94,7 @@ Core entities and the important relationships. (Illustrative, not final DDL.)
|
||||
- **Person** — belongs to a Tree. Has many **Name** records (with parts: given, surname, prefix/suffix, and a type such as birth/married/alias) to support variants and changes over time. Carries living/deceased status.
|
||||
- **Relationship** — typed edge between two Persons within a Tree: parent–child (with a qualifier: biological, adoptive, step, foster, donor, guardian), partnership/marriage (with its own events), sibling (often derived). Modeling parentage as qualified edges — rather than assuming a two-biological-parent nuclear family — is what makes adoption, donor conception, and blended families first-class rather than awkward.
|
||||
- **Event** — typed (birth, death, marriage, residence, immigration, etc.), with a date (supporting ranges, approximations, and non-Gregorian calendars), an optional Place, and attachable to a Person or a partnership.
|
||||
- **Place** — a tenant-shared gazetteer entity: hierarchical (place within place), with **historical name variants and date ranges** so a record entered as "Königsberg, 1900" sorts and displays correctly against "Kaliningrad." Optional coordinates.
|
||||
- **Place** — a tenant-shared gazetteer entity: hierarchical (place within place), with **historical name variants and date ranges** so a record entered as "Königsberg, 1900" sorts and displays correctly against "Kaliningrad." Optional coordinates. *(Phase 0 scopes Place to a Tree via `tree_id` for absolute tenant isolation; a deployment-wide shared gazetteer is a deliberate later refinement. Variants live in a `PlaceName` child table.)*
|
||||
|
||||
### Sources (first-class)
|
||||
- **Source** — a reusable record of an origin: title, repository, type, optional URL, free citation text, optional quality grade. One Source backs many facts.
|
||||
@@ -147,6 +148,7 @@ Three parts, deliberately separated:
|
||||
- `AuthProvider` interface with implementations for **local** (password + email verification/reset), **OIDC** (validated against Authentik; expected to work with Keycloak, Auth0, etc.), and **social** (Google, Apple, Facebook).
|
||||
- Operators enable any subset via config. This deployment will use Authentik (`auth.jpaul.io`) plus selected social providers; a bare self-hoster can run local-only.
|
||||
- Sessions are backend-issued; the assistant principal is minted per-session and scoped to the acting user.
|
||||
- *Status:* **local auth has landed** — Argon2id password hashing, opaque backend-issued sessions (only the token hash is stored; presented as a Bearer token or HttpOnly cookie), and email verification + password reset via the `Mailer` interface (console in dev, SMTP for operators). OIDC and social providers are Phase 5. Every write records an attributable actor in the audit log.
|
||||
|
||||
## 10. Search
|
||||
|
||||
@@ -168,13 +170,26 @@ Jobs are idempotent and retryable; an external failure degrades gracefully rathe
|
||||
|
||||
## 12. Deployment & CI/CD
|
||||
|
||||
- **Images** are built by **Gitea Actions** on `git.jpaul.io` and pushed to the **Gitea container registry**.
|
||||
- Servers **pull** new images to deploy — no build on the host.
|
||||
- **Images** are built by **Gitea Actions** (`runs-on: docker`) and pushed to the **Gitea container registry**, one package per component (`provenance-backend`, `provenance-frontend`) linked to the repo.
|
||||
- **Split push/pull endpoints** (mirrors the drawbar setup): CI **pushes** to the LAN registry endpoint `192.168.0.2:1234` over plain HTTP (buildx configured `insecure`/`http`) to bypass the Cloudflare request-body limit; servers **pull** from the public `git.jpaul.io` FQDN (TLS via Cloudflare). Same Gitea registry, two front doors. Auth uses the `REGISTRY_TOKEN` Actions secret.
|
||||
- Tag scheme: `test-main` (current main), `test-sha-<long>` (rollback pins), the component version, and `latest` on `v*` tags.
|
||||
- Servers **pull** new images to deploy — no build on the host. The deploy compose references `git.jpaul.io/justin/provenance-{backend,frontend}:${IMAGE_TAG:-test-main}`; `docker-compose.dev.yml` is a local-build override.
|
||||
- **Caddy** terminates TLS and reverse-proxies frontend + backend. **Cloudflare Tunnel** is the preferred ingress (no open inbound ports) but is never required; a plain Caddy-on-a-public-host deployment is equally supported.
|
||||
- **Configuration** is entirely environment-driven (twelve-factor). One `.env` plus the compose file is enough to stand up a deployment.
|
||||
- **Migrations** run on backend start (or via an explicit job) so an image pull + restart is a complete upgrade.
|
||||
- **Backups:** documented procedure for Postgres dump + object-store sync; restore is the inverse.
|
||||
|
||||
**Repository layout (as scaffolded):**
|
||||
|
||||
```
|
||||
/backend # FastAPI, uv-managed. app/{api/v1, services (+privacy), repositories, models, schemas, integrations (auth/mailer), core}; migrations/ = Alembic
|
||||
/deploy # docker-compose.yml, Caddyfile, .env.example
|
||||
/.gitea/workflows # Gitea Actions: build images → Gitea registry
|
||||
/frontend # Next.js (App Router, TS, Tailwind). app/ pages, lib/api (openapi-typescript client), components/ui, Dockerfile (standalone)
|
||||
```
|
||||
|
||||
The compose stack runs `postgres` (pgvector image — includes `pgvector`; `pg_trgm` ships in contrib), `minio`, `backend`, and `caddy`. The **worker** container (same image as backend, worker mode) joins once queue-driven jobs exist. Phase 0 ships a minimal backend with `/health` (liveness) and `/health/ready` (Postgres reachability) to validate the deploy wiring before the data model lands.
|
||||
|
||||
## 13. Observability
|
||||
|
||||
- Structured (JSON) logs from backend and worker.
|
||||
|
||||
Reference in New Issue
Block a user