anetos

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: Apache-2.0 Imports: 31 Imported by: 0

README

Anetos

Anetos (Greek άνετος, "at ease, comfortable"; say AH-neh-tos). Module anetos.dev/anetos, command anetos.

A batteries-included Go web framework with the developer comfort of Laravel, built the Go way: typed, net/http-compatible, code generation instead of runtime magic, and a supervised runtime where HTTP, queue workers, pub/sub listeners and the scheduler run together in one binary.

Status: pre-alpha. v0.4.0 is tagged; the first public release is v0.5. APIs will change.

  • v0.1, the foundation: the kernel, configuration, runtime supervisor, HTTP layer, validation, data layer with relations, migrations, model code generation, views, sessions, forms, the CLI and testing helpers.
  • v0.2, the batteries: the cache, server-side sessions and rate limiting, authentication, social login, queues, events, pub/sub listeners, the scheduler, mail, file storage, plugins (anetos add), test fakes, N+1 detection and anetos make:auth; examples/saas runs them as one binary or split by role.
  • v0.3, search, AI and the starter experience: full-text search and search by meaning with hybrid ranking (PostgreSQL with pgvector, MariaDB 11.7+ or SQLite); AI with Anthropic, OpenAI (and compatible servers) and Gemini: typed calls, tools that run as the user, agents, stored conversations, streaming, budgets and a test fake (examples/assistant); roles and permissions, global and per team (examples/teams); internationalization, with Bangla, French and Spanish translations of the framework (examples/i18n); an audit log and soft deletes (examples/audit); the admin (module anetos.dev/anetos/admin, examples/admin) with two-factor sign-in; account settings; Google Cloud Storage; anetos build, a Dockerfile and a systemd unit (Deploy); a starter theme, make:crud and error pages in the app's layout; a security review (checklist, SECURITY.md) and a doctor command in every app; benchmarks against plain net/http, chi, Gin and Echo, with CI holding every change to allocation budgets.
  • v0.4, the API stack: anetos new --stack=api, an app that serves JSON only; accounts that sign in with API tokens, with two-factor codes and abilities; make:crud JSON endpoints with pages, sorting and filters; typed results with the route's status; an OpenAPI 3.1 description generated from the handlers, served and checked in a test (examples/bookmarks).

The tutorial builds an issue tracker step by step, and examples/tracker, the reference app, is a bigger one; the API tutorial builds examples/bookmarks, a JSON API. The docs are at docs.anetos.dev.

Documents

Developer experience

anetos new blog && cd blog # templ views in a starter theme (light/dark), sessions, CSRF, SQLite
go tool anetos make:crud Post title:string body:text published:bool  # pages to list, show, create, edit, delete
go tool anetos make:auth   # accounts: password, Google, GitHub, API tokens
go tool anetos make:admin  # an admin at /admin; make:admin:resource Post adds posts
go run . migrate
go tool anetos dev         # rebuild and reload on every change

go tool anetos build       # bin/blog: one static binary, everything in it
./bin/blog                 # everything: web, queue workers and the scheduler
./bin/blog run --only=http # or split by role when you scale
./bin/blog run --only=workers
./bin/blog doctor          # unsafe settings, pending migrations
./bin/blog help            # migrate, routes:list, your own commands, …
docker build -t blog .     # or the image, from the generated Dockerfile

anetos new shop --stack=api # or JSON only: routes under /api/v1, errors as problem details,
                            # an OpenAPI 3.1 description (openapi.json) a test keeps current;
                            # there, make:auth writes accounts that sign in with API tokens,
                            # and make:crud JSON endpoints (pages, sorting, filters)

License

Apache License 2.0. See LICENSE and NOTICE.

Documentation

Overview

Package anetos is a batteries-included web framework for Go.

Anetos is pre-alpha: APIs will change before v1.0. See docs/planning/roadmap.md for the plan and docs/design/design.md for the architecture.

An App ties together configuration (AppConfig, package config), a structured logger, a small typed service container (Provide, Resolve), [Provider]s that wire in functionality, and a supervised runtime (package supervisor) that runs long-lived components as goroutines and shuts them down gracefully.

app, err := anetos.New()
if err != nil {
	log.Fatal(err)
}
app.Use(&database.Provider{}) // illustrative
_ = app.Go("cache-warmer", warmCache)

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
if err := app.Run(ctx); err != nil {
	log.Fatal(err)
}

Concept guide: docs/site/concepts/application-lifecycle.md.

Example (Lifecycle)

Example_lifecycle shows the smallest complete application: configuration, a supervised background task, and a graceful shutdown.

package main

import (
	"context"
	"fmt"
	"io"

	"anetos.dev/anetos"
	"anetos.dev/anetos/config"
)

func main() {
	app, err := anetos.New(
		anetos.WithSource(config.Map{"APP_NAME": "demo", "APP_ENV": "development"}),
		anetos.WithLogOutput(io.Discard),
	)
	if err != nil {
		fmt.Println(err)
		return
	}

	app.OnShutdown("goodbye", func(context.Context) error {
		fmt.Println("shutdown hook ran")
		return nil
	})

	ctx, cancel := context.WithCancel(context.Background())
	_ = app.Go("ticker", func(taskCtx context.Context) error {
		fmt.Println("task started in", app.Config().Env)
		cancel() // in a real app, SIGTERM would do this
		<-taskCtx.Done()
		fmt.Println("task stopped")
		return nil
	})

	if err := app.Run(ctx); err != nil {
		fmt.Println(err)
	}
}
Output:
task started in development
task stopped
shutdown hook ran

Index

Examples

Constants

View Source
const DateLayout = time.DateOnly

DateLayout is the layout of a Date as text: ISO 8601, "2006-01-02".

Variables

View Source
var ErrNotProvided = errors.New("service not provided")

ErrNotProvided is wrapped by Resolve when no service of the requested type has been provided.

Functions

func Location added in v0.3.0

func Location(ctx context.Context) *time.Location

Location returns the time zone of the app in ctx (App.Location), or time.Local when ctx has no app.

func Logger added in v0.2.0

func Logger(ctx context.Context) *slog.Logger

Logger returns the logger of the app in ctx (App.Logger: LOG_LEVEL, LOG_FORMAT, with the app's name and environment), or slog.Default() when ctx has none. Jobs, listeners, scheduled tasks and handlers get contexts with the app in them, so they log with:

anetos.Logger(ctx).InfoContext(ctx, "invoice sent", "invoice", inv.ID)

(In handlers, c.Logger() adds the request ID and route.)

func Lookup

func Lookup[T any](a *App) (T, bool)

Lookup returns the service of type T and whether it was provided.

func MustResolve

func MustResolve[T any](a *App) T

MustResolve is like Resolve but panics if the service is missing. Use it only during Boot, where a missing service is a programming error.

func Now added in v0.2.0

func Now(ctx context.Context) time.Time

Now returns the time on the clock of the app in ctx (App.Now), or time.Now when ctx has none. Use it, rather than time.Now, for times tests may want to control:

order.PaidAt = anetos.Now(ctx)

func Provide

func Provide[T any](a *App, v T)

Provide registers v as the application's service of type T, replacing any previous value of that type. Providers typically call it in Register:

anetos.Provide[*sql.DB](app, db)
anetos.Provide[mail.Mailer](app, smtpMailer) // provide by interface type

The container is keyed by the static type T, so Provide[Mailer] and Provide[*SMTPMailer] are different entries. Prefer passing dependencies through constructors; use the container for wiring between providers. Provide is safe for concurrent use.

func Resolve

func Resolve[T any](a *App) (T, error)

Resolve returns the service of type T, or an error wrapping ErrNotProvided that names the missing type.

func Version added in v0.2.0

func Version() string

Version returns the version of the Anetos module the app is built with: from the binary's build information ("v0.2.3"), or, when the module is replaced by a directory or built from a commit rather than a release (a pseudo-version such as v0.1.1-0.20261001…), the version its source is heading for, such as "v0.4.0-dev". Plugins' version requirements are checked against it.

func VersionText added in v0.3.0

func VersionText() string

VersionText describes the binary, as the version command prints it: the app's version (anetos build --version, else the one Go records from git), the commit, the Anetos and Go versions. It needs no settings, so main can print it before setting the app up:

if len(os.Args) == 2 && os.Args[1] == "version" {
	fmt.Print(anetos.VersionText())
	return
}

It prints:

blog v1.2.0 (commit 1a2b3c4d5e6f, 2026-10-06T10:00:00Z)
Anetos v0.3.0
go1.26.8 linux/amd64

func WithClock added in v0.2.0

func WithClock(ctx context.Context, now func() time.Time) context.Context

WithClock returns ctx with now as its clock, for Now, for code without an app (a package's own tests).

Types

type App

type App struct {
	// contains filtered or unexported fields
}

App is an Anetos application: its configuration, logger, services, providers and supervised components.

Create one with New, add providers and components, then call App.Run. An App is safe for concurrent use.

func New

func New(opts ...Option) (*App, error)

New creates an App. Unless options say otherwise it loads configuration with config.Load (process environment, .env.<APP_ENV>, .env), binds and validates AppConfig, and builds a structured logger.

New returns an error listing every invalid or missing setting.

func (*App) AddCarrier added in v0.3.0

func (a *App) AddCarrier(c Carrier)

AddCarrier adds a value that queue jobs and async event listeners carry from the work that started them. It panics if the carrier is invalid or its name is taken, which are programming errors.

func (*App) AddCheck added in v0.3.0

func (a *App) AddCheck(c Check)

AddCheck adds a check to the app's doctor command. Checks run in the order they were added, the booted ones last, after doctor boots the app; checks added while it boots (by a plugin's Boot) run then too:

app.AddCheck(anetos.Check{Name: "billing", Run: func(ctx context.Context) []anetos.Finding {
	if cfg.TestMode && app.Config().Env.IsProduction() {
		return []anetos.Finding{{Severity: anetos.Problem, Message: "BILLING_TEST_MODE=true in production: nobody pays"}}
	}
	return nil
}})

It panics if c has no name or no Run, which is a programming error.

func (*App) AddCommand

func (a *App) AddCommand(c cmd.Command) error

AddCommand registers a command of the application binary (see App.Execute). It returns an error if the command is invalid or its name is taken.

func (*App) AddContextValue

func (a *App) AddContextValue(key, val any)

AddContextValue makes val available under key (as with context.WithValue) in every context the app hands out: providers' Boot, components and app.Go tasks, HTTP requests, shutdown hooks, and contexts passed through App.Context. Services use it so request and job code can reach them from a plain context.Context; the db package, for example, adds the database connection. Call it during Register; a value added in Boot reaches components, requests and hooks, but not the Boot contexts of providers that already ran.

func (*App) AroundUnits added in v0.2.0

func (a *App) AroundUnits(fn UnitFunc)

AroundUnits adds fn, which wraps every unit of work from now on: the server's requests, the queue's jobs, async and queued event listeners, pub/sub listeners, scheduled tasks and AI tool calls call App.StartUnit. db.Connect uses it to detect repeated queries.

func (*App) Boot

func (a *App) Boot(ctx context.Context) error

Boot runs every provider's Register, then every provider's Boot. It is called by App.Run if needed, and may be called directly (for example in tests or one-off commands that don't run components).

If any step fails, Boot runs the shutdown hooks registered so far, so resources opened by earlier providers are released, and returns the error.

func (*App) Booted

func (a *App) Booted() bool

Booted reports whether Boot has started (or the app has run or stopped): providers can no longer be added.

func (*App) Carried added in v0.3.0

func (a *App) Carried(ctx context.Context) map[string]string

Carried returns the values the app's carriers capture from ctx, nil if there are none. Packages that hand work off (queue, events) call it, and App.WithCarried where the work runs.

func (*App) Close

func (a *App) Close() error

Close runs the shutdown hooks of an app that was booted but not run, such as a one-off command that only needed its services. It returns an error if Run is in progress (cancel Run's context instead) and does nothing if the app has already stopped.

func (*App) Command

func (a *App) Command(name, description string, run func(ctx context.Context, args *cmd.Args) error)

Command registers a custom command, run with the app booted:

app.Command("reports:send", "Email the weekly report", func(ctx context.Context, args *cmd.Args) error {
	return reports.Send(ctx)
})

It panics if the name is invalid or taken, which is a programming error.

func (*App) Commands

func (a *App) Commands() []cmd.Command

Commands returns the registered commands, built-in ones included, sorted by name.

func (*App) Component

func (a *App) Component(c supervisor.Component, opts ...ComponentOption) error

Component adds a long-running component, such as a custom server or consumer. Defaults: no roles, supervisor.StageBackground, supervisor.RestartNever.

func (*App) Config

func (a *App) Config() AppConfig

Config returns the application configuration.

func (*App) Context

func (a *App) Context(parent context.Context) context.Context

Context returns parent with the values added by App.AddContextValue, except those whose key parent already has a value for. Use it for contexts the app didn't create, such as in tests or one-off commands:

ctx := app.Context(context.Background())

func (*App) Execute

func (a *App) Execute()

Execute runs the command named by the program's arguments and exits:

func main() {
	app, err := anetos.New()
	…
	app.Execute()
}

With no arguments (or only flags) it runs the "run" command, which starts every component. "help" lists the commands. The context is canceled on SIGINT and SIGTERM. The exit status is 0 on success, 1 on errors and 2 for usage errors.

func (*App) ExecuteArgs

func (a *App) ExecuteArgs(ctx context.Context, args []string, stdout, stderr io.Writer) int

ExecuteArgs is App.Execute with explicit arguments and output, for tests. It returns the exit status.

func (*App) Go

func (a *App) Go(name string, fn func(ctx context.Context) error, opts ...ComponentOption) error

Go runs fn as a supervised background goroutine: panics are recovered and logged, ctx is canceled on shutdown (fn should return promptly then), and the task appears in supervisor.Supervisor.Status. Use it instead of a bare `go` statement so work is not silently lost on deploy.

Go may be called before Run or while running (for example from a request handler). Names must be unique among components; returning an error from fn records a failure. By default a failed task is not restarted.

func (*App) HasAroundUnits added in v0.2.0

func (a *App) HasAroundUnits() bool

HasAroundUnits reports whether App.AroundUnits added a function, so that code starting many units can skip building their names.

func (*App) Location added in v0.3.0

func (a *App) Location() *time.Location

Location returns the app's time zone (APP_TIMEZONE, default UTC).

func (*App) Logger

func (a *App) Logger() *slog.Logger

Logger returns the application logger.

func (*App) Now added in v0.2.0

func (a *App) Now() time.Time

Now returns the time on the app's clock, in the app's zone (App.Location): the system's, unless a test set another with App.SetClock (anetostest's Freeze and Travel). The framework reads the time an app can observe from it: model timestamps, expiry of sessions, tokens, signed URLs and cache items in memory, dates in validation rules, emails' Date. Durations, timeouts and the time kept by database and Redis servers stay real.

func (*App) OnShutdown

func (a *App) OnShutdown(name string, fn func(ctx context.Context) error)

OnShutdown registers fn to run after all components have stopped, in reverse registration order (so resources opened first are closed last). Hooks share a context limited by what remains of APP_SHUTDOWN_TIMEOUT (see AppConfig); each hook runs even if an earlier one fails. A hook registered while hooks are running (for example, by another hook) runs after the current round.

If components did not stop before the deadline, hooks still run so resources are released, even though a stuck component may still be using them.

func (*App) Run

func (a *App) Run(ctx context.Context, roles ...string) error

Run boots the app if necessary, then runs the components selected by roles (all of them if roles is empty) until ctx is canceled or a component escalates a failure. After the components stop, shutdown hooks run.

A typical main function:

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
if err := app.Run(ctx); err != nil {
	app.Logger().Error("app stopped with error", "error", err)
	os.Exit(1)
}

Run may be called only once.

func (*App) SetClock added in v0.2.0

func (a *App) SetClock(now func() time.Time)

SetClock makes now the app's clock, for tests (anetostest's Freeze and Travel use it); nil restores the system clock. It applies at once, also to contexts made before.

func (*App) Source

func (a *App) Source() config.Source

Source returns the configuration source, for binding other config structs:

mailCfg, err := config.Get[MailConfig](app.Source())

func (*App) StartUnit added in v0.2.0

func (a *App) StartUnit(ctx context.Context, u Unit) (context.Context, func())

StartUnit runs the App.AroundUnits functions for u, in the order they were added, and returns the context for the unit and the function to call when it ends (which ends them in reverse order). Packages that run units of work call it; with no functions, it returns ctx and a no-op.

func (*App) Supervisor

func (a *App) Supervisor() *supervisor.Supervisor

Supervisor returns the runtime supervisor, for health checks and status.

func (*App) Use

func (a *App) Use(providers ...Provider)

Use adds providers. Call it before App.Boot or App.Run; calling it later panics, because the providers would never be registered.

func (*App) WithCarried added in v0.3.0

func (a *App) WithCarried(ctx context.Context, values map[string]string) context.Context

WithCarried returns ctx with the carried values restored by their carriers. Values no carrier of the app knows are ignored.

type AppConfig

type AppConfig struct {
	// Name identifies the application in logs. APP_NAME, default "anetos".
	Name string `env:"APP_NAME" default:"anetos"`

	// Env is the deployment environment. APP_ENV, default "production", so a
	// missing setting fails safe.
	Env Environment `env:"APP_ENV" default:"production"`

	// URL is the app's public base URL ("https://example.com"), for links
	// that leave the app: OAuth callbacks, links in emails (mailer.URL).
	// APP_URL; empty until set, and the features that need it say so.
	URL string `env:"APP_URL"`

	// Debug enables diagnostics that must never reach production users.
	// APP_DEBUG, default false. Not allowed together with APP_ENV=production.
	Debug bool `env:"APP_DEBUG"`

	// ShutdownTimeout is the total budget for graceful shutdown: components
	// first, then shutdown hooks. Hooks always get at least the smaller of 5s
	// and a fifth of the budget, which components may not use.
	// APP_SHUTDOWN_TIMEOUT, default 30s (Kubernetes' default grace period).
	// Set it at or below your platform's grace period.
	ShutdownTimeout time.Duration `env:"APP_SHUTDOWN_TIMEOUT" default:"30s"`

	// Key encrypts and authenticates session cookies and other data: 32
	// random bytes written as "base64:…". APP_KEY. Required by the features
	// that use it (sessions), which fail at startup without it. Keep it
	// secret; changing it logs everyone out unless the old key is kept in
	// APP_PREVIOUS_KEYS.
	Key Secret `env:"APP_KEY"`

	// PreviousKeys still decrypt data written with them, so keys can be
	// rotated without logging everyone out. APP_PREVIOUS_KEYS,
	// comma-separated.
	PreviousKeys []Secret `env:"APP_PREVIOUS_KEYS"`

	// TimeZone is the app's time zone, an IANA name ("Asia/Dhaka"):
	// the process's local zone, the zone of [Now] and the default of
	// SCHEDULE_TIMEZONE. Times are stored in UTC whatever it is.
	// APP_TIMEZONE, default UTC; empty means UTC.
	TimeZone string `env:"APP_TIMEZONE" default:"UTC"`

	// Log configures the default logger. LOG_* variables.
	Log LogConfig `prefix:"LOG_"`
}

AppConfig is the framework's own configuration, read from APP_* and LOG_* variables. See docs/site/reference/configuration.md for every key.

The zero value is not valid; use DefaultAppConfig or let New load it.

func DefaultAppConfig

func DefaultAppConfig() AppConfig

DefaultAppConfig returns the configuration used when no variables are set.

func (AppConfig) Validate

func (c AppConfig) Validate() error

Validate implements config.Validator.

type Carrier added in v0.3.0

type Carrier struct {
	// Name identifies the value: lowercase letters, digits and . _ -, up
	// to 50 characters, unique in the app.
	Name string
	// Capture returns the value to carry from ctx, "" for none.
	Capture func(ctx context.Context) string
	// Restore returns ctx with the value restored.
	Restore func(ctx context.Context, value string) context.Context
}

Carrier moves a value from the context of work that hands off to the context of the work it starts: a queue job keeps it from the code that dispatched it, an async event listener from the code that emitted the event. Package audit carries the actor this way, so a job's changes are attributed to the user who asked for them.

Carried values travel as text, in the queue's storage among other places: don't carry secrets.

type Check added in v0.3.0

type Check struct {
	// Name says what it checks, as doctor prints it: "app", "db",
	// "session".
	Name string
	// Booted runs it after the app has booted, for checks that need a
	// service: the database's connection, say. Other checks run first,
	// so doctor reports them even when the app can't boot.
	Booted bool
	// Run returns what it found: nothing when all is well. The context
	// has the app's values (see [App.Context]).
	Run func(ctx context.Context) []Finding
}

Check is a check of the app's settings that the doctor command runs (v0.3). Features add theirs when they are set up (session.ForApp checks SESSION_SECURE, migrate.ForApp checks for pending migrations); an app or plugin can add its own with App.AddCheck.

type ComponentOption

type ComponentOption func(*supervisor.Spec)

ComponentOption customizes how App.Go and App.Component supervise a component.

func Backoff

Backoff configures restart delays for supervisor.RestartOnFailure.

func Restart

func Restart(policy supervisor.Restart) ComponentOption

Restart sets the failure policy. Default supervisor.RestartNever.

func Roles

func Roles(roles ...string) ComponentOption

Roles assigns roles to the component, so `run --only=<role>` can select it. Components without roles run in every process.

func Stage

func Stage(stage supervisor.Stage) ComponentOption

Stage sets the shutdown stage. Default supervisor.StageBackground.

type Date added in v0.3.0

type Date struct {
	Year  int        // the year, 2026
	Month time.Month // the month, January = 1
	Day   int        // the day of the month, from 1
}

Date is a calendar date: a year, month and day, without a time of day or a time zone. Use it for birthdays, due dates and DATE columns: a date kept in a time.Time is a midnight somewhere, which is another day in UTC (midnight in Dhaka is 18:00 the day before), and the database stores times in UTC.

A Date is a column of a model (written as "2006-01-02" text, read from text or from the time the driver returns), binds from form and query values ("2026-10-03", what <input type="date"> sends), and is a JSON string. The zero Date is no date: it is written as NULL, shown as "", and "" reads as it. The date rules of package validate (after, before, …) compare Dates.

func DateOf added in v0.3.0

func DateOf(t time.Time) Date

DateOf returns the date of t in t's own zone. Convert t first to see it elsewhere: DateOf(t.In(loc)).

func NewDate added in v0.3.0

func NewDate(year int, month time.Month, day int) Date

NewDate returns the date year-month-day, normalized as time.Date normalizes (October 32 is November 1).

func ParseDate added in v0.3.0

func ParseDate(s string) (Date, error)

ParseDate parses "2006-01-02". An empty string is the zero Date.

func Today added in v0.3.0

func Today(ctx context.Context) Date

Today returns today's date in the app's zone (APP_TIMEZONE), on the app's clock (Now).

func (Date) AddDate added in v0.3.0

func (d Date) AddDate(years, months, days int) Date

AddDate returns d plus the given years, months and days, normalized as time.Time.AddDate normalizes (January 31 plus a month is March 3, or March 2 in a leap year).

func (Date) AddDays added in v0.3.0

func (d Date) AddDays(n int) Date

AddDays returns d plus n days (minus, for a negative n).

func (Date) After added in v0.3.0

func (d Date) After(e Date) bool

After reports whether d is after e.

func (Date) Before added in v0.3.0

func (d Date) Before(e Date) bool

Before reports whether d is before e.

func (Date) Compare added in v0.3.0

func (d Date) Compare(e Date) int

Compare returns -1 if d is before e, +1 if after, and 0 if they are the same day. Fields out of range count as the day they stand for (October 32 is November 1).

func (Date) DaysSince added in v0.3.0

func (d Date) DaysSince(e Date) int

DaysSince returns the number of days from e to d: positive when d is later.

func (Date) In added in v0.3.0

func (d Date) In(loc *time.Location) time.Time

In returns the start of d (midnight) in loc.

func (Date) IsValid added in v0.3.0

func (d Date) IsValid() bool

IsValid reports whether d is a day of the calendar (not February 30) in the years 1 to 9999, which every database stores.

func (Date) IsZero added in v0.3.0

func (d Date) IsZero() bool

IsZero reports whether d is the zero Date (no date).

func (Date) MarshalText added in v0.3.0

func (d Date) MarshalText() ([]byte, error)

MarshalText returns d as "2006-01-02", or nothing for the zero Date. A date that isn't valid (Date.IsValid) is an error.

func (*Date) Scan added in v0.3.0

func (d *Date) Scan(src any) error

Scan reads a date from a time (its day in its own zone: drivers return DATE columns as midnight UTC) or from text "2006-01-02", possibly followed by a time (SQLite); NULL, and the zero time (MySQL's 0000-00-00), are the zero Date.

func (Date) String added in v0.3.0

func (d Date) String() string

String returns d as "2006-01-02", or "" for the zero Date.

func (*Date) UnmarshalText added in v0.3.0

func (d *Date) UnmarshalText(b []byte) error

UnmarshalText parses "2006-01-02"; empty text is the zero Date.

func (Date) Value added in v0.3.0

func (d Date) Value() (driver.Value, error)

Value writes d as "2006-01-02" text, or NULL for the zero Date. A date that isn't valid (Date.IsValid) is an error.

func (Date) Weekday added in v0.3.0

func (d Date) Weekday() time.Weekday

Weekday returns d's day of the week.

type Environment

type Environment string

Environment names the deployment environment (APP_ENV).

const (
	Development Environment = "development"
	Testing     Environment = "testing"
	Staging     Environment = "staging"
	Production  Environment = "production"
)

Supported environments.

func (Environment) Deployed added in v0.3.0

func (e Environment) Deployed() bool

Deployed reports whether the environment is one the world reaches, production or staging: where the doctor's checks of deployment settings apply.

func (Environment) IsDevelopment

func (e Environment) IsDevelopment() bool

IsDevelopment reports whether e is Development.

func (Environment) IsProduction

func (e Environment) IsProduction() bool

IsProduction reports whether e is Production.

func (Environment) IsTesting

func (e Environment) IsTesting() bool

IsTesting reports whether e is Testing.

type Finding added in v0.3.0

type Finding struct {
	// Severity says how much it matters.
	Severity Severity
	// Message says what is wrong and what to do, naming the setting:
	// "SESSION_SECURE=false in production: session cookies go over plain
	// HTTP; remove the setting".
	Message string
}

Finding is what a Check found.

type LogConfig

type LogConfig struct {
	// Level is the minimum level: debug, info, warn or error.
	// LOG_LEVEL, default info.
	Level slog.Level `env:"LEVEL" default:"info"`

	// Format is "text", "json", or empty for automatic: JSON in production,
	// text elsewhere. LOG_FORMAT.
	Format string `env:"FORMAT"`
}

LogConfig configures the default structured logger.

type Option

type Option func(*options)

Option configures New.

func WithAppConfig

func WithAppConfig(cfg AppConfig) Option

WithAppConfig uses cfg as the application configuration instead of reading APP_* and LOG_* variables. It is still validated.

func WithConfigDir

func WithConfigDir(dir string) Option

WithConfigDir sets the directory searched for .env files. Default ".".

func WithLogOutput

func WithLogOutput(w io.Writer) Option

WithLogOutput sets where the default logger writes. Default os.Stderr.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger replaces the default logger built from LogConfig.

func WithSource

func WithSource(src config.Source) Option

WithSource makes New read configuration from src instead of calling config.Load. Tests use it with a config.Map for isolation.

type Provider

type Provider interface {
	// Name identifies the provider in logs and errors.
	Name() string
	// Register adds the provider's services and commands to a.
	Register(a *App) error
	// Boot starts what the provider needs, when the app boots.
	Boot(ctx context.Context, a *App) error
}

Provider packages a piece of functionality (a database, a mailer, a set of routes, a plugin) and wires it into an App.

Providers run in two phases, in the order they were added with App.Use:

  • Register: provide services and read configuration. No I/O, no goroutines. Every provider's Register runs before any Boot, so Boot may rely on services from providers added later.
  • Boot: open connections, add components, register shutdown hooks.

The public plugin API (roadmap B11) builds on this interface.

type Secret

type Secret string

Secret is a configuration value that must never be printed or logged, such as APP_KEY. Its String, GoString, MarshalJSON and LogValue methods all hide it, so fmt, encoding/json and log/slog show "[redacted]". Convert it to a string to use it: string(cfg.Key).

func (Secret) GoString

func (s Secret) GoString() string

GoString hides the secret from %#v.

func (Secret) LogValue

func (s Secret) LogValue() slog.Value

LogValue hides the secret from log/slog.

func (Secret) MarshalJSON

func (s Secret) MarshalJSON() ([]byte, error)

MarshalJSON hides the secret from encoding/json.

func (Secret) String

func (s Secret) String() string

String returns "[redacted]", or "" for an empty secret.

type Severity added in v0.3.0

type Severity int

Severity says how much a doctor Finding matters.

const (
	// Note is for information: a choice worth knowing about, nothing to
	// fix.
	Note Severity = iota
	// Warning is a setting that is often a mistake: look at it.
	Warning
	// Problem is a setting that is unsafe or broken: fix it. The doctor
	// command exits 1.
	Problem
)

func (Severity) String added in v0.3.0

func (s Severity) String() string

String returns "note", "warning" or "problem".

type Unit added in v0.2.0

type Unit struct {
	// Kind is "request", "job", "listener", "message", "task" or "tool".
	Kind string
	// Name says which: "GET /posts/7", the job type's name, the
	// listener's, task's or tool's name.
	Name string
}

Unit is a unit of work the app runs: an HTTP request, a queue job, an event or pub/sub listener's handling of a message, a scheduled task, a model's call of a tool (package ai), which runs inside another unit.

type UnitFunc added in v0.2.0

type UnitFunc func(ctx context.Context, u Unit) (context.Context, func())

UnitFunc wraps units of work (App.AroundUnits): it returns the context the unit runs with, and a function called when it ends.

Directories

Path Synopsis
ai
Package ai connects language models to the app: text and typed answers, streaming, and tools that run as the current user.
Package ai connects language models to the app: text and typed answers, streaming, and tools that run as the current user.
aitest
Package aitest is the conformance suite of AI providers (ai.Provider): each driver module runs it, so every provider behaves alike for the ai package: text, streaming, conversations, tool calls, structured output, the token limit and errors.
Package aitest is the conformance suite of AI providers (ai.Provider): each driver module runs it, so every provider behaves alike for the ai package: text, streaming, conversations, tool calls, structured output, the token limit and errors.
Package anetostest tests Anetos applications: it boots the app with test settings and a clean database, sends requests through the router like a browser (cookies, CSRF tokens, the Referer), and asserts on responses, sessions, database rows, files, and the jobs, events, email and pub/sub messages the app sent (AssertDispatched, AssertEmitted, AssertMailSent, AssertPublished; faked with FakeQueue, FakeEvents, FakePubSub), and the model requests it made, with scripted answers (FakeAI, App.AssertPrompted).
Package anetostest tests Anetos applications: it boots the app with test settings and a clean database, sends requests through the router like a browser (cookies, CSRF tokens, the Referer), and asserts on responses, sessions, database rows, files, and the jobs, events, email and pub/sub messages the app sent (AssertDispatched, AssertEmitted, AssertMailSent, AssertPublished; faked with FakeQueue, FakeEvents, FakePubSub), and the model requests it made, with scripted answers (FakeAI, App.AssertPrompted).
Package audit keeps an audit log: who created, changed, deleted, restored and permanently deleted the rows of the models an app tracks, field by field, and the events the app records itself.
Package audit keeps an audit log: who created, changed, deleted, restored and permanently deleted the rows of the models an app tracks, field by field, and the events the app records itself.
Package auth signs users in and out, remembers them, finds the current user of a request, issues API tokens and password-reset and email-verification tokens, and checks typed policies.
Package auth signs users in and out, remembers them, finds the current user of a request, issues API tokens and password-reset and email-verification tokens, and checks typed policies.
password
Package password hashes and checks passwords: argon2id for new hashes, with bcrypt hashes (from another framework, say Laravel) still accepted.
Package password hashes and checks passwords: argon2id for new hashes, with bcrypt hashes (from another framework, say Laravel) still accepted.
rbac
Package rbac gives users roles and permissions, globally or in a scope such as a team, and checks them for the request's signed-in user.
Package rbac gives users roles and permissions, globally or in a scope such as a team, and checks them for the request's signed-in user.
social
Package social signs users in with an account at another service: Google, GitHub, or any OpenID Connect provider.
Package social signs users in with an account at another service: Google, GitHub, or any OpenID Connect provider.
Package cache keeps values for a while in a shared store (memory, the database, or Redis through drivers/redis), and provides locks across the app's instances.
Package cache keeps values for a while in a shared store (memory, the database, or Redis through drivers/redis), and provides locks across the app's instances.
cachetest
Package cachetest is a conformance suite for cache stores.
Package cachetest is a conformance suite for cache stores.
Package cmd defines the commands of an application binary: the built-in ones (run, serve, migrate, routes:list, …) and your own.
Package cmd defines the commands of an application binary: the built-in ones (run, serve, migrate, routes:list, …) and your own.
Package config loads application configuration from the environment and .env files into typed Go structs.
Package config loads application configuration from the environment and .env files into typed Go structs.
db
Package db is Anetos's data layer: connections, a typed query builder, model CRUD with timestamps, soft deletes and hooks, transactions, raw SQL scanned into structs, and pagination.
Package db is Anetos's data layer: connections, a typed query builder, model CRUD with timestamps, soft deletes and hooks, transactions, raw SQL scanned into structs, and pagination.
dbtest
Package dbtest is a conformance suite for db drivers.
Package dbtest is a conformance suite for db drivers.
factory
Package factory makes model values for tests and seeders:
Package factory makes model values for tests and seeders:
migrate
Package migrate changes database schemas with versioned migrations written in Go (or SQL), compiled into the app's binary, and fills them with seeders.
Package migrate changes database schemas with versioned migrations written in Go (or SQL), compiled into the app's binary, and fills them with seeders.
drivers
mysql module
postgres module
sqlite module
Package encryption encrypts and authenticates small messages, such as session cookies, with the application key (APP_KEY).
Package encryption encrypts and authenticates small messages, such as session cookies, with the application key (APP_KEY).
Package events delivers typed, in-process events to listeners, so the code that does something (place an order) doesn't need to know everything that follows from it (an email, a stock update, analytics).
Package events delivers typed, in-process events to listeners, so the code that does something (place an order) doesn't need to know everything that follows from it (an email, a stock update, analytics).
examples
i18n command
Command i18n shows Anetos's translations: catalogs in locales/ (English and Bangla), translated and pluralized messages, dates, prices and relative times in the visitor's language, the visitor's locale (?locale=, the cookie, the browser's languages), a language switcher, and validation messages in the visitor's language.
Command i18n shows Anetos's translations: catalogs in locales/ (English and Bangla), translated and pluralized messages, dates, prices and relative times in the visitor's language, the visitor's locale (?locale=, the cookie, the browser's languages), a language switcher, and validation messages in the visitor's language.
i18n/locales
Package locales holds the example's translations: en.yaml, and the files of the bn folder.
Package locales holds the example's translations: en.yaml, and the files of the bn folder.
lifecycle command
Command lifecycle is a minimal Anetos application: typed configuration, a provider, a supervised background task and graceful shutdown.
Command lifecycle is a minimal Anetos application: typed configuration, a provider, a supervised background task and graceful shutdown.
notes command
Command notes is a small JSON API built with Anetos's HTTP layer: routing, named routes, typed handlers with binding and validation, and error responses.
Command notes is a small JSON API built with Anetos's HTTP layer: routing, named routes, typed handlers with binding and validation, and error responses.
validation command
Command validation shows Anetos's validate package: tag rules, labels and messages, a custom rule, a Validate method for checks that need other data, and the 422 response a web.H handler returns.
Command validation shows Anetos's validate package: tag rules, labels and messages, a custom rule, a Validate method for checks that need other data, and the 422 response a web.H handler returns.
Package ext is the public plugin API.
Package ext is the public plugin API.
Package i18n translates an app's messages, the framework's included, into the user's language.
Package i18n translates an app's messages, the framework's included, into the user's language.
internal
appkey
Package appkey parses and generates APP_KEY values.
Package appkey parses and generates APP_KEY values.
cmd/doccheck command
Command doccheck lists exported identifiers of the public packages that have no doc comment: declarations, methods, struct fields and interface methods (the v0.1 exit criterion).
Command doccheck lists exported identifiers of the public packages that have no doc comment: declarations, methods, struct fields and interface methods (the v0.1 exit criterion).
cmd/docnav command
Command docnav checks the front matter that orders the docs site's sidebar (documentation guide §5.1): every page of getting-started, guides, concepts and reference, but the folders' README.md files, has a group and a weight; the weights of a folder's pages are distinct, and each group's are a run that no other group's interleave with, so that the site (anetos-dev/docs's sync) shows the groups in order, as folders of the sidebar.
Command docnav checks the front matter that orders the docs site's sidebar (documentation guide §5.1): every page of getting-started, guides, concepts and reference, but the folders' README.md files, has a group and a weight; the weights of a folder's pages are distinct, and each group's are a run that no other group's interleave with, so that the site (anetos-dev/docs's sync) shows the groups in order, as folders of the sidebar.
cmd/docsnippets command
Command docsnippets checks that code blocks in docs/site that claim to be copied from an example match that region exactly.
Command docsnippets checks that code blocks in docs/site that claim to be copied from an example match that region exactly.
cmd/genzones command
Command genzones writes i18n/zones.go, the time zones users choose from (i18n.TimeZones), from the IANA database's zone.tab: one zone per region of each country, sorted, with UTC first.
Command genzones writes i18n/zones.go, the time zones users choose from (i18n.TimeZones), from the IANA database's zone.tab: one zone per region of each country, sorted, with UTC first.
convert
Package convert turns strings into typed Go values.
Package convert turns strings into typed Go values.
dbutil
Package dbutil holds SQL helpers shared by the stores that keep their data in the app's database (the cache and the queue).
Package dbutil holds SQL helpers shared by the stores that keep their data in the app's database (the cache and the queue).
htmltext
Package htmltext turns HTML into readable plain text, for the text part of emails written in HTML: paragraphs and line breaks are kept, lists get "- " or numbers, links are followed by their URL in parentheses, images by their alt text; scripts, styles and the head are dropped.
Package htmltext turns HTML into readable plain text, for the text part of emails written in HTML: paragraphs and line breaks are kept, lists get "- " or numbers, links are followed by their URL in parentheses, images by their alt text; scripts, styles and the head are dropped.
httperr
Package httperr lets packages that package web imports (session) fail a request through the router's error handler: web sets Write to web.WriteError when it is initialized.
Package httperr lets packages that package web imports (session) fail a request through the router's error handler: web sets Write to web.WriteError when it is initialized.
jsonfield
Package jsonfield lists a struct's fields as encoding/json reads and writes them, and parses validate tags as package validate does: what the JSON Schemas of package ai and web/openapi are built from.
Package jsonfield lists a struct's fields as encoding/json reads and writes them, and parses validate tags as package validate does: what the JSON Schemas of package ai and web/openapi are built from.
naming
Package naming holds the naming rules the db package uses for columns and tables.
Package naming holds the naming rules the db package uses for columns and tables.
netaddr
Package netaddr has small helpers for host names and addresses.
Package netaddr has small helpers for host names and addresses.
socialstub
Package socialstub connects anetostest.FakeSocial to package auth/social: when the app provides a *Stub (anetos.Provide) before social.ForApp runs, in APP_ENV=testing, every provider signs in through the stand-in OpenID Connect provider it describes.
Package socialstub connects anetostest.FakeSocial to package auth/social: when the app provides a *Stub (anetos.Provide) before social.ForApp runs, in APP_ENV=testing, every provider signs in through the stand-in OpenID Connect provider it describes.
Package mailer sends email.
Package mailer sends email.
plugins
postmark module
Package pubsub publishes messages to topics of a message broker and runs listeners of its subscriptions: for streams other services share, such as "orders.created", where the queue package is for an app's own jobs.
Package pubsub publishes messages to topics of a message broker and runs listeners of its subscriptions: for streams other services share, such as "orders.created", where the queue package is for an app's own jobs.
pubsubtest
Package pubsubtest is a conformance suite for pub/sub brokers.
Package pubsubtest is a conformance suite for pub/sub brokers.
Package qr draws QR codes (ISO/IEC 18004): text as a square of dark and light modules, as SVG for a page.
Package qr draws QR codes (ISO/IEC 18004): text as a square of dark and light modules, as SVG for a page.
Package queue runs jobs in the background: typed structs, dispatched from a request (or anywhere), kept in a store and run by workers, with retries, backoff, timeouts and a record of the jobs that failed.
Package queue runs jobs in the background: typed structs, dispatched from a request (or anywhere), kept in a store and run by workers, with retries, backoff, timeouts and a record of the jobs that failed.
queuetest
Package queuetest is a conformance suite for queue stores.
Package queuetest is a conformance suite for queue stores.
Package schedule runs tasks on schedules (cron expressions, or helpers such as Daily and Every) inside the app, as a supervised component: no system cron, and a deploy of the binary deploys the schedule.
Package schedule runs tasks on schedules (cron expressions, or helpers such as Daily and Every) inside the app, as a supervised component: no system cron, and a deploy of the binary deploys the schedule.
Package session keeps per-visitor state across requests in an encrypted cookie: values, flash messages, the CSRF token, and the validation errors and form input a failed form post leaves for the next page.
Package session keeps per-visitor state across requests in an encrypted cookie: values, flash messages, the CSRF token, and the validation errors and form input a failed form post leaves for the next page.
Package storage keeps files on disks: a local directory (the default), memory, or an S3-compatible bucket (drivers/s3).
Package storage keeps files on disks: a local directory (the default), memory, or an S3-compatible bucket (drivers/s3).
storagetest
Package storagetest is the conformance suite of storage.Backend: every backend (local, memory, drivers/s3) runs it.
Package storagetest is the conformance suite of storage.Backend: every backend (local, memory, drivers/s3) runs it.
Package supervisor runs an application's long-running components (HTTP servers, queue workers, pub/sub listeners, the scheduler and background tasks) as goroutines in one process, and shuts them down gracefully.
Package supervisor runs an application's long-running components (HTTP servers, queue workers, pub/sub listeners, the scheduler and background tasks) as goroutines in one process, and shuts them down gracefully.
Package validate checks structs against rules written in `validate` struct tags and reports one message per field.
Package validate checks structs against rules written in `validate` struct tags and reports one message per field.
Package view renders HTML.
Package view renders HTML.
htmx
Package htmx bundles htmx (https://htmx.org), so pages get interactivity without a JavaScript build step, and its server-sent events extension (for streamed AI answers, say).
Package htmx bundles htmx (https://htmx.org), so pages get interactivity without a JavaScript build step, and its server-sent events extension (for streamed AI answers, say).
web
Package web is Anetos's HTTP layer: a router on top of net/http's ServeMux, a request context, typed handlers with automatic binding, consistent error responses, standard middleware and a supervised server.
Package web is Anetos's HTTP layer: a router on top of net/http's ServeMux, a request context, typed handlers with automatic binding, consistent error responses, standard middleware and a supervised server.
openapi
Package openapi describes an app's API as an OpenAPI 3.1 document, generated from its routes: each typed handler's (web.H) input (path, query and header parameters, the JSON body, with what their validate rules say), its result and the route's status (web.Route.Status), what its middleware asks for and may answer (web.Documented: package auth's Require is a bearer token), and errors as RFC 9457 problem details.
Package openapi describes an app's API as an OpenAPI 3.1 document, generated from its routes: each typed handler's (web.H) input (path, query and header parameters, the JSON body, with what their validate rules say), its result and the route's status (web.Route.Status), what its middleware asks for and may answer (web.Documented: package auth's Require is a bearer token), and errors as RFC 9457 problem details.
openapi/internal/other
Package other has a type named as one of the tests', for the components' names.
Package other has a type named as one of the tests', for the components' names.
ratelimit
Package ratelimit limits how often clients may do something: requests to routes, with Middleware, or actions in handlers, such as login attempts, with Allow.
Package ratelimit limits how often clients may do something: requests to routes, with Middleware, or actions in handlers, such as login attempts, with Allow.

Jump to

Keyboard shortcuts

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