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

clusterconfig

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: GPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package clusterconfig keeps the settings and identity material that the nodes of an Ether cluster share in the owner's PostgreSQL database:

  • the shared settings document (Shared, a config.SharedBackend),
  • sealed identity files such as the plex.direct certificate (Secrets),
  • one-time join tokens (jointoken.go),

and the bootstrap steps that turn a standalone server into the first cluster node (Init) and add further nodes (Join). Everything stored is sealed with the cluster's credential key, which travels only inside join tokens and never sits in the database in the clear.

The tables exist on SQLite too (migrations are dialect-neutral) but a standalone server never uses them.

Index

Constants

View Source
const DefaultJoinTokenTTL = time.Hour

DefaultJoinTokenTTL is how long a join token stays valid.

View Source
const JoinTokenPrefix = "ether-join-v1:"

JoinTokenPrefix starts every join token.

View Source
const TokenWarning = "" /* 374-byte string literal not displayed */

TokenWarning is shown with every new token.

Variables

View Source
var (
	// ErrTokenInvalid: the token is malformed or unknown to the cluster.
	ErrTokenInvalid = errors.New("clusterconfig: join token is not valid for this cluster")
	// ErrTokenUsed: the token was already used to join a node.
	ErrTokenUsed = errors.New("clusterconfig: join token has already been used")
	// ErrTokenExpired: the token's validity window has passed.
	ErrTokenExpired = errors.New("clusterconfig: join token has expired")
)

Functions

func Attach

func Attach(ctx context.Context, cfg *config.Store, db *sql.DB, o config.SharedOptions) error

Attach switches cfg to the cluster's shared settings in db, seeding them from cfg when the database holds none (the first node of a cluster). It is the boot step between opening the database and building components; see config.Store.AttachShared for the options.

func CheckSetup added in v0.2.4

func CheckSetup(ctx context.Context, s SetupRequest) error

CheckSetup checks connectivity and eligibility without migrations, writes or consuming a join token. Apply repeats the authoritative checks after shutdown.

func ConsumeJoinToken

func ConsumeJoinToken(ctx context.Context, db *sql.DB, t JoinToken, usedBy string, now time.Time) error

ConsumeJoinToken marks the token used by usedBy, atomically: of any number of concurrent attempts exactly one succeeds. A used, expired or unknown token fails with ErrTokenUsed, ErrTokenExpired or ErrTokenInvalid.

func CreateJoinToken

func CreateJoinToken(ctx context.Context, db *sql.DB, databaseURL string, key []byte, ttl time.Duration, now time.Time) (JoinToken, TokenInfo, error)

CreateJoinToken stores a new one-time token (only its hash) valid for ttl and returns it. databaseURL is the URL the joining node will use and key the cluster credential key; both travel only inside the token.

func Handler

func Handler(o HandlerOptions) http.Handler

Handler is the owner API mounted at /ether/api/cluster:

GET    /settings             shared-settings status and join tokens
POST   /join-tokens          create a one-time join token {ttlMinutes?, databaseUrl?}
DELETE /join-tokens/{id}     revoke an unused token

The admin package authenticates the owner; token creation additionally requires setup to be complete, so the unauthenticated LAN access the wizard allows before then can never mint a token.

func Init

func Init(ctx context.Context, o InitOptions) error

Init turns a stopped standalone server into the first node of a cluster: it copies every table of the local SQLite database into the empty PostgreSQL database (a logical export/import, as backups use), seeds the shared settings from config.json and points config.json at PostgreSQL. It refuses a database that already holds data.

func NewToken

func NewToken(ctx context.Context, db *sql.DB, cfg *config.Store, databaseURL string, ttl time.Duration) (JoinToken, TokenInfo, error)

NewToken creates a join token for the cluster whose database is db, embedding databaseURL and cfg's credential key.

func RevokeJoinToken

func RevokeJoinToken(ctx context.Context, db *sql.DB, id string) (bool, error)

RevokeJoinToken deletes an unused token; it reports whether one existed.

func SetupError added in v0.2.4

func SetupError(err error) string

SetupError never includes connection strings or join-token material.

Types

type HandlerOptions

type HandlerOptions struct {
	// DB is the cluster database; nil or SQLite means clustering is off.
	DB *sql.DB
	// Clustered reports whether this node runs against PostgreSQL.
	Clustered bool
	Config    *config.Store
	// DatabaseURL is the URL placed in new tokens (the node's own).
	DatabaseURL func() string
}

HandlerOptions configure the owner API.

type InitOptions

type InitOptions struct {
	// DataDir is the standalone server's data directory (config.json,
	// .credentials-key and ether.db).
	DataDir string
	// DatabaseURL is the empty PostgreSQL database the cluster will use.
	DatabaseURL string
	Logger      *slog.Logger
}

InitOptions configure Init.

type JoinOptions

type JoinOptions struct {
	// DataDir is the new node's data directory.
	DataDir string
	// Token is the join token string from an existing node.
	Token string
	// DatabaseURL overrides the URL in the token (e.g. the database is
	// reached under another name from this node).
	DatabaseURL string
	// Force moves an already configured server's settings aside.
	Force bool
	// NodeName is recorded as the token's user (default: host name).
	NodeName string
	Logger   *slog.Logger
	Now      func() time.Time
}

JoinOptions configure Join.

type JoinResult

type JoinResult struct {
	// MovedAside lists files renamed out of the way.
	MovedAside []string
	// MachineIdentifier is the cluster's server identity.
	MachineIdentifier string
}

JoinResult reports what Join did.

func Join

func Join(ctx context.Context, o JoinOptions) (JoinResult, error)

Join makes DataDir a node of the cluster the token belongs to: it checks the token's credential key against the cluster, consumes the token, then installs the key and writes a config.json holding node-local defaults (or this server's existing node-local settings) and the database URL. The node then boots with the cluster's shared settings.

type JoinToken

type JoinToken struct {
	DatabaseURL string `json:"d"`
	ID          string `json:"i"`
	Secret      string `json:"s"`
	Key         []byte `json:"k"`
}

JoinToken is what a join token string carries. It is a secret: it holds the database URL (with its password) and the cluster credential key.

func ParseJoinToken

func ParseJoinToken(s string) (JoinToken, error)

ParseJoinToken decodes a token string.

func (JoinToken) String

func (t JoinToken) String() string

String encodes the token as "ether-join-v1:" + base64url(JSON).

type Sealer

type Sealer interface {
	Seal(name string, plain []byte) (string, error)
	Unseal(name, sealed string) ([]byte, error)
}

Sealer encrypts values with the cluster credential key (*config.Store).

type Secrets

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

Secrets stores named, sealed blobs (identity files) shared by all nodes. Each name has a version that increases on every write.

func NewSecrets

func NewSecrets(db *sql.DB, sealer Sealer) *Secrets

NewSecrets returns a blob store sealing values with sealer.

func (*Secrets) Get

func (s *Secrets) Get(ctx context.Context, name string) ([]byte, int64, error)

Get returns the blob stored under name and its version; version 0 means nothing is stored.

func (*Secrets) Put

func (s *Secrets) Put(ctx context.Context, name string, data []byte) (int64, error)

Put stores data under name and returns the new version.

func (*Secrets) Version

func (s *Secrets) Version(ctx context.Context, name string) (int64, error)

Version is the stored version of name (0 when absent).

type SetupRequest added in v0.2.4

type SetupRequest struct {
	Mode        string `json:"mode"`
	DatabaseURL string `json:"databaseUrl"`
	Token       string `json:"token"`
	Replace     bool   `json:"replace"`
}

SetupRequest carries secrets only in memory until the stopped server applies it.

func (SetupRequest) URL added in v0.2.4

func (s SetupRequest) URL() string

func (SetupRequest) Validate added in v0.2.4

func (s SetupRequest) Validate() error

type Shared

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

Shared stores the shared settings document in clusterconfig_shared (one row, versioned for optimistic concurrency).

func NewShared

func NewShared(db *sql.DB) *Shared

NewShared returns the database-backed config.SharedBackend.

func (*Shared) LoadShared

func (s *Shared) LoadShared(ctx context.Context) (config.SharedDoc, bool, error)

LoadShared implements config.SharedBackend.

func (*Shared) SaveShared

func (s *Shared) SaveShared(ctx context.Context, d config.SharedDoc, expect int64) (int64, error)

SaveShared implements config.SharedBackend.

func (*Shared) SharedVersion

func (s *Shared) SharedVersion(ctx context.Context) (int64, error)

SharedVersion implements config.SharedBackend.

type TokenInfo

type TokenInfo struct {
	ID        string    `json:"id"`
	CreatedAt time.Time `json:"createdAt"`
	ExpiresAt time.Time `json:"expiresAt"`
	UsedAt    time.Time `json:"usedAt,omitzero"`
	UsedBy    string    `json:"usedBy,omitempty"`
}

TokenInfo describes a stored token (never its secret).

func ListJoinTokens

func ListJoinTokens(ctx context.Context, db *sql.DB) ([]TokenInfo, error)

ListJoinTokens returns stored tokens, newest first.

Jump to

Keyboard shortcuts

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