# Einherjar [![license](https://img.shields.io/badge/license-AGPL--3.0-22863A?style=flat-square)](LICENSE) [![go](https://img.shields.io/badge/Go-1.26+-00ADD8?style=flat-square&logo=go&logoColor=white)](https://go.dev) [![modules](https://img.shields.io/badge/modules-14-5C4EE5?style=flat-square)]() > *For those who come after.* A Go framework built from battle-tested production code — organized for teams, documented for the developer who was never in the room. --- ## The Name In Norse mythology, the **Einherjar** are the chosen — warriors who fell in battle and were carried to Valhalla by the Valkyries. There they train, day after day, preparing for Ragnarök. Not for themselves. For what comes after. This framework carries that name deliberately. Every interface defined here, every architectural decision recorded, every module boundary enforced exists for the developer who will build on this code without ever speaking to its authors. No tribal knowledge. No implicit conventions. **The documentation is the system.** --- ## What Is This Einherjar is a collection of composable Go modules, each addressing one infrastructure concern. The mental model is Spring Boot starters — not one framework binary, but a curated set of independent modules with isolated dependency graphs. Each module is its own `go.mod`. Import only what your service needs. A minimal CRUD API requires exactly three: **`core` + `web` + one `db-*`**. --- ## Modules | Module | Version | Purpose | Depends on | |---|---|---|---| | [`contracts`][contracts] | v1.0.0 | Pure interfaces. The constitution of the framework. | — | | [`core`][core] | v1.0.0 | Lifecycle, logging, errors, validation | `contracts` | | [`web`][web] | v1.0.0 | HTTP server, middleware, routing, health | `contracts` `core` | | [`auth`][auth] | v1.0.0 | Auth middleware, RBAC | `contracts` `core` `web` | | [`auth-jwt`][auth-jwt] | v1.0.0 | JWT signing, token pairs, refresh | `contracts` `core` `auth` | | [`auth-firebase`][auth-firebase] | — | Firebase authentication *(deferred)* | `contracts` `core` `auth` | | [`db-postgres`][db-postgres] | v1.0.0 | PostgreSQL client | `contracts` `core` | | [`db-mysql`][db-mysql] | v1.0.0 | MySQL client | `contracts` `core` | | [`db-sqlite`][db-sqlite] | v1.0.0 | SQLite client | `contracts` `core` | | [`cache-valkey`][cache-valkey] | v1.0.0 | Valkey / Redis-compatible cache | `contracts` `core` | | [`storage-minio`][storage-minio] | v1.0.0 | Object storage | `contracts` `core` | | [`telemetry`][telemetry] | v1.0.0 | OpenTelemetry — `main` packages only | `contracts` `core` | | [`worker`][worker] | v1.0.0 | Concurrent background worker pool | `contracts` | | [`httpclient`][httpclient] | v1.0.0 | Resilient outbound HTTP client | `contracts` `core` | | [`smtp`][smtp] | v1.0.0 | SMTP email delivery with no-op fallback | `contracts` `core` | --- ## Dependency Graph ```mermaid graph TD contracts["contracts\nv1.0.0"] core["core\nv1.0.0"] web["web\nv1.0.0"] auth["auth\nv1.0.0"] auth_jwt["auth-jwt\nv1.0.0"] auth_firebase["auth-firebase\n(deferred)"] db_postgres["db-postgres\nv1.0.0"] db_mysql["db-mysql\nv1.0.0"] db_sqlite["db-sqlite\nv1.0.0"] cache_valkey["cache-valkey\nv1.0.0"] storage_minio["storage-minio\nv1.0.0"] telemetry["telemetry\nv1.0.0"] worker["worker\nv1.0.0"] httpclient["httpclient\nv1.0.0"] smtp["smtp\nv1.0.0"] contracts --> core contracts --> web core --> web contracts --> auth core --> auth web --> auth contracts --> auth_jwt core --> auth_jwt auth --> auth_jwt contracts --> auth_firebase core --> auth_firebase auth --> auth_firebase contracts --> db_postgres core --> db_postgres contracts --> db_mysql core --> db_mysql contracts --> db_sqlite core --> db_sqlite contracts --> cache_valkey core --> cache_valkey contracts --> storage_minio core --> storage_minio contracts --> telemetry core --> telemetry contracts --> worker contracts --> httpclient core --> httpclient contracts --> smtp core --> smtp classDef foundation fill:#b45309,stroke:#78350f,color:#fff classDef weblayer fill:#1d4ed8,stroke:#1e3a8a,color:#fff classDef datalayer fill:#15803d,stroke:#14532d,color:#fff classDef standalone fill:#6d28d9,stroke:#4c1d95,color:#fff classDef deferred fill:#6b7280,stroke:#374151,color:#fff,stroke-dasharray:5 5 class contracts,core foundation class web,auth,auth_jwt weblayer class auth_firebase deferred class db_postgres,db_mysql,db_sqlite,cache_valkey,storage_minio datalayer class telemetry,worker,httpclient,smtp standalone ``` **Arrow direction:** dependency flows downward — a module above provides interfaces consumed by the module below it. | Colour | Layer | |---|---| | Amber | Foundation — `contracts`, `core` | | Blue | Web layer — `web`, `auth`, `auth-jwt` | | Green | Data layer — `db-*`, `cache-*`, `storage-*` | | Purple | Standalone — `telemetry`, `worker`, `httpclient`, `smtp` | | Grey (dashed) | Deferred — not yet released | Data modules (`db-*`, `cache-*`, `storage-*`) never import `web`. Repositories do not know HTTP exists. This boundary is absolute. --- ## The Law `contracts` is the constitution of this framework — pure interfaces, zero dependencies, permanent signatures. Changes flow **outward from `contracts`**, never inward. Before any change to `contracts`, the blast radius is calculated: which interfaces are affected, which modules implement them, which modules consume those. Release sequence: `contracts` first, then implementors, then consumers. Adding a method to a published interface is a breaking change in Go. It is not permitted without a major version bump and coordinated updates across all implementing modules. --- ## For Those Who Come After Good code is not written for the machine. It is not even written for the deadline. It is written for the person who will read it at 11pm six months from now, under pressure, trying to understand what this thing does and why it was built this way. Every ADR in this organization exists because a decision was made and the reasoning behind it matters as much as the outcome. Every interface in `contracts` is as small as it can be because the developer implementing it in a new context deserves to know exactly what is required and nothing more. This is the standard. Not the deadline. --- ## Legal Einherjar is developed and maintained by **NOCHEBUENADEV**, the trade name of its founder operating as a *Persona Física con Actividad Empresarial* (PFAE) under Mexican law. A PFAE is a natural person — an individual — who conducts business under their own legal identity. There is no separate corporate entity: the copyright holder is the individual behind NOCHEBUENADEV. All modules are licensed under the **GNU Affero General Public License v3.0** (AGPL-3.0). See the `LICENSE` file in each repository. Contributions are accepted under the terms of the [Contributor License Agreement](https://code.nochebuena.dev/einherjar/contracts/src/branch/main/CLA.md). Contributors retain ownership of their work; they grant NOCHEBUENADEV a perpetual, irrevocable license that includes the right to relicense. --- [contracts]: https://code.nochebuena.dev/einherjar/contracts [core]: https://code.nochebuena.dev/einherjar/core [web]: https://code.nochebuena.dev/einherjar/web [auth]: https://code.nochebuena.dev/einherjar/auth [auth-jwt]: https://code.nochebuena.dev/einherjar/auth-jwt [auth-firebase]: https://code.nochebuena.dev/einherjar/auth-firebase [db-postgres]: https://code.nochebuena.dev/einherjar/db-postgres [db-mysql]: https://code.nochebuena.dev/einherjar/db-mysql [db-sqlite]: https://code.nochebuena.dev/einherjar/db-sqlite [cache-valkey]: https://code.nochebuena.dev/einherjar/cache-valkey [storage-minio]: https://code.nochebuena.dev/einherjar/storage-minio [telemetry]: https://code.nochebuena.dev/einherjar/telemetry [worker]: https://code.nochebuena.dev/einherjar/worker [httpclient]: https://code.nochebuena.dev/einherjar/httpclient [smtp]: https://code.nochebuena.dev/einherjar/smtp