admin

package
v0.5.47 Latest Latest
Warning

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

Go to latest
Published: May 1, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package admin serves the voting-config endpoint by proxying the GitHub Pages CDN (valargroup/token-holder-voting-config) and exposes HTTP endpoints for validator join registration (pending queue in SQLite).

Published vote_servers URLs are updated via manual PRs on the config repo; this package does not write to GitHub.

Index

Constants

View Source
const PendingEvictionSweepInterval = time.Hour

PendingEvictionSweepInterval is how often expired pending_registrations rows are deleted (not configurable).

View Source
const PendingRegistrationTTL = 7 * 24 * time.Hour

PendingRegistrationTTL is how long an operator remains in the pending join queue before the row expires (SQLite; not configurable).

View Source
const VotingConfigRefreshInterval = time.Minute

VotingConfigRefreshInterval is how often the admin re-fetches voting-config from ConfigURL so the cached GET /api/voting-config response stays warm. Not configurable — the CDN is small, this is purely a freshness window.

Variables

This section is empty.

Functions

func AddressToValoper

func AddressToValoper(address string) (string, error)

AddressToValoper converts an account bech32 address to a valoper address.

func RegisterRoutes

func RegisterRoutes(
	router *mux.Router,
	getAdmin func() *Admin,
	logger log.Logger,
)

RegisterRoutes registers admin HTTP routes on the given mux router.

func RegisterUIConfigRoutes added in v0.5.6

func RegisterUIConfigRoutes(router *mux.Router, logger log.Logger)

RegisterUIConfigRoutes registers GET /api/ui-config on the given router.

All values are resolved once at registration time from environment vars. Changing them requires a restart, which matches how the rest of the daemon reads its configuration.

func RunConfigRefresher added in v0.5.21

func RunConfigRefresher(ctx context.Context, a *Admin, interval time.Duration, logger log.Logger)

RunConfigRefresher periodically re-fetches voting-config so the cached GET /api/voting-config response stays warm and picks up newly added or removed servers without a process restart. Blocks until ctx is cancelled.

This used to be a side effect of the in-process fleet health watchdog, which now lives in vote-infrastructure/watchdog/ as a standalone Rust service. The refresher remains in-process because the cached endpoint only matters when the admin HTTP server is up.

func RunPendingSweeper added in v0.5.12

func RunPendingSweeper(ctx context.Context, a *Admin, interval time.Duration, logger log.Logger)

RunPendingSweeper periodically deletes expired pending_registrations rows. It blocks until ctx is cancelled.

func VerifyArbitrarySignature

func VerifyArbitrarySignature(signerAddress, payload string, signatureB64, pubKeyB64 string) error

VerifyArbitrarySignature verifies a Keplr-style ADR-036 signArbitrary signature. It checks that:

  1. The secp256k1 signature is valid over the amino sign doc.
  2. The public key derives to the claimed signer address.

Hashing note: secp256k1.PubKey.VerifySignature already SHA-256s its input before ECDSA verification, mirroring secp256k1.PrivKey.Sign on the signing side (which is what `svoted sign-arbitrary` and any Keplr/ADR-036 signer invoke via Keyring.Sign). We therefore pass the raw amino sign doc here — pre-hashing it would cause a double-hash and reject every real signature.

Types

type Admin

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

Admin fetches and caches voting-config from the CDN and stores pending validator registrations in SQLite.

func New

func New(cfg Config, homeDir string, checkValidatorExists ValidatorChecker, logger log.Logger) (*Admin, error)

New creates a new Admin from the given configuration. homeDir is used to resolve default DBPath when cfg.DBPath is empty. checkValidatorExists may be nil; in that case validator checks always return false.

func (*Admin) Close

func (a *Admin) Close() error

Close releases the SQLite store.

func (*Admin) GetVotingConfig added in v0.5.0

func (a *Admin) GetVotingConfig() (*VotingConfig, error)

GetVotingConfig returns the cached voting config, refreshing if stale.

func (*Admin) Store

func (a *Admin) Store() *Store

Store returns the SQLite store (never nil when Admin is non-nil).

func (*Admin) ValidatorExists added in v0.5.42

func (a *Admin) ValidatorExists(operatorAddress string) bool

ValidatorExists reports whether the operator account (bech32) has a staking validator record. It converts the account address to its valoper form before checking staking state.

type Config

type Config struct {
	// Disable turns off the admin server entirely.
	Disable bool `mapstructure:"disable"`

	// ConfigURL is the voting-config JSON the admin re-serves at the
	// cached GET /api/voting-config endpoint. It points at the same canonical
	// CDN URL that wallets and join.sh fetch directly
	// (valargroup.github.io/token-holder-voting-config/voting-config.json) —
	// override only for staging mirrors or fork testing. Fleet health
	// probing of these endpoints is handled by the standalone watchdog
	// service in vote-infrastructure/watchdog/.
	ConfigURL string `mapstructure:"config_url"`

	// DBPath is the SQLite path for pending validator registrations.
	// Default: $HOME/.svoted/admin.db (resolved in New).
	DBPath string `mapstructure:"db_path"`
}

Config holds the admin server configuration, read from app.toml admin.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the default admin configuration.

type PendingRegistration

type PendingRegistration struct {
	OperatorAddress string `json:"operator_address"`
	URL             string `json:"url"`
	Moniker         string `json:"moniker"`
	RequestedAt     int64  `json:"requested_at"`
	ExpiresAt       int64  `json:"expires_at"`
}

PendingRegistration is a row in pending_registrations and the API shape for GET /api/pending-validators.

type PendingValidatorPublic added in v0.5.12

type PendingValidatorPublic struct {
	OperatorAddress string `json:"operator_address"`
	URL             string `json:"url"`
	Moniker         string `json:"moniker"`
	RequestedAt     int64  `json:"requested_at"`
	ExpiresAt       int64  `json:"expires_at"`
}

PendingValidatorPublic is returned by GET /api/pending-validators (no secrets).

type ServiceEntry

type ServiceEntry struct {
	URL             string `json:"url"`
	Label           string `json:"label"`
	OperatorAddress string `json:"operator_address,omitempty"`
}

ServiceEntry is the wire format for a server in the voting-config response.

type Store

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

Store is a SQLite-backed store for pending validator registrations.

func NewStore

func NewStore(dbPath string) (*Store, error)

NewStore opens (or creates) a SQLite database and runs migrations.

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database.

func (*Store) EvictExpiredPending added in v0.5.12

func (s *Store) EvictExpiredPending() (int64, error)

EvictExpiredPending deletes rows whose expires_at is in the past.

func (*Store) ListPendingRegistrations

func (s *Store) ListPendingRegistrations() ([]PendingRegistration, error)

ListPendingRegistrations returns non-expired pending registrations.

func (*Store) RemovePendingRegistration

func (s *Store) RemovePendingRegistration(operatorAddress string) (bool, error)

RemovePendingRegistration deletes a pending row by operator address. Returns true if a row was deleted.

func (*Store) UpsertPendingRegistration

func (s *Store) UpsertPendingRegistration(r PendingRegistration) error

UpsertPendingRegistration inserts or updates a pending registration. requested_at is preserved on conflict.

type UIConfigResponse added in v0.5.6

type UIConfigResponse struct {
	// Mode is "dev" or "prod". The UI uses this to gate developer-only widgets.
	Mode UIMode `json:"mode"`
	// DevPIRControls is a denormalised flag for the most common gate so the UI
	// does not need to know the meaning of each mode value.
	DevPIRControls bool `json:"dev_pir_controls"`
	// PrecomputedBaseURL is the bucket origin that this svoted's PIR siblings
	// fetch their snapshots from. The UI composes the manifest URL as
	// "<base>/snapshots/<height>/manifest.json" using a shared subpath
	// constant. Resolved at startup from SVOTE_PRECOMPUTED_BASE_URL with a
	// production-bucket default. Has no trailing slash.
	PrecomputedBaseURL string `json:"precomputed_base_url"`
}

UIConfigResponse is the wire format returned by GET /api/ui-config.

It carries everything the static admin UI bundle needs to know about the svoted instance it's served by. New fields should be additive — older UI builds must continue to work against newer servers.

type UIMode added in v0.5.6

type UIMode string

UIMode controls which features the admin UI exposes.

"prod" hides developer-only controls such as the in-process PIR rebuild triggers. "dev" enables them. Production deployments must explicitly set SVOTE_UI_MODE=dev to opt in to the developer surface; the default is "prod" so a forgotten environment file can never accidentally expose them.

const (
	UIModeProd UIMode = "prod"
	UIModeDev  UIMode = "dev"

	// DefaultPrecomputedBaseURL is the production DigitalOcean Spaces bucket
	// where the publish-snapshot workflow uploads pre-computed PIR snapshots.
	// Per-deployment overrides go through SVOTE_PRECOMPUTED_BASE_URL so a
	// staging svoted can point at a staging bucket.
	DefaultPrecomputedBaseURL = "https://vote.fra1.digitaloceanspaces.com"
)

type ValidatorChecker added in v0.5.42

type ValidatorChecker func(valoper string) bool

ValidatorChecker returns whether a validator with the given valoper bech32 exists in staking state.

type VotingConfig

type VotingConfig struct {
	Version     int            `json:"version"`
	VoteServers []ServiceEntry `json:"vote_servers"`
	PIRServers  []ServiceEntry `json:"pir_endpoints"`
	// SnapshotHeight is the canonical Orchard nullifier-tree snapshot height
	// for the current voting round. PIR servers must serve this exact height,
	// and the admin UI auto-populates round drafts from it.
	SnapshotHeight *uint64 `json:"snapshot_height,omitempty"`
}

VotingConfig is the wire format returned by GET /api/voting-config.

Jump to

Keyboard shortcuts

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