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

setup

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: GPL-3.0 Imports: 17 Imported by: 0

Documentation

Overview

Package setup is the first-run wizard: the only thing this server serves until an operator has told it what it is.

Why there is a token

An operator runs this on a remote machine, so the port is reachable by strangers before an administrator account exists. Without a token, whoever loads the page first becomes the administrator. It is printed to the terminal only, never to a log, it is at least 128 bits from crypto/rand, it is compared in constant time, it is single-use, and a fresh one is minted on every restart so that a token left in an old scrollback is already dead.

Why the database decides whether this runs

The rule is not "no data directory means setup mode". A data directory is a cache of a connection string: it can be deleted, and on a container it can live on a volume that does not survive a restart. If its absence reopened the wizard, anyone who could delete it would be handed the administrator account of a database that already has one.

So a single row in the database marks completion, written in the same transaction that creates the first administrator, and the server asks the database at every startup. See github.com/pacenote-sim/server/internal/db.Store.CompleteSetup.

Index

Constants

View Source
const (
	TokenAttemptsBurst     = 5.0
	TokenAttemptsPerMinute = 1.0
)

TokenAttemptsBurst and TokenAttemptsPerMinute rate-limit the one endpoint that compares a secret. Five attempts, then one a minute: enough that an operator who mistypes is not locked out, and far too few for guessing to be worth starting.

View Source
const CSRFCookie = "pacenote_setup_csrf"

CSRFCookie is the other half of the double-submit check on every form.

View Source
const SessionCookie = "pacenote_setup"

SessionCookie carries the wizard session. It is not a credential to anything but the wizard, and it dies with the process.

View Source
const SessionTTL = 2 * time.Hour

SessionTTL is how long a half-finished wizard stays open.

Variables

This section is empty.

Functions

This section is empty.

Types

type Deps

type Deps struct {
	// Log receives the wizard's own lines. The token is never one of them.
	Log *slog.Logger
	// Version is the build, shown in the page header and recorded against the
	// completed setup.
	Version string
	// Token is the one-time setup token, already printed to the terminal.
	Token string
	// DataDir is where config.json will be written.
	DataDir string
	// Base is the configuration the server is running with: the listen
	// addresses, and a database URL if one came from the environment.
	Base config.Config
	// Done is signalled once, after the wizard has committed everything, to
	// tell the server to switch to normal mode. It must be buffered, so that
	// signalling never blocks the request that is still being written.
	Done chan<- struct{}
	// Now is the clock, injectable for tests.
	Now func() time.Time
	// OnAttempt is called after every setup token attempt with "accepted",
	// "rejected" or "rate_limited". It is how the metrics port sees that
	// someone is guessing.
	OnAttempt func(outcome string)
	// Probe tests a connection string and diagnoses what is wrong with it.
	// nil means [github.com/pacenote-sim/server/internal/db.Probe], which is what the server
	// uses; it is a field so that a test can drive the wizard without a
	// database, and so that the admin panel's "test connection" button can
	// reuse the same seam later.
	Probe func(ctx context.Context, url string) error
	// AlreadySetUp reports whether that database has already been through
	// setup. nil means ask the database, which is the rule: the database
	// decides, not the filesystem.
	AlreadySetUp func(ctx context.Context, url string) (bool, error)
}

Deps is everything the wizard needs from the rest of the server.

type Wizard

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

Wizard serves the first-run pages.

func New

func New(d Deps) (*Wizard, error)

New builds the wizard. It returns an error only if the embedded templates do not parse, which is a build fault rather than a runtime one.

func (*Wizard) Completed

func (w *Wizard) Completed() bool

Completed reports whether setup has finished in this process.

func (*Wizard) Handler

func (w *Wizard) Handler() http.Handler

Handler is the whole of what a server in setup mode serves: the wizard, the one stylesheet, and a plain page for everything else saying why there is nothing there.

Jump to

Keyboard shortcuts

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