console

package
v0.6.14 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 48 Imported by: 0

Documentation

Overview

Package console is backplane's console server (§11): the HTTP the shell talks to directly or through a deployment-owned reverse proxy.

POST /auth/login     {token} → session cookie (HttpOnly, SameSite=Strict)
POST /auth/logout    ends the session
GET  /auth/session   the current session, 401 without one
GET  /ws             ws-proto: backplane's own API (CatalogService,
                     SessionService, and what WithServices adds) and the
                     relay of everything else to the services' internal
                     API on their platform ports
GET  /plugins/<service>/<hash>/<path>
                     console plugin bundles, fetched from a live instance
                     and cached by content hash
GET  /               the shell

The console listens on its own address (Settings.Listen) from the start of its node to its stop, under Settings.Prefix (or at "/"). A reverse proxy must preserve this path and support WebSocket upgrades.

Index

Constants

View Source
const (
	// SessionTTL is the absolute lifetime of a session.
	SessionTTL = 12 * time.Hour
	// IdleTimeout ends a session without activity (a login, a request with
	// its cookie, an RPC on its connection).
	IdleTimeout = time.Hour
)

Session lifetime (§11.3).

View Source
const (
	// SecretHeader carries the installation's internal secret.
	SecretHeader = "bp-internal-secret" //nolint:gosec // a header name, not a credential
	// SessionHeader names the console session the call comes from.
	SessionHeader = "bp-console-session"
)

Metadata the relay sets on a forwarded call (§11.1, §17).

View Source
const (
	// CookieName is the session cookie.
	CookieName = "bp_session"
)
View Source
const (
	// MinAdminTokenLen is the shortest admin token accepted, in bytes.
	MinAdminTokenLen = 16
)
View Source
const (
	// SecretHTTPHeader carries the internal secret to a platform port.
	SecretHTTPHeader = "Bp-Internal-Secret" //nolint:gosec // a header name, not a credential

)

Variables

View Source
var (
	// ErrNoAdminToken: no admin token is configured.
	ErrNoAdminToken = errors.New("console: no admin token configured")
	// ErrShortAdminToken: the admin token is shorter than MinAdminTokenLen.
	ErrShortAdminToken = fmt.Errorf("console: admin token shorter than %d characters", MinAdminTokenLen)
)

Errors of Settings.Validate.

View Source
var ErrAttempts = errors.New("console: login attempts unavailable")

ErrAttempts: the login attempts cannot be counted; the login is refused.

View Source
var (
	// ErrNoSession: no such session.
	ErrNoSession = errors.New("console: no such session")
)

Errors of Sessions.

Functions

func SessionID

func SessionID(ctx context.Context) (uuid.UUID, bool)

SessionID is the console session a call on /ws comes from: backplane's own handlers read it from their context (the metadata bp-console-session carries it too when the call has any headers).

Types

type Attempts

type Attempts interface {
	// Allow takes one attempt from addr; refused, it is how long to wait.
	Allow(ctx context.Context, addr string) (time.Duration, error)
	// Failed records a wrong token from anywhere.
	Failed(ctx context.Context) error
	// Succeeded resets the global backoff.
	Succeeded(ctx context.Context) error
}

Attempts counts login attempts for brute force protection (§11.3), shared by every replica: PostgreSQL in the server, memory in tests. When they cannot be counted the login is refused.

type Console

type Console struct {
	deps.Component
	// contains filtered or unexported fields
}

Console is the console server: a component whose start checks the admin token is configured and opens the listener, whose goroutine deletes stale sessions and closes relay connections of gone instances, and whose stop shuts the listener down and closes the open /ws connections.

A Console is shared by pointer: it holds the connections, the relay pool and the bundle cache.

func New

func New(
	parent deps.Scope, s Settings, sessions Sessions, attempts Attempts, src registry.Source, opts ...Option,
) *Console

New creates the console under parent: sessions keep the sessions, attempts count logins across replicas, src is the installation. Its start fails when s does not pass Validate.

func (*Console) Addr

func (c *Console) Addr() net.Addr

Addr is the listener's address once started; nil without one.

func (*Console) Handler

func (c *Console) Handler() http.Handler

Handler serves the console under Route (the prefix included).

func (*Console) Route

func (c *Console) Route() string

Route is where the console is mounted: "/" or "<prefix>/".

type LoginLimits

type LoginLimits struct {
	// Every address has Burst attempts per Window.
	Burst  int
	Window time.Duration
	// After Threshold failures (any address, none older than Forget apart)
	// every login waits Base, doubling per further failure up to Max; a
	// success resets it.
	Threshold int
	Base, Max time.Duration
	Forget    time.Duration
}

LoginLimits of shared login attempts.

func DefaultLoginLimits

func DefaultLoginLimits() LoginLimits

DefaultLoginLimits: 5 attempts a minute per address; after 10 failures in a row 1s, doubling up to a minute.

type Option

type Option func(o *options)

Option configures New.

func WithCacheBytes

func WithCacheBytes(n int64) Option

WithCacheBytes caps the plugin bundle cache (default 64 MiB).

func WithClock

func WithClock(now func() time.Time) Option

WithClock replaces time.Now (tests).

func WithServices

func WithServices(register func(r grpc.ServiceRegistrar)) Option

WithServices registers more of backplane's own API on /ws (another component's gRPC services, e.g. configuration).

func WithSessionTelemetry

func WithSessionTelemetry(handler http.Handler) Option

WithSessionTelemetry installs log admission behind console session and origin checks, without requiring a public ingest key in the frontend.

func WithShell

func WithShell(fsys fs.FS) Option

WithShell serves the console's frontend from fsys (index.html for every path that is not a file); without it "/" answers a placeholder.

func WithTelemetry

func WithTelemetry(handler http.Handler) Option

WithTelemetry installs independent OTLP admission without operator auth.

type PG

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

PG keeps sessions in PostgreSQL (schema backplane): console_session.

func NewPG

func NewPG(st deps.Dependency[*store.Store]) PG

NewPG is the PostgreSQL Sessions over the store; the store is read once it is provided (from the console's start on).

func (PG) ByToken

func (p PG) ByToken(ctx context.Context, tokenHash []byte) (Session, error)

ByToken implements Sessions.

func (PG) Create

func (p PG) Create(ctx context.Context, tokenHash []byte, s Session) (uuid.UUID, error)

Create implements Sessions.

func (PG) Delete

func (p PG) Delete(ctx context.Context, id uuid.UUID) (bool, error)

Delete implements Sessions.

func (PG) DeleteOthers

func (p PG) DeleteOthers(ctx context.Context, keep uuid.UUID) (int64, error)

DeleteOthers implements Sessions.

func (PG) DeleteStale

func (p PG) DeleteStale(ctx context.Context, now, idleSince time.Time) (int64, error)

DeleteStale implements Sessions.

func (PG) Get

func (p PG) Get(ctx context.Context, id uuid.UUID) (Session, error)

Get implements Sessions.

func (PG) List

func (p PG) List(ctx context.Context) ([]Session, error)

List implements Sessions.

func (PG) Touch

func (p PG) Touch(ctx context.Context, id uuid.UUID, at time.Time) error

Touch implements Sessions.

type PGAttempts

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

PGAttempts counts attempts in the platform's PostgreSQL schema. A database outage refuses logins; there is no per-replica fallback.

func NewPGAttempts

func NewPGAttempts(st deps.Dependency[*store.Store], limits LoginLimits) PGAttempts

func (PGAttempts) Allow

func (p PGAttempts) Allow(ctx context.Context, address string) (time.Duration, error)

func (PGAttempts) Failed

func (p PGAttempts) Failed(ctx context.Context) error

func (PGAttempts) Prune

func (p PGAttempts) Prune(ctx context.Context) error

Prune removes expired IP counters in bounded batches, skipping rows used by concurrent logins. Every replica may run it safely.

func (PGAttempts) Succeeded

func (p PGAttempts) Succeeded(ctx context.Context) error

type Session

type Session struct {
	ID         uuid.UUID
	CreatedAt  time.Time
	ExpiresAt  time.Time
	LastSeenAt time.Time
	Address    string
	UserAgent  string
}

Session is one login.

func (Session) IsZero

func (s Session) IsZero() bool

IsZero reports whether s is the zero Session (no session).

type Sessions

type Sessions interface {
	// Create stores a session under the hash of its token; s.ID is ignored
	// and the new id returned.
	Create(ctx context.Context, tokenHash []byte, s Session) (uuid.UUID, error)
	// ByToken finds a session by the hash of its token; ErrNoSession.
	ByToken(ctx context.Context, tokenHash []byte) (Session, error)
	// Get finds a session by id; ErrNoSession.
	Get(ctx context.Context, id uuid.UUID) (Session, error)
	// Touch moves last seen forward to at (never back).
	Touch(ctx context.Context, id uuid.UUID, at time.Time) error
	// List is every session, newest first.
	List(ctx context.Context) ([]Session, error)
	// Delete removes a session; false when there was none.
	Delete(ctx context.Context, id uuid.UUID) (bool, error)
	// DeleteOthers removes every session but keep and reports how many.
	DeleteOthers(ctx context.Context, keep uuid.UUID) (int64, error)
	// DeleteStale removes sessions expired at now or idle since idleSince.
	DeleteStale(ctx context.Context, now, idleSince time.Time) (int64, error)
}

Sessions is where the sessions live: PostgreSQL (PG) in the server, memory in tests.

type Settings

type Settings struct {
	// Listen is the console's own listener (":8081"); empty: none (the
	// Handler is served by someone else, e.g. a test server).
	Listen string
	// Prefix the console is mounted under ("/backplane"); empty: "/".
	Prefix string
	// Origins allowed to open /ws and to log in (scheme://host[:port]);
	// empty: the request's own host.
	Origins []string
	// TrustedProxies whose X-Forwarded-For names the client (addresses,
	// CIDRs): deployment-owned proxy addresses.
	TrustedProxies []string
	// InsecureCookie drops Secure from the session cookie and HSTS from the
	// responses: plain-HTTP development only.
	InsecureCookie bool
	// AdminToken is what /auth/login accepts: configuration of the
	// deployment, at least MinAdminTokenLen characters.
	AdminToken config.Secret
	// InternalSecret is presented to the services' platform ports
	// (relay and bundles).
	InternalSecret config.Secret
}

Settings are the console's part of the server configuration.

func (Settings) Validate

func (s Settings) Validate() error

Validate checks the admin token: required, at least MinAdminTokenLen characters.

Jump to

Keyboard shortcuts

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