Repository navigation
Contributing
Nautelle is a TypeScript monorepo — Bun workspaces and Turborepo — with a NestJS API, a Next.js web app, a Bun-compiled runner and a framework-free shared package. This page covers getting a development setup running, the conventions every change follows, how CI decides what to test, and how decisions are recorded. The authority on what Nautelle is and how it is built is docs/: code that disagrees with it is either a bug or an undocumented decision.
flowchart LR
spec["Spec + issue<br/>(docs/specs)"] --> branch["Branch from develop"]
branch --> pr["Pull request → develop"]
pr --> ci["Woodpecker: check · build ·<br/>tests of what changed"]
ci --> review["Review + merge"]
review --> staging["Push to develop<br/>deploys staging"]
staging --> main["Release: develop → main"]
| Path | Package | Stack |
|---|---|---|
apps/api |
@nautelle/api |
NestJS 11, Prisma 7, PostgreSQL 16, BullMQ on Redis 7 — port 8180 |
apps/web |
@nautelle/web |
Next.js 16 (App Router), React 19, Tailwind CSS 4 — port 3517 |
apps/runner |
@nautelle/runner, published as nautelle-runner
|
Bun, compiled to a single binary |
packages/shared |
@nautelle/shared |
framework-free TypeScript shared by the API, the web app and the runner |
plugins/nautelle |
— | the Claude Code plugin: hooks, MCP server, commands |
docker |
— | PostgreSQL and Redis for local development |
deploy |
— | the staging stack, the CI image, release notes |
docs |
— | product, architecture, ADRs, specs |
Requirements: Bun 1.4.0+, Node.js 24+, Docker.
git clone https://github.com/AndreyBegma/Nautelle && cd Nautelle
bun install
cp docker/.env.example docker/.env
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env
bun run db:setup # PostgreSQL :5421, Redis :6421, migrations, seed
bun run admin:create # the first admin
bun run start:dev # API :8180, web :3517The examples work as they are for local development. See the Getting Started for pairing a local runner.
| Command | What it does |
|---|---|
bun run start:dev |
API and web in watch mode |
bun run check |
Biome lint, format and import order, plus the CI and image checks, without writing |
bun run format |
Biome format, writing |
bun run test |
the test suites |
bun run typecheck |
TypeScript across the workspaces |
bun run build |
build everything (Turborepo, cached) |
bun run db:setup |
start PostgreSQL and Redis, apply migrations, seed |
bun run db:migrate |
prisma migrate dev: create and apply a migration |
bun run db:generate |
regenerate the Prisma client |
bun run db:studio |
Prisma Studio |
The e2e tests use their own database, TEST_DATABASE_URL, which they wipe on every run — see the e2e test database guide.
-
Bun is the package manager, with versions pinned exactly. Never
npm installoryarn. - Biome is the only linter and formatter: single quotes, trailing commas, 2 spaces. No ESLint or Prettier.
-
TypeScript is strict everywhere, with no
any. -
Prisma is used only by
apps/api, throughPrismaService. The web app talks to the API over HTTP, never to the database. -
Schema changes go through
prisma migrate devwith a descriptive name. Never edit an applied migration. -
Every API input is a DTO validated with
class-validator; the globalValidationPiperejects unknown fields. -
Shared types and pure helpers go in
packages/shared, which stays framework-free. -
Environment variables live in git-ignored
apps/*/.envand are documented in.env.example— add a variable to the example in the same change that reads it. -
Tests sit next to the code as
*.spec.ts. A bug fix comes with the test that would have caught it. - Everything in the repository is written in English.
- The control plane never reads a project's filesystem — that is the runner's job (ADR-0001).
- The runner accepts typed commands only — no shell strings (ADR-0010).
- The UI uses only glass-ui components. A missing component is added to glass-ui, not written here (ADR-0011, usage guide).
- Branch from
develop. - Make the change, with tests and documentation.
- Run
bun run check,bun run testandbun run build. - Open a pull request into
develop.mainreceives releases fromdevelop.
A push to develop deploys staging.
-
A feature starts as a spec in
docs/specs/(<issue>-<slug>.md) and a GitHub issue with acceptance criteria. Specs are written with/code-sentinel:spec. -
An architectural change records a new ADR in
docs/adr/in the same pull request. ADRs are numbered and never rewritten: a changed decision gets a new ADR that supersedes the old one. Read the relevant ADRs before changing architecture.
CI runs on Woodpecker (.woodpecker/pipeline.yml). check and build run in full every time; which tests run depends on the event:
| Event | Tests |
|---|---|
| pull request | the packages changed since the merge base, plus their dependents |
pull request touching a root file (package.json, bun.lock, turbo.json, tsconfig.json, biome.json, .woodpecker/**, deploy/ci-image/**) |
the full suite |
pull request touching only docs/**, *.md, .github/**
|
none, and build is skipped; check still reports |
push to develop
|
none — the pull request already tested it |
push to main, manual run |
the full suite |
Pull-request pipelines are untrusted: their steps get no Docker socket, no host volume, no secret and no privileged container. bun run check fails a change that breaks those rules.
New @andrewbehma/glass-ui releases arrive as pull requests into develop from Renovate (renovate.json). Renovate watches only that package, pinned exactly, and a person merges its pull requests. Every other dependency is updated by hand.
Decisions: ADR-0001 · ADR-0010 · ADR-0011 · ADR-0016 · ADR-0018 · ADR-0053
Nautelle · Website · Discord · Engineering docs · MIT
Start
Work
Cost and health
Surfaces
Operate
Reference