Saltar al contenido principal

Principles

Five non-negotiable principles guide every decision in Nucleus. They are formalised in SPEC.md; this page is the human-readable summary.

1. Stdlib-first runtime

The runtime is built on net/http, database/sql, log/slog, and context.Context. New third-party dependencies require an ADR and a dependency-impact review.

The reasoning is operational, not aesthetic:

  • The standard library is the most reviewed Go code in existence; its semantics rarely surprise.
  • Third-party deps are the dominant source of maintenance debt and supply-chain risk.
  • Go's release cadence is predictable; building on the stdlib lets us adopt new features without coordinating across vendors.

When a dependency is genuinely worth taking — Asynq for the task queue, Casbin for RBAC, OpenTelemetry for tracing — the ADR records why, what the abstraction boundary is, and how we would replace it.

2. Explicit configuration & lifecycle

There are no hidden global singletons, no implicit init() registration, no package-level state masquerading as the framework. Every Nucleus application is created by an explicit call:

a, err := app.New(cfg, opts...)

The implications:

  • Multiple App instances coexist trivially in the same process — end-to-end tests run a real App, not a mock.
  • Lifecycle is observable. App.Run blocks; App.Shutdown runs hooks in reverse order; defer a.Shutdown(ctx) is enough.
  • Wiring is reviewable. The composition root is in cmd/server/main.go (or in a fluent call when you opt in); nothing happens at import time.

3. Compatibility by contract

The stable surface is explicit:

  • exported symbols in pkg/*,
  • registered CLI commands,
  • registered config keys.

Each of these is frozen by tests under contracts/. Removals require a deprecation entry; rename-and-keep-the-shim is the default path. Details: Compatibility policy.

4. Security by default

Production-sensitive defaults ship enabled. The defaults are designed so that "I forgot to configure CSRF" looks like an explicit opt-out rather than a missing line:

  • CSRF on for state-changing form posts.
  • Session cookies Secure by default (session_cookie_secure: true); plain-HTTP local dev must opt out explicitly.
  • CORS denies unknown origins.
  • Rate limiting on every public endpoint.
  • Argon2id password hashing with versioned cost parameters.
  • TLS hardening on the embedded HTTP server when it is used directly.

Each setting is reachable from nucleus.yml. If you need to relax a default, you do it deliberately, in writing.

5. SQL-first operations

There is no ORM. Migrations are SQL files; the CLI applies them in order; queries are written in SQL. pkg/db adds database/sql ergonomics, telemetry and health checks; pkg/model adds metadata for the admin panel and CRUD helpers.

This is a constraint we want, not one we tolerate:

  • Postgres, MySQL and SQLite all behave differently in subtle ways. MSSQL and Oracle add their own dialects on top (opt-in via the -tags mssql / -tags oracle build tags). Hiding any of this behind an ORM either produces the lowest-common-denominator query or quietly emits incompatible SQL.
  • Migrations as SQL files are reviewable and replayable independently of the binary that wrote them. Oracle multi-block PL/SQL scripts are split correctly by db.ExecScript so the slash-terminator works as developers expect (see Models & database → Multi-block scripts).
  • The CLI is deterministic: the same nucleus migrate against the same database ends in the same state. nucleus migrate drift reports divergence between the live schema and the registered models on every supported engine.

What follows from the principles