Help us shape the future of Go! Take the 2026 Go Developer Survey

app

package
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package app assembles a running application from a configuration and a list of modules.

New checks the composition; Run migrates, opens the connection, and then does whichever half of the work this process's role names: web builds the API, proves that every operation declared its authorization and every event it publishes was promised, and serves; worker relays the outbox, consumes subscriptions, schedules the periodic jobs and answers the two probes; all does both in one process. The order is written here, once, in the order it happens — which is the whole argument of docs/adr/0002: a startup sequence that is read rather than derived.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bootstrap

func Bootstrap(ctx context.Context, cfg config.Config, mods []module.Module, fn func(context.Context, db.Tx[db.System]) error) error

Bootstrap migrates the database and runs fn in one cross-tenant transaction.

It is the only way an installation with no tenants gets its first one: every other write in the application happens inside a tenant, and a tenant is what this creates. The migration is part of it because an empty database is the ordinary case here — a person who has just cloned the repository has run `make up` and nothing else — and it is the same ledger the application applies, modules included, because a bootstrap that migrated less than the process it is bootstrapping would leave a schema the app then has to finish.

The lock makes "it refuses once any tenant exists" true of two bootstraps racing and not only of two run in sequence: both would otherwise read an empty tenants table in their own snapshot and both would create one, leaving two first tenants and two administrators who each believe they are the only one. pg_advisory_xact_lock serializes them, so the second reads the first one's tenant and refuses.

func MigrationSources

func MigrationSources(mods []module.Module) []db.MigrationSource

MigrationSources keeps the foundation first and modules in composition order. Module names own their histories; no global version allocation is needed. A module's Adopts travels with its files, so an installation migrated before the module owned its SQL is re-owned on the way past and nothing re-runs.

Types

type App

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

App is a composed application that has not started yet.

func New

func New(ctx context.Context, cfg config.Config, mods []module.Module, opts Options) (*App, error)

New checks the composition and returns the application it describes. Every error it can return is a wiring mistake, so they all surface before anything is opened, listened on or migrated.

func (*App) Run

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

Run migrates, then serves or works or both, and returns when ctx is done.

type Options

type Options struct {
	Tenants   httpx.TenantLoader
	Authorize httpx.Authorizer
	// Entitle answers what a tenant's plan includes, for the operations whose
	// declaration names a feature. It is optional: an application that sells
	// nothing declares no feature and is never asked. One that does declare a
	// feature and leaves this nil fails to start, which is httpx's check and
	// not this one.
	Entitle      httpx.Entitler
	Authenticate func(ctx context.Context, tx db.Tx[db.Tenant], r *http.Request) (tenancy.Principal, bool, error)
	Log          *slog.Logger

	// Role defaults to All.
	Role Role

	// Transport carries events between the relay and the handlers. An explicit
	// value overrides nats.transport and Transports. Otherwise All defaults to
	// memory and a separate Worker to JetStream; nats.transport can opt All
	// into JetStream, and Transports says how each is built.
	Transport events.Transport

	// Transports are the constructors behind nats.transport's two names. New
	// refuses a composition whose selected name has no constructor, so a
	// worker that would have found out at Run finds out before anything is
	// opened; the check runs in every role, because one image must answer
	// "will this start?" the same way whichever half it runs.
	Transports Transports
}

Options are the cross-cutting implementations main chooses: the three questions the kernel cannot answer for itself — which host is which tenant, who is calling, and what they may do — plus the role, the event transport constructors, the tenant list the periodic jobs walk, and where to log.

type Role

type Role string

Role is which half of the application a process runs. One binary and one image; the role is a flag. See docs/adr/0005.

const (
	// Web serves the API and nothing else.
	Web Role = "web"
	// Worker relays the outbox, consumes events and runs the periodic jobs.
	Worker Role = "worker"
	// All is both in one process, which is what a laptop and a small
	// deployment want. It is the default, so a deployment that says nothing
	// gets a whole application.
	All Role = "all"
)

type Transports added in v1.1.0

type Transports struct {
	// Memory builds the in-process transport the combined role defaults to.
	Memory func() events.Transport
	// JetStream builds the shared broker transport a separate role defaults
	// to, from the NATS settings New has already validated.
	JetStream func(config.NATS) (events.Transport, error)
}

Transports is how the two event transports nats.transport can name are built. The kernel knows the names and the rule between them — memory is one process talking to itself, so a separate worker cannot use it — and nothing about how either is made: the composing application supplies both and is the one importer of the provider packages, so kit/app links neither the NATS client nor the in-process queue (scripts/check_packages.sh records that). apps/platformkit passes memory.New and nats.Connect; a product that runs one process forever may leave JetStream nil and set nats.transport to memory, and is refused the day a separate worker is deployed against it.

Jump to

Keyboard shortcuts

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