Status: current after R0-R4
Style: modular monolith
Contract: backend/docs/api/api-contract-v1.yaml
This document describes the system as it exists now. The readiness assessment is retained as historical analysis and should not be used as the current component map.
Boardit has three runtime components:
- React web application: public reading, authentication, note tree, editor, metadata, and publishing UI.
- Go API: authentication plus note, folder, publishing, revision, and tree use-cases.
- PostgreSQL: current state, immutable revisions, refresh sessions, and asynchronous/AI foundation tables.
MSW provides a frontend-only mock API for deterministic development and Playwright smoke tests. SQLite supports fast backend tests; PostgreSQL 15 remains the production dialect and CI integration target.
main/config
-> router and middleware
-> HTTP adapters
-> noteapp.Service
-> Repository interface
-> GORM/PostgreSQL
| Module | Responsibility |
|---|---|
internal/config |
Validate runtime configuration before startup |
internal/router |
Assemble routes, middleware, CORS, and trusted proxies |
internal/handler |
Parse HTTP requests and map use-case errors to the API contract |
internal/noteapp |
Authorization, validation, publishing, concurrency, transactions, and DTO mapping |
internal/model |
Persistence-only GORM models |
internal/database |
Connection, embedded versioned migrations, rollback, and test cleanup |
Authentication handlers still use the shared database package directly; extracting an auth application module is tracked as debt.
| Module | Responsibility |
|---|---|
src/api/gen |
Orval-generated API types and React Query hooks |
src/api/orval-axios.ts |
Generated-client requester adapter |
src/api/axios.ts |
Shared HTTP client and single-flight token refresh |
src/auth |
Token storage and shared authentication form rules |
src/features/editor |
Note pagination/tree, metadata dialogs, save coordination, and editor types |
src/pages |
Route-level composition only |
src/mocks |
Contract-shaped MSW development API |
The Editor route is lazy-loaded so MDXEditor and CodeMirror do not enter the public-page initial bundle.
Editor state + base version
-> generated updateNote request
-> HTTP adapter
-> noteapp optimistic version check
-> one transaction:
conditional note update
immutable note_revision insert
outbox_event insert
-> updated note + next version
If the base version is stale, the transaction returns VERSION_CONFLICT with the current server snapshot. A revision failure or outbox failure rolls back the note update.
Tree moves also advance the note version and record a revision/event. Deletion leaves a note.deleted outbox event for future index cleanup.
- Login creates a short-lived access token and a persisted refresh session.
- Refresh tokens have an explicit token type and random
jti. - Refresh atomically revokes the old session and creates a replacement in the same session family. Requests serialize on the immutable family root: an overlapping same-token loser returns 401 without revoking the winner, while a request that begins with an already-rotated token revokes every active session in the family.
- Logout revokes the supplied refresh session and immediately clears local browser tokens.
- Access-token refresh requests share one frontend promise per refresh token, so callers using a newly rotated token never join an older token's failed request. Refresh, login, logout, and atomic token-pair publication share a browser-wide Web Lock. A losing browser tab preserves and reuses a newer token pair only when its subject matches the original request and refresh token. A definitive refresh 401 clears only the still-current pair; timeouts and server failures preserve it. Retried requests pin the validated access token so a later cross-tab account change cannot overwrite their authorization header before dispatch.
Refresh tokens are currently stored in localStorage. Migration to an HttpOnly cookie is an open security decision.
outbox_eventsstores transactionally durable pending records from note transactions; it does not provide delivery until a worker exists.background_jobsdefines provider-neutral claiming, retry, and deduplication state.ai_runsrecords model/prompt/status/cost metadata boundaries.ai_candidatesstores proposed Markdown against a base note version.
No worker, provider SDK, prompt runtime, vector index, or automatic candidate acceptance is implemented yet. See backend/docs/ai-data-boundaries.md.
Docker Compose remains the intended single-machine deployment model. Microservices, a separate message cluster, Kubernetes, and multi-model routing are intentionally deferred until measured scale or reliability requirements justify them.