NoteBugsDocs

Start

System overview

How NoteBugs is put together, and where each rule is applied.

The stack

LayerWhat
ApplicationNext.js 15 (App Router) and React 19, a single container
DatabasePostgreSQL, through Prisma
Filesa disk volume or an S3-like bucket, decided per content type
DeliveryDocker Compose; migrations run in the entrypoint

There is no mandatory external service, queue, cache or worker. The backup schedule is a timer inside the process itself, not a separate service.

What happens before any route

Every request goes through the same three checks before it reaches any business code, in this order:

  1. 1Rate limit, before any query hits the database.
  2. 2Identity: who is calling, resolved once per request.
  3. 3Role: what that identity reaches, checked against the permission matrix.

A new route is born closed

The matrix's default rule is: reading takes VIEWER, writing takes MEMBER. A route that is not registered still demands identity: none is born open.

Business rules are checked in one place

Each rule is checked from inside the transaction, and never rewritten route by route. That is why the API behaves the same from the interface or from a curl.

RuleWhat it guarantees
Card position within a columnthe order is the same for everyone, even with two simultaneous drags
Card and epic in the same projecta card only enters an epic of its own project
Card due date against the epic targetno date already chosen is overwritten without an explicit ask
Workspace boundarynothing crosses from one workspace into another
Column set in effectthe columnId sent belongs to that card's board
Composing a card from a templatewhatever came in the request wins; the template fills the rest
Where the bytes are writtendisk or bucket, according to the content type
What each role reachesthe narrowest of the two axes always wins

The interface applies those same rules to warn before the request goes out, but the one who refuses is always the server: an outdated client works around none of them.

Date, time and zone

The container runs in UTC. A card's and an epic's due date carries the zone it was declared in (dueAtZone), and that is what makes the same day hold for every reader, wherever they are.

An error message never carries a formatted date

Formatting on the server would get the day wrong, because the container is in UTC. Formatting is the client's job: it knows the reader's zone.