# 06 — Technical architecture ## Architecture decision Build the MVP as a **modular monolith** with two deployable process roles from one repository: - `web`: UI, API and synchronous domain operations; - `worker`: imports, Gitea synchronization, artifact generation and maintenance jobs. Both use PostgreSQL. Built-in playbook packages are mounted or copied into the application image and imported idempotently. No Redis, Elasticsearch or vector database is required for the MVP. ## Required workspace Routine implementation choices are fixed in `docs/25-implementation-defaults.md`. ```text apps/ web/ Next.js application and route adapters worker/ background process entry point packages/ domain/ entities, value objects, policies, domain errors application/ use cases and ports persistence/ PostgreSQL repositories and migrations playbook-schema/ JSON Schema, semantic validation and canonicalization prompt-engine/ composition, linting, provenance and rendering repository-intel/ profile normalization, findings and adapters integrations-gitea/ Gitea adapter artifacts/ Markdown and ZIP generation ui/ reusable design-system components config/ environment parsing and feature flags content/ playbooks/ canonical built-in packages fixtures/ non-sensitive evaluation fixtures schemas/ published interchange schemas docs/ ``` The bootstrap layout and canonical root commands in `docs/40-bootstrap-repository-contract.md` are normative. A materially different repository structure requires a blocker-level ADR before Milestone 1 and proof that package boundaries, deployment simplicity and every acceptance criterion remain equivalent. Do not bury domain logic in React components, server actions or HTTP handlers. ## Main request flows ### Library read ```text Browser → Next.js route → Library query use case → PostgreSQL projection → response DTO ``` ### Prompt composition ```text Browser → Composition API → load immutable playbook version → load selected profile revision → normalize and validate input → resolve policies and compatibility → compose prompt blocks → lint rendered prompt → return preview + provenance + findings ``` Preview is ephemeral. Final generation creates an immutable `generated_run` transactionally. ### Built-in package import The runtime imports the 28 P0 package directories under `content/playbooks/`; the 72-entry seed catalog remains a roadmap and is not silently exposed as executable content. ```text Worker startup/job → enumerate package directories → structural schema validation → semantic validation → canonicalize → compute digest → upsert playbook identity → insert missing immutable version → update search projection → report package-specific errors ``` One invalid package must not hide errors in other packages. The release build should fail if bundled packages are invalid. ### Gitea synchronization ```text Scheduled/manual job → load encrypted credential → verify base URL and capability snapshot → bounded API collection → normalize evidence → store immutable snapshot → derive findings → optionally propose a new profile revision ``` The job must support cancellation, per-step timeouts, rate-limit handling and safe partial failure. A partially collected snapshot is never marked complete. ## Technology guidance ### Frontend - Next.js App Router with TypeScript; - server rendering for library and detail views where useful; - client components only for interactive composer, editors and visualizations; - Tailwind CSS and an accessible component foundation; - Monaco or CodeMirror for schema-aware YAML/Markdown authoring; - a small motion library for functional animation; - URL-driven filter state; - browser tests using Playwright. ### Backend - route handlers or a thin API layer; - Zod or equivalent validation at transport boundaries; - explicit use-case classes/functions; - PostgreSQL with a typed migration/ORM layer; - a PostgreSQL-backed job table and worker polling/notification mechanism; - object artifacts on local disk in MVP, behind a storage port for future S3-compatible support. During Milestone 0, Codex must verify current stable package compatibility before selecting exact versions. It may not replace the architecture or prohibited-technology boundaries merely because another starter template is familiar. ## API style Use REST-style JSON endpoints with generated OpenAPI documentation. Favor explicit resources and actions over mirroring database tables. Examples: - `GET /api/playbooks` - `GET /api/playbooks/{slug}/versions/{version}` - `POST /api/compositions/preview` - `POST /api/runs` - `POST /api/runs/{id}/artifacts/run-pack` - `POST /api/playbook-imports` - `POST /api/repositories/{id}/snapshots` ## Background jobs Initial job types: - built-in playbook import; - user playbook import; - Gitea capability refresh; - repository snapshot collection; - Run Pack generation; - artifact retention cleanup; - stale integration health check; - optional search projection rebuild. Job records require state, attempt count, lease owner, lease expiry, progress, error code, redacted error detail and timestamps. Jobs must be idempotent or use idempotency keys. ## Configuration Environment values are parsed once into a typed configuration object. Invalid production configuration fails fast. Required categories: - database URL; - public base URL; - session/auth secrets; - encryption master key and key version; - content directory; - artifact storage directory; - maximum import/artifact sizes; - allowed Gitea network ranges or host policy; - log level; - retention settings; - feature flags. Never expose server-only configuration through client bundles. ## Storage model ### PostgreSQL Structured application data, canonical package JSON, prompt text, provenance and audit events. ### Content directory Read-only built-in playbook packages distributed with the application. Development mode can watch changes; production imports at startup or explicit migration job. ### Artifact directory Generated ZIP and Markdown files using opaque storage keys. Downloads require authorization; filenames are metadata, not direct filesystem paths. ## Failure and degraded-mode design - Database unavailable: readiness fails; liveness remains healthy while process is alive. - Gitea unavailable: local app and last snapshots continue working. - Package import failure: existing valid versions remain available; admin sees precise package errors. - Artifact storage unavailable: prompt generation still succeeds, binary export shows a recoverable error. - Worker unavailable: synchronous reads/composition work; jobs show queued/stalled state. ## Identity and first run Authentication, workspace authorization and setup lifecycle follow `docs/26-authentication-authorization.md` and `docs/31-first-run-and-instance-lifecycle.md`. Database relations follow `database/reference-schema.sql`. ## Migration policy - forward migrations are reviewed and idempotent where possible; - destructive changes require a two-release expand/migrate/contract strategy; - application startup must not silently apply irreversible migrations in production unless explicitly configured; - backup guidance appears before migrations with destructive potential; - migration version is exposed in admin health. ## Architectural constraints - no domain import from framework-specific code; - adapters depend inward on ports, never the reverse; - external API payloads are mapped to internal normalized models; - generated prompt output is based only on immutable snapshots; - direct forge writes require a separate future ADR and permission model; - direct code execution requires a separate isolation architecture and is prohibited in MVP code paths.