core

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2026 License: AGPL-3.0 Imports: 0 Imported by: 0

README

einherjar/core

version license go

The chosen warriors do not choose their weapons. They forge them.

code.nochebuena.dev/einherjar/core is the foundational implementation module of the Einherjar framework. It sits directly above contracts in the dependency graph and provides the concrete tools every service needs before anything else can start: a lifecycle runner, a structured logger, typed errors, and struct validation.


What Is Einherjar?

In Norse mythology, the Einherjar are the chosen warriors of Valhalla — selected not for glory, but to be ready for what comes after. They train. They prepare. They build the capability that others will rely on.

This framework is named for that purpose. Every module is a piece of that preparation: built carefully, documented for those who were never in the room, and designed to hold under pressure.


Sub-packages

Package Import path Purpose
launcher .../core/launcher Application lifecycle — init, start, shutdown
logz .../core/logz Structured, leveled logging via log/slog
xerrors .../core/xerrors Typed error codes with context enrichment
valid .../core/valid Struct validation with pluggable i18n messages

All four are in one module because they ship together in every Einherjar service. See ADR-001.


Usage

Launcher
import (
    "code.nochebuena.dev/einherjar/core/launcher"
    "code.nochebuena.dev/einherjar/core/logz"
)

logger := logz.New(logz.Config{JSON: true, StaticArgs: []any{"service", "api"}})

lc := launcher.New(logger)
lc.Append(db, cache, server)
lc.BeforeStart(func() error {
    return server.RegisterRoutes(db, cache)
})

if err := lc.Run(); err != nil {
    logger.Error("launcher failed", err)
    os.Exit(1)
}

Environment variables (uses caarlos0/env tag syntax — application supplies the loader):

Variable Default Effect
EINHERJAR_BANNER (on) Set to off or false to suppress the startup banner
EINHERJAR_COMPONENT_STOP_TIMEOUT 15s Maximum time per component OnStop
Logger
import "code.nochebuena.dev/einherjar/core/logz"

logger := logz.New(logz.Config{Level: slog.LevelDebug, JSON: true})

// Attach request context in middleware
ctx = logz.WithRequestID(ctx, requestID)
ctx = logz.WithField(ctx, "user_id", userID)

// Enrich logger from context in handlers
reqLogger := logger.WithContext(ctx)
reqLogger.Info("handling request", "path", r.URL.Path)

// Error enrichment is automatic — no extra code needed
reqLogger.Error("query failed", err) // appends error_code and context fields

Environment variables:

Variable Default Effect
EINHERJAR_LOG_LEVEL INFO Minimum log level (DEBUG, INFO, WARN, ERROR)
EINHERJAR_LOG_JSON false JSON output when true
Errors
import "code.nochebuena.dev/einherjar/core/xerrors"

// Named constructors for common cases
err := xerrors.NotFound("user %s not found", userID)
err := xerrors.InvalidInput("email is required")
err := xerrors.Aborted("order modified by another session")

// Builder pattern for structured context
err := xerrors.New(xerrors.ErrInvalidInput, "validation failed").
    WithContext("field", "email").
    WithContext("rule", "required").
    WithError(cause)

// Inspecting errors
var e *xerrors.Err
if errors.As(err, &e) {
    switch e.Code() {
    case xerrors.ErrNotFound:       // HTTP 404
    case xerrors.ErrUnauthorized:   // HTTP 401
    case xerrors.ErrInvalidInput:   // HTTP 400
    }
}
Validation
import "code.nochebuena.dev/einherjar/core/valid"

type CreateUserReq struct {
    Email string `json:"email" validate:"required,email"`
    Age   int    `json:"age"   validate:"gte=18"`
}

v := valid.New() // English messages
// v := valid.New(valid.WithMessageProvider(valid.SpanishMessages))

if err := v.Struct(req); err != nil {
    var xe *xerrors.Err
    errors.As(err, &xe)
    // xe.Code()    == xerrors.ErrInvalidInput
    // xe.Fields()  == {"field": "email", "tag": "required"}
    // xe.Message() == "field 'email' is required"
}

All go-playground/validator built-in tags have specific messages in both DefaultMessages and SpanishMessages. The generic fallback only fires for unknown tags.

Custom validators
import "strings"

v := valid.New(
    valid.WithCustomValidator("nohttp", func(fl valid.FieldLevel) bool {
        return !strings.HasPrefix(fl.Field().String(), "http://")
    }),
    valid.WithMessageProvider(valid.OverrideProvider(
        map[string]func(field, param string) string{
            "nohttp": func(field, _ string) string {
                return fmt.Sprintf("field '%s' must not use http://", field)
            },
        },
        valid.DefaultMessages,
    )),
)

OverrideProvider chains a tag→message map with a fallback provider, so custom tag messages are handled without re-implementing all built-ins.


Error Codes

xerrors provides the full gRPC canonical error code set plus HTTP 410 (ErrGone):

Constant Wire value HTTP When to use
ErrInvalidInput INVALID_ARGUMENT 400 Malformed or invalid request data
ErrOutOfRange OUT_OF_RANGE 400 Valid value but outside accepted bounds
ErrUnauthorized UNAUTHENTICATED 401 Missing or invalid credentials
ErrPermissionDenied PERMISSION_DENIED 403 Authenticated but not authorised
ErrNotFound NOT_FOUND 404 Resource does not exist
ErrAlreadyExists ALREADY_EXISTS 409 Creation conflict (duplicate)
ErrAborted ABORTED 409 Concurrent modification; retry may succeed
ErrGone GONE 410 Resource permanently deleted
ErrPreconditionFailed FAILED_PRECONDITION 412 Business rule blocks the operation
ErrRateLimited RESOURCE_EXHAUSTED 429 Rate limit or quota exceeded
ErrCancelled CANCELLED 499 Request cancelled by the caller
ErrInternal INTERNAL 500 Unexpected server-side failure
ErrDataLoss DATA_LOSS 500 Unrecoverable data corruption
ErrNotImplemented UNIMPLEMENTED 501 Operation not implemented
ErrUnavailable UNAVAILABLE 503 Service temporarily unavailable
ErrDeadlineExceeded DEADLINE_EXCEEDED 504 Operation timed out

Wire values are stable across versions and safe to persist, send over the network, or switch on in client code.


Dependency Rules

contracts  (zero dependencies)
    ↑
  core     (depends on contracts only)
    ↑
 starters  (depend on core + contracts)
    ↑
  your app

core imports contracts. Nothing above core in this chain may import core directly — they depend on starters, which compose core's sub-packages behind framework-specific APIs.


Verification

cd core/
go build ./...     # must compile clean
go vet ./...       # no warnings
go test ./...      # structural + behavioural compliance passes
gofmt -l .         # no output

Architecture Decisions

ADR Title
ADR-001 Four sub-packages in one Go module
ADR-002 logz adopts contracts/errs instead of private duck typing
ADR-003 Config naming convention and caarlos0/env tag standard

They were not chosen because they were the strongest. They were chosen because they understood what they were building toward.

Documentation

Overview

Package core provides the foundational implementations of the Einherjar framework contracts: lifecycle management, structured logging, structured errors, and struct validation.

Sub-packages:

  • launcher — application lifecycle orchestration (init → start → shutdown)
  • logz — structured, leveled logging backed by log/slog
  • xerrors — typed error codes with context enrichment
  • valid — struct validation with pluggable i18n messages

Directories

Path Synopsis
Package launcher orchestrates the application lifecycle.
Package launcher orchestrates the application lifecycle.
Package logz provides a structured, leveled logger backed by the standard library's log/slog package.
Package logz provides a structured, leveled logger backed by the standard library's log/slog package.
Package valid wraps github.com/go-playground/validator/v10 behind a minimal Validator interface, returning xerrors-typed errors with stable codes.
Package valid wraps github.com/go-playground/validator/v10 behind a minimal Validator interface, returning xerrors-typed errors with stable codes.
Package xerrors provides structured application errors with stable typed codes, cause chaining, and key-value context fields.
Package xerrors provides structured application errors with stable typed codes, cause chaining, and key-value context fields.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL