setup

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 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