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

adminpath

package
v1.0.2 Latest Latest
Warning

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

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

Documentation

Overview

Package adminpath holds the rules for the per-install admin address: its format, how strong it is, the list of addresses scanners try, and (in generate.go) the random generator. It is a leaf package: pure functions plus the Site interface the server fills in. It imports no database, server or admin code.

Index

Constants

This section is empty.

Variables

View Source
var ErrFormat = errors.New("not a valid admin address")

ErrFormat is wrapped by every Normalize failure.

View Source
var ErrNoSiteConfig = errors.New("adminpath: no site config row")

ErrNoSiteConfig means the singleton site config row does not exist yet (first boot seeds it).

View Source
var ReservedPrefixes = []string{
	"api", "p", "theme", "media", ".well-known", "sitemap.xml", "robots.txt", "health", "ready",
	"llms.txt", "plugin-ui", "feed", "feed.xml", "atom.xml", "sitemap", "forms", "auth", "-", "favicon.ico",
}

ReservedPrefixes are the first segments the site always serves (or must keep free) at the top level. The server test checks that every fixed route prefix appears here or is exempt. "admin" is deliberately absent: it is the legacy default and may be chosen as a weak address.

View Source
var ScannerList = []string{
	"admin", "administrator", "admins", "adm", "wp-admin", "wp-login", "wp", "wordpress",
	"login", "signin", "sign-in", "auth", "dashboard", "dash", "cms", "backend", "backoffice",
	"manage", "manager", "management", "panel", "controlpanel", "cpanel", "cp", "console", "portal",
	"user", "users", "account", "accounts", "member", "members", "ghost", "umbraco",
	"typo3", "joomla", "drupal", "craft", "hatamoto", "hm", "editor", "staff", "secure", "private",
	"root", "sysadmin", "webadmin", "siteadmin",
	"admin-panel", "adminpanel", "admin-login", "adminlogin", "moderator", "owner", "config", "setup", "install",
}

ScannerList holds the addresses that automated scanners try first.

Functions

func Generate

func Generate() (string, error)

Generate returns a random address of the form /xxxx-xxxx-xxxx: 12 Crockford base32 characters (60 bits) from crypto/rand. It draws again, up to 10 times, when Assess does not rank a draw strong (a rare run of neighbouring characters), and fails if none does.

func Normalize

func Normalize(s string) (string, error)

Normalize returns the canonical form of an admin address: lower case, one segment of 4 to 64 letters, digits or dashes after a leading slash, with no dash at either end.

Types

type DB

type DB interface {
	QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
	Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
}

DB is what Store needs; *pgxpool.Pool satisfies it.

type Level

type Level int

Level says how an address ranks. Higher is better; LevelInvalid and LevelReserved are refused outright, LevelWeak needs an explicit confirm.

const (
	LevelInvalid  Level = iota // malformed
	LevelReserved              // the site serves something at this address
	LevelWeak                  // guessable
	LevelOK                    // acceptable, random is stronger
	LevelStrong
)

func (Level) String

func (l Level) String() string

type Reason

type Reason struct{ Code, Text string }

Reason explains a level. Code is stable ("format", "scanner", "short", "dictionary", "site-name", "served", "reserved"); Text is for people.

type Result

type Result struct {
	Path    string
	Level   Level
	Reasons []Reason
	Bits    float64
}

Result is the outcome of Assess. Path is the normalised form when the input was well formed, else the trimmed input.

func Assess

func Assess(ctx context.Context, path string, site Site) Result

Assess ranks an admin address. site may be nil. The order is: a shape that is not a single segment is invalid; a first segment the site serves is reserved (checked before the length rule, so "/api" is reserved, not just too short); then the format rules; then the weak rules; then ok or strong.

type Site

type Site interface {
	// Serves reports whether the first segment of path collides with a URL
	// pattern, entity slug or redirect source, and says what serves it.
	Serves(ctx context.Context, path string) (bool, string)
	// Name is the site name, or "".
	Name() string
	// Domain is the host of the public base URL, or "".
	Domain() string
}

Site tells Assess what the running site serves and what it is called. Any method may be unhelpful (false, ""), and Assess accepts a nil Site.

type Store

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

Store reads and writes the admin address columns of hatamoto_site_config directly. It does not go through siteconfig.Update, whose COALESCE cannot set a column back to NULL.

func NewStore

func NewStore(db DB) *Store

func (*Store) Ack

func (s *Store) Ack(ctx context.Context, path, by string) error

Ack records that someone knowingly accepted path as weak, and when, without touching who chose it or when. It writes nothing, and returns ErrNoSiteConfig, when path is not the stored address.

func (*Store) Get

func (s *Store) Get(ctx context.Context) (Stored, error)

Get returns the stored address, or ErrNoSiteConfig when the row is missing.

func (*Store) Set

func (s *Store) Set(ctx context.Context, path, by string) error

Set stores path, records by as who chose it, and clears any acknowledgement.

func (*Store) SetAck

func (s *Store) SetAck(ctx context.Context, path, by string) error

SetAck stores path and records who knowingly accepted it as weak, and when. The same person is recorded as having chosen it.

func (*Store) SetIfPending

func (s *Store) SetIfPending(ctx context.Context, path string) (bool, error)

SetIfPending stores path only while none is stored, so replicas booting together agree on one value. It reports whether this call wrote it.

type Stored

type Stored struct {
	Path  *string
	AckBy *string
	AckAt *time.Time
	// SetBy and SetAt record who last chose the address and when. They are
	// nil for a server-generated address (SetIfPending) and for rows that
	// predate the columns.
	SetBy *string
	SetAt *time.Time
}

Stored is the persisted admin address. A nil Path means pending: nothing has been chosen yet.

Jump to

Keyboard shortcuts

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