seatcred

package
v1.2.13 Latest Latest
Warning

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

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

Documentation

Overview

Package seatcred resolves a seat's fleet Redis user and password in the process that needs them (nova-tools#4052). nova-sprint, nova-card, nova-swarm and nova-wake take `--seat <name>` (or NOVA_SEAT) and read the seat's file through pkg/secrets -- the same store, key and sops, and the same checks, `nova-secrets exec` uses -- so no shell wrapper stands between a coordinator and its own store.

The password is held as a secrets.Secret: never printed, never logged, never an argument. It is handed to the Redis client in memory and, for the one child that needs it (`nova-sprint redis-cli`), to that child's environment only; it is never set in this process's own environment, so no other child this process starts inherits it.

Index

Constants

View Source
const (
	// SeatEnv names the seat when no --seat flag does.
	SeatEnv = "NOVA_SEAT"
	// StoreEnv overrides the store directory, DefaultStore under $HOME.
	StoreEnv = "NOVA_SECRETS_STORE"
	// KeyEnv overrides the seat's age key, DefaultKeyDir/<seat>.key under $HOME.
	KeyEnv = "NOVA_SECRETS_KEY"
	// SopsEnv overrides the sops binary, else sops on PATH.
	SopsEnv = "NOVA_SECRETS_SOPS"
	// UserEnv picks the Redis ACL user when the seat's file holds more than
	// one password; it is the variable internal/nsprint/store already reads.
	UserEnv = "NOVA_SPRINT_REDIS_USER"

	// DefaultStore and DefaultKeyDir are the fleet layout under $HOME.
	DefaultStore  = "nova-bench/secrets"
	DefaultKeyDir = ".config/nova-secrets"
)
View Source
const ConfigProfileColumns = "name, dsn, password env"

ConfigProfileColumns names the tab-separated columns for a nova-config seat profile.

View Source
const ProfileColumns = "name, redis addr, redis user, secret env, store, key[, github token env]"

ProfileColumns names the tab-separated columns, in order; a refusal about a row prints it so the fix needs no doc. The seventh, the GitHub token env, is optional: a six-column row is the #4330 row, and its GitHub verbs read GH_TOKEN from the session as before.

View Source
const ProfileFile = "seats.tsv"

ProfileFile is the profile's name under $XDG_CONFIG_HOME/<tool>/.

Variables

View Source
var ErrNoProfileRow = errors.New("no row")

ErrNoProfileRow is wrapped by LoadProfile's refusal when the file has no row for the seat (or there is no file).

View Source
var Users = []string{"coordinator", "bench"}

Users is the order a seat's Redis user is chosen in when UserEnv is unset: the first whose password the seat's file holds.

Functions

func Addr

func Addr() string

Addr is this process's seat's Redis address; see Selection.Addr.

func FromArgs

func FromArgs(args []string, getenv func(string) string) ([]string, error)

FromArgs selects this process's seat from args; see Selection.FromArgs.

func PasswordKey

func PasswordKey(user string) string

PasswordKey is the key in a seat's file that holds user's Redis password.

func ProfilePath

func ProfilePath(tool string, getenv func(string) string) (string, error)

ProfilePath is $XDG_CONFIG_HOME/<tool>/seats.tsv, else $HOME/.config/<tool>/seats.tsv (the XDG default).

Types

type ConfigProfile

type ConfigProfile struct {
	Name        string // the seat --seat names
	DSN         string // the PostgreSQL DSN, postgres://user@host:port/db
	PasswordEnv string // the name of the variable that holds the password
}

ConfigProfile is one seats.tsv row for nova-config: the seat names where its Postgres store is, and the name of the variable holding its password.

func LoadConfigProfile

func LoadConfigProfile(path, seat string) (ConfigProfile, error)

LoadConfigProfile reads path and returns the row named seat. A missing file or row is an error wrapping ErrNoProfileRow. When seat is not found in the file, the error names the known seats. Blank lines and lines starting with # are skipped.

type Cred

type Cred struct {
	Seat     string
	User     string
	Key      string
	Password secrets.Secret
	// GitHubKey is the key of the seat's file its seats.tsv row names for the
	// GitHub token (the seventh column), "" when the row names none. GitHub
	// is the token when the file holds it; GitHubErr says why not when it
	// does not (nova-tools#4330).
	GitHubKey string
	GitHub    secrets.Secret
	GitHubErr error
}

Cred is one seat's Redis login. The password formats as secrets.Redacted under every verb.

func Resolve

func Resolve(seat string, getenv func(string) string) (Cred, error)

Resolve opens seat's file through secrets.OpenSeatFile and picks its Redis login: UserEnv's user when set, else the first of Users whose password the file holds. A refusal names the seat and what to do, never a value.

func ResolveProfile

func ResolveProfile(p Profile, getenv func(string) string) (Cred, error)

ResolveProfile opens the row's seat file through secrets.OpenSeatFile -- the store, key and checks nova-secrets exec uses -- and returns the row's login: User with the password held under SecretEnv, and, when the row names a GitHub token env, the token held under it (a file without it is GitHubErr, refused only when a GitHub verb asks). sops is SopsEnv's, else the one on PATH. A refusal names the seat, the row's file and the remedy, never a value.

type Paths

type Paths struct{ Store, Key, Sops string }

Paths is where a seat's file, key and sops are.

func PathsFor

func PathsFor(seat string, getenv func(string) string) (Paths, error)

PathsFor resolves the store, the seat's key and sops from getenv: the override variables when set, else the fleet layout under HOME and sops on PATH.

type Profile

type Profile struct {
	Name      string // the seat --seat names
	Addr      string // host:port of the seat's Redis
	User      string // the Redis ACL user
	SecretEnv string // the key of the seat's file that holds User's password
	Store     string // the nova-secrets store directory
	Key       string // the age key that opens the seat's file; <as>.key names the file <as>.yaml
	GitHubEnv string // optional seventh column: the key of the seat's file that holds its GitHub token, "" when none
}

Profile is one seats.tsv row.

func LoadProfile

func LoadProfile(path, seat, home string) (Profile, error)

LoadProfile reads path and returns the row named seat. A missing file or row is an error wrapping ErrNoProfileRow that names the file and its columns; a malformed row is an error naming file:line, even when it is not the seat's row, since the fleet play wrote the whole file. Blank lines and lines starting with # are skipped. A leading ~/ in store or key is home.

func (Profile) AsName

func (p Profile) AsName() string

AsName is the seat's name in the store: the key's file name without .key, the fleet layout (<store>/<as>.yaml opened by <keydir>/<as>.key).

type Selection

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

Selection is a seat choice: Select records it, Active resolves it once. The process has one (Process), which the package functions act on; a test holds its own so it runs in parallel with every other (nova-tools#4330).

func Process

func Process() *Selection

Process is this process's seat selection.

func (*Selection) Active

func (s *Selection) Active() (c Cred, ok bool, err error)

Active is the selected seat's login, resolved on first use and kept for the life of the selection. ok is false when no seat is selected: the caller then authenticates as it did before (its own environment variables).

func (*Selection) Addr

func (s *Selection) Addr() string

Addr is the selected seat's Redis address from its profile row, "" when no seat is selected or its row names none: the address a store dial falls back to when its caller names none.

func (*Selection) FromArgs

func (s *Selection) FromArgs(args []string, getenv func(string) string) ([]string, error)

FromArgs takes --seat <name> or --seat=<name> out of args (only before a "--", which ends a tool's own flags), selects that seat or else getenv's NOVA_SEAT, and returns the rest. A --seat with no name is an error.

func (*Selection) Select

func (s *Selection) Select(seat string)

Select makes seat the selection ("" is none) and forgets any earlier resolution.

func (*Selection) SelectProfile

func (s *Selection) SelectProfile(p Profile, resolve func(seat string) (Cred, error))

SelectProfile is SelectWith for a seats.tsv row: the row's seat and its Redis address, resolved through resolve.

func (*Selection) SelectWith

func (s *Selection) SelectWith(seat, redisAddr string, resolve func(seat string) (Cred, error))

SelectWith is Select with the seat's Redis address (Addr, "" when the seat names none) and the resolution Active runs on first use: resolve (a seat profile row, nova-tools#4330) instead of Resolve's default layout. A nil resolve is Resolve.

func (*Selection) Selected

func (s *Selection) Selected() string

Selected is the seat Select recorded, "" when none.

Directories

Path Synopsis
Package seattest builds a real nova-secrets store for a test: a git working copy with an upstream, recovery.pub, .sops.yaml and one seat's file sealed by sops to a fresh age key, laid out where pkg/seatcred looks by default under a temporary HOME.
Package seattest builds a real nova-secrets store for a test: a git working copy with an upstream, recovery.pub, .sops.yaml and one seat's file sealed by sops to a fresh age key, laid out where pkg/seatcred looks by default under a temporary HOME.

Jump to

Keyboard shortcuts

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