Sthora
Contributing

Repository layout

What lives where in the monorepo, and the boundaries that are enforced rather than agreed.

A pnpm monorepo: apps/* are deployable applications, packages/* are shared libraries. Applications depend on packages; packages never depend on an application; two applications never import each other.

Packages

Path
packages/shared@sthora/shared — contract schemas for every resource crossing a package boundary. Reached only through explicit subpath exports.
packages/email@sthora/email — the email transport seam: contracts, configuration, header safety, and one file per provider. Knows no subject line and no template.
packages/server-driver@sthora/server-driver — the ServerDriver contract and the implementation that drives the machine it runs on. A package because two applications run it.

Applications

Path
apps/backend@sthora/backend — the NestJS control-plane API.
apps/agent@sthora/agent — the process a customer runs on their own machine. Listens on nothing.
apps/dashboard@sthora/dashboard — the TanStack Start operator portal.
apps/website@sthora/website — the public marketing site.
apps/docs@sthora/docs — this site.

Inside the backend

Path
src/servers/driverWhich machine a call acts on. ServerDriverRegistry resolves a driver per server row.
src/agentThe channel a customer's server dials into.
src/deploymentsSource materialisation, build-type detection, the compose sanitiser and renderer, the per-application lock, the queues, and the log bus.
src/gitDeploy-key generation, cloning through the driver, repository-URL validation, the optional GitHub App.
src/webhooksThe one public route that can start work, behind an HMAC check.
src/proxyTraefik's compose project, and the ingress and certificate state read back from it.
src/notificationsAlert rules and their copy, the alert lifecycle, delivery channels, the notify queue.
src/servicesManaged PostgreSQL, Redis, and MinIO, and their scheduled backups. Everything kind-specific in one catalogue.
src/billingPlans, the one subscription an organization has, the usage meter, and the ceilings they impose.
src/emailsReact Email templates, one subfolder per audience.
src/cryptoAES-256-GCM at rest.
prisma/The schema and its checked-in migrations. The generated client lands in src/generated/prisma and is git-ignored.

Boundaries that are enforced

RuleEnforced by
child_process, dockerode, and node:fs appear nowhere in apps/backend/src outside src/servers/driver/scripts/lint-driver-boundary.mjs
No barrel files; imports are explicit files or subpath exportsLint and review
process.env is read only inside configuration factoriesReview, and one env schema per area
Clients import contracts only, never persistence schemasPackage exports

Deployment and packaging

Path
apps/*/DockerfileOne image per application.
deploy/docker-compose.ymlThe platform's own compose file.
install.shThe installer — see the installer.
.github/workflows/ci.ymlPublishes multi-arch images to GHCR after the suite passes on main.

Where the rules live

FolderContains
.ai/Coding standards and procedures. Project-independent and copy-safe — no product names, packages, ports, or domain concepts.
docs/Everything about this project: the fact sheet, the implementation plan, the runbook, the tenancy design.
apps/docs/content/These pages — the product documentation, written for whoever is using the platform.

Learn something about the project, write it to docs/. Establish a coding standard, write it to .ai/ — and only if it would still make sense in an unrelated project.

On this page