Skip to content
BoringStack
GitHub

Monorepo layout

4 min read

Repository contract

BoringStack ships as a single monorepo. API, UI, docs site, and Compose infra live in one tree with path-filtered CI and root-level bun run regen / bun run check for cross-app contracts.

apps/

api · ui · docs

infra/

compose · bootstrap

1

git clone

The runtime is composed from apps/api, apps/ui, and infra/compose. Optional VPS provisioning lives in infra/bootstrap. The docs site source is apps/docs (deployed to boringstack.xyz).

Monorepo layout
boringstack/
  • apps/api/Bun + Elysia + Drizzle API
  • apps/ui/Vite + React SPA
  • apps/docs/Astro Starlight (boringstack.xyz)
  • infra/compose/Docker Compose runtime
  • infra/bootstrap/optional OpenTofu bootstrap

Each app keeps its own package.json, lockfile, and lint config. CI workflows live at the repo root (.github/workflows/) with path filters such as apps/api/** and infra/compose/**.

context

Agents work in smaller trees

Open `apps/ui` for UI work without loading the whole API schema graph unnecessarily.

release

Images ship independently

API and UI each publish their own GHCR image. Compose pins versions via env.

contract

Join points are explicit

OpenAPI, ACL types, lint-meta RULES, and docs JSON catalogs are regenerated from root.

Compose build contexts
boringstack/
  • infra/compose/compose/
    • docker-compose.ymlbuild.context → ../../../apps/api and ../../../apps/ui
    • api.dev.env.exampleapi-dev / api-migrate env
    • dev.shdev profile entrypoint
apps/api
  • HTTP routes, auth, OAuth, password hashing
  • Email send and provider abstraction
  • BullMQ jobs and queue workers
  • Audit log writes
  • Drizzle schema and migrations
apps/ui
  • Pages, components, queries, stores, routes
  • OpenAPI client (from /swagger/json)
  • i18n (en, de), shadcn/ui primitives
  • Storybook, Playwright e2e
infra/compose
  • docker-compose YAMLs and profile overlays
  • Prometheus, Grafana, Loki, Promtail configs
  • Traefik labels, ACME, security middlewares
  • Backup scripts and runbooks
apps/docs
  • boringstack.xyz Starlight site
  • Generated lint-meta and scripts tooling catalogs
infra/bootstrap (optional)
  • Hetzner provisioning, Cloudflare DNS, cloud-init

From the repo root:

Terminal window
bun run regen # ACL types → OpenAPI schema → RULES.md → docs JSON
bun run check # drift checks (api on :3000 required for OpenAPI check)
ContractProducerConsumerGenerated artifact
ACL typesapps/apiapps/uiapps/ui/src/lib/acl/acl.types.generated.ts
OpenAPIapps/api /swagger/jsonapps/uiapps/ui/src/lib/api/schema.d.ts
lint-metaeach app scripts/lint-meta/committed RULES.mdper app
Docs catalogsapi + ui scripts/READMEapps/docssrc/data/*.json

CI runs the same checks from one checkout on GitHub main.

The API exposes its OpenAPI document at /swagger/json. The UI ships bun run generate:api that reads that document and rewrites src/lib/api/schema.d.ts. When the API changes routes, the UI either fails typecheck or you run bun run regen at the repo root.

See OpenAPI client for the full flow.

infra/compose/compose/docker-compose.yml defines api-dev / api / ui-dev / ui services whose build context is apps/api and apps/ui. Env vars are documented in compose/api.dev.env.example and compose/api.prod.env.example.

In prod the default image: reference pulls a pre-built image from GHCR, published by the release workflows. See Deployment.