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

console

package
v0.1.0 Latest Latest
Warning

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

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

Documentation

Overview

Package console is the human side of a lockkit product: it holds the credentials and the only backend connection, enforces the approved policy, shows every request that needs a human decision on an approval screen, and writes the audit trail. Clients (a product's CLI and MCP server) reach it over the per-profile socket and never see a secret.

A product supplies a Backend (its methods, its connection, its checks and its policy) and calls Run. The pieces Run is made of — the terminal, the approvers, the isolation checks, the request loop — are exported for products that assemble their own console.

Index

Constants

View Source
const (
	// ApprovalTimeout bounds every human answer.
	ApprovalTimeout = 5 * time.Minute
	// PolicyPoll is how often the config files are checked for changes.
	PolicyPoll = 2 * time.Second
	// ResponseQuantum levels the response time of a request whose answer
	// could otherwise tell the agent how long a human or a backend took.
	ResponseQuantum = 250 * time.Millisecond
)

Timeouts of a console session. The idle timeout and the maximum session length are the profile's.

View Source
const CredentialsNone = "none"

CredentialsNone is the credentials mode of a backend that needs no secret (a local file, a socket the account owns).

View Source
const FingerDeadline = 30 * time.Second

FingerDeadline bounds the wait for a finger after a typed yes.

Variables

View Source
var ErrConfig = errors.New("configuration error")

ErrConfig marks usage and configuration errors (exit code 3).

View Source
var ErrSecretTimeout = errors.New("console: no password entered")

ErrSecretTimeout is AskSecret's error when no answer comes within ApprovalTimeout.

View Source
var ErrTooManyNoMatch = errors.New("fingerprint: three consecutive non-matching fingers")

ErrTooManyNoMatch is set on the third consecutive no-match: the caller ends the console.

View Source
var Paint = ui.Painter{On: true}

Paint styles console lines.

Functions

func Bell

func Bell(io IO)

Bell rings io's bell when it has one.

func CloseVerifier

func CloseVerifier(v fingerprint.Verifier)

CloseVerifier releases a verifier that holds a connection (fprintd's).

func Decode

func Decode(req ipc.Request, v any) *ipc.Response

Decode decodes the params of req into v, refusing unknown fields; nil when they decode (or are absent), else the error response.

func DenyReason

func DenyReason(ans string) string

DenyReason is the reason of an answer "n <text>" or "no <text>" (n and no in any case), trimmed, cut at 200 bytes and escaped; "" for any other answer.

func ErrResp

func ErrResp(id int64, code int, msg string) ipc.Response

ErrResp is an error response.

func FormatChanges

func FormatChanges(changes []config.Change) []string

FormatChanges renders a policy diff, loosenings in red. Every value is escaped: a pattern or a host could otherwise carry terminal escapes that hide a loosening from the human.

func Harden

func Harden() error

Harden keeps the secret out of core dumps and away from same-user ptrace and /proc/<pid>/mem readers: RLIMIT_CORE=0 and PR_SET_DUMPABLE=0.

func Level

func Level(ctx context.Context, quantum time.Duration, start time.Time)

Level waits until the next multiple of quantum since start, so that the response time tells nothing finer than quantum (0 or less: no wait).

func Loosens

func Loosens(changes []config.Change) bool

Loosens reports whether any change loosens the policy.

func OKResp

func OKResp(id int64, v any) ipc.Response

OKResp is a result response; a value that does not encode gives an internal error.

func PeerFrom

func PeerFrom(ctx context.Context) (ipc.Cred, bool)

PeerFrom returns the client behind the request of ctx.

func PeerText

func PeerText(ctx context.Context) string

PeerText describes the client behind a request for the approval screen.

func ProcessText

func ProcessText(pid int) string

ProcessText names the process behind pid on Linux: its executable path when the kernel lets the console read it (a process of its own account), else the name the process gave itself, labelled as such; "" elsewhere. The uid and pid are the kernel's; the name is the process's choice.

func Run

func Run(ctx context.Context, p kit.Product, b Backend, o Options) error

Run is a product's `console` command: the isolation checks, the policy review, the production confirmation, the credentials and the connection, the backend's checks, the socket, then the request loop until Ctrl-C, logout, :quit or a timeout.

func SameUserRefusal

func SameUserRefusal(p kit.Product) string

SameUserRefusal is the one text of the same-user refusal of p.

func SameUserRefused

func SameUserRefused(p kit.Product, sys *sysconf.Config, allowSameUser bool) error

SameUserRefused is the refusal of a console of p without a separated setup (sys nil, as sysconf.Load returns it), or nil.

func StampPeer

func StampPeer(ctx context.Context, b *audit.Base)

StampPeer sets the peer fields of b from the client behind ctx: its uid, pid and account name. The process name, chosen by the agent, stays on the screen (PeerText) and out of the audit log.

func WithPeer

func WithPeer(ctx context.Context, c ipc.Cred) context.Context

WithPeer returns ctx carrying the credentials of the client behind a request.

Types

type Approval

type Approval struct {
	Approved bool
	// Auto: approved by auto mode, without a prompt.
	Auto bool
	// Retype: the human typed Screen.Retype.
	Retype bool
	// Refusal is the answer to send when neither Approved nor Retype: the
	// human denied (with the reason they typed after "n"), the approval
	// timed out, the client left, or no finger matched.
	Refusal ipc.Response
}

Approval is the outcome of Env.Approve.

type Approver

type Approver interface {
	Approve(ctx context.Context, d Decision) Result
	// Mode is the audit value: "session" or "desktop+fingerprint".
	Mode() string
}

Approver asks the human for one decision. Each call is one decision: a fingerprint match never carries over.

func NewDesktopTypedApprover

func NewDesktopTypedApprover(io IO) Approver

NewDesktopTypedApprover is the level-1 approver: typed answers in a window the agent can type into, audited apart from the session shape.

func NewFingerprintApprover

func NewFingerprintApprover(io IO, v fingerprint.Verifier, finger string, deadline time.Duration) Approver

NewFingerprintApprover wraps the typed approval with a fingerprint of the dedicated finger (system.toml `finger`), named in the prompt (level 2).

func NewTypedApprover

func NewTypedApprover(io IO) Approver

NewTypedApprover is the session approver: typed answers in a login session of the console account, audited as "session".

type Backend

type Backend interface {
	// Methods are the product's IPC methods, beyond the console's own
	// (hello, status, change.request, logout). A name the console serves
	// is refused at start.
	Methods() map[string]Handler
	// Connect opens a session with the credentials the console obtained
	// (asked, from the OS keychain, or from Vault). It is called at start
	// and again after a lost connection or an expired login. The console
	// wipes c.Secret once Connect returns.
	Connect(ctx context.Context, c Credentials) (Session, error)
	// Checks audits a new session: what the backend account can do beyond
	// the profile (a red warning, typed "continue" to go on), or a reason
	// to refuse it. On a production profile every finding refuses.
	Checks(ctx context.Context, s Session) []Finding
	// LoadPolicy reads the current policy from the config files. The
	// console calls it at start and every PolicyPoll.
	LoadPolicy() (Policy, error)
	// Status is the product's part of the status method's answer.
	Status(s Session) any
}

Backend is what a product plugs into the console: its methods, its connection to the backend it guards, the checks of that connection and its policy. Everything else — the terminal, the isolation, the approval screen, the modes, the policy review, the session limits, the audit log, the socket — is the console's.

type Beller

type Beller interface {
	Bell()
}

Beller is implemented by an IO that can ring the terminal bell, so that a human away from the console notices a prompt waiting for them.

type Credentials

type Credentials struct {
	User string
	// Secret is nil for a profile with credentials "none".
	Secret []byte
}

Credentials are what Connect needs: the user and its secret.

type Decision

type Decision struct {
	// Prompt is the typed prompt, already framed by the caller.
	Prompt string
	// Expected is the typed answer: "y" (any case) or the production
	// profile name (exact).
	Expected string
	// Production selects the exact match.
	Production bool
	// Retype is the typed answer that asks to retype placeholders ("" when
	// not offered).
	Retype string
}

Decision names what a human is approving.

type Env

type Env interface {
	// Approve shows s and asks the human (or approves it in auto mode, see
	// Screen.NeverAuto). The decision is audited before Approve returns.
	Approve(ctx context.Context, id int64, s Screen) Approval
	// Auto reports auto mode in effect (never on a production profile).
	Auto() bool
	// ShowResults reports the clear results switch (Ctrl-O).
	ShowResults() bool
	// UnmaskAllowed reports whether clients may ask for unmasked output
	// (:unmask), each such request approved by the human.
	UnmaskAllowed() bool
	// Session returns the live session, reconnecting first when the login
	// expired (credentials_ttl, a Vault lease) or the connection was lost
	// (Lost).
	Session(ctx context.Context) (Session, error)
	// Lost drops the session after a lost connection: the next Session
	// reconnects.
	Lost()
	// Policy is the approved policy in force.
	Policy() Policy
	// Audit writes rec as is; AuditBase stamps b with the profile and the
	// client behind ctx first.
	Audit(rec audit.Record)
	AuditBase(ctx context.Context, b audit.Base)
	// Println writes one console line; Safe escapes client text for it.
	Println(line string)
	Safe(s string) string
	// Sanitizer scrubs error text for a client or the audit log.
	Sanitizer() *sanitize.Sanitizer
	// Level levels a response time (Options.Quantum) from start.
	Level(ctx context.Context, start time.Time)
	// End ends the session after the current request.
	End(reason string)
}

Env is what a Handler may use of the console.

type Finding

type Finding struct {
	// Refuse stops the console (or the reconnection) whatever the profile.
	Refuse bool
	Text   string
}

Finding is one result of Checks.

type Handler

type Handler func(ctx context.Context, req ipc.Request, env Env) ipc.Response

Handler serves one product method. env is the console: the approval screen, the modes, the session, the audit log.

type IO

type IO interface {
	Println(s string)
	// Ask prints prompt and reads one line. It returns false when no answer
	// came within timeout, when ctx ended (Ctrl-C, client gone) or when the
	// terminal is closed.
	Ask(ctx context.Context, prompt string, timeout time.Duration) (string, bool)
	// AskSecret reads a line with echo off.
	AskSecret(ctx context.Context, prompt string) ([]byte, error)
}

IO is the console's terminal. Ask and AskSecret discard pending input before prompting, so type-ahead never answers a prompt.

type Isolation

type Isolation struct {
	Product kit.Product
	Sys     *sysconf.Config
	Display sysconf.Display
	Self    int
	GID     int
	// Desktop is the desktop shape; Verifier is then its fingerprint
	// reader (nil at level 1 and in the session shape).
	Desktop  bool
	Verifier fingerprint.Verifier
	// contains filtered or unexported fields
}

Isolation is how the console is kept apart from the agent: in a separated setup (sysconf) the console runs as the service account and serves the client group over a shared socket, either in its own login session (the session shape: the kernel keeps the agent out of its terminal) or in a terminal of the console account opened on the agent's desktop (the desktop shape: every approval also takes a fingerprint). Without one it refuses to start (except when IsolationEnv.AllowSameUser is set by a product's test build).

func CheckIsolation

func CheckIsolation(p kit.Product, io IO, env IsolationEnv) (*Isolation, error)

CheckIsolation applies the separation rules before any prompt and before the console writes any state. Warnings go to the terminal; a violation is an error. The production rule on X11 needs the profile and is applied later, by RefuseX11IfProduction.

func (*Isolation) Boundary

func (iso *Isolation) Boundary() string

Boundary names where the approval screen stands, for the status screen.

func (*Isolation) Level

func (iso *Isolation) Level() int

Level is the security level of the isolation: 2 for the desktop shape with a fingerprint reader, 1 for the desktop shape without, 0 for the session shape (the kernel is the boundary).

func (*Isolation) Listen

func (iso *Isolation) Listen(path string) (net.Listener, error)

Listen opens the socket.

func (*Isolation) PeerCheck

func (iso *Isolation) PeerCheck() func(ipc.Cred) bool

PeerCheck decides which peers are served: the console's own account and, in a separated setup, allowed_uids and the client group. The group is looked up at every connection: a uid removed from it loses access at its next connection, not at the next console start. Nil means the console's own uid only.

func (*Isolation) RefuseX11IfProduction

func (iso *Isolation) RefuseX11IfProduction(production bool) error

RefuseX11IfProduction applies the production rule once the profile is known: a production profile never runs in an X11 session.

func (*Isolation) SocketPath

func (iso *Isolation) SocketPath(projectKey, profile string) (string, error)

SocketPath is the console socket of a project and profile: in the shared socket directory of a separated setup, else in the user's runtime directory.

type IsolationEnv

type IsolationEnv struct {
	LoadSys       func() (*sysconf.Config, error)
	Display       func() sysconf.Display
	UID           func() int
	UserName      func() (string, error)
	LoginUID      func() (int, bool)
	TerminalOwner func() (int, bool)
	ClientAllowed func(sys *sysconf.Config, uid int) bool
	ClientGID     func(sys *sysconf.Config) (int, error)
	// AccountExists reports whether uid names an account.
	AccountExists func(uid int) bool
	// Relay reports a session opened through a relay (sysconf.SessionRelay).
	Relay func() string
	// MachinectlUIDs lists the callers of running `machinectl shell`
	// processes (sysconf.MachinectlShellUIDs).
	MachinectlUIDs func() ([]int, bool)
	// DesktopEnv reports <PREFIX>DESKTOP=1: the desktop shape is asked for.
	DesktopEnv func() bool
	// DisplaySet reports a non-empty DISPLAY.
	DisplaySet func() bool
	// ParentChain lists the ancestors of the process, from the parent up
	// (sysconf.ParentChain).
	ParentChain func() ([]sysconf.Proc, error)
	// NewVerifier opens the fingerprint reader of user.
	NewVerifier func(user string) (fingerprint.Verifier, error)
	// EnrolledTimeout bounds the start-up question to fprintd; past it, the
	// reader counts as unavailable. 0 is 10s.
	EnrolledTimeout time.Duration
	// AllowSameUser lets the console run without a separated setup, in the
	// agent's account. Only a product's test build sets it (a build tag):
	// no config file, flag, environment variable or client may.
	AllowSameUser bool
}

IsolationEnv holds what CheckIsolation reads from the system; tests replace its functions (RealIsolationEnv is the real one).

func RealIsolationEnv

func RealIsolationEnv(p kit.Product, tty *os.File) IsolationEnv

RealIsolationEnv is the environment of a console of p on terminal tty (nil when unknown).

type Key

type Key int

Key is a control key the human pressed, outside a line. Keys reach the console only from its own terminal: no client can send one.

const (
	// KeyShiftTab is back-tab (ESC [ Z, the same on Linux and macOS): it
	// cycles the approval mode.
	KeyShiftTab Key = iota + 1
	// KeyCtrlO toggles the clear results.
	KeyCtrlO
)

type KeySource

type KeySource interface {
	Keys() <-chan Key
	SetKeyHandler(func(Key))
}

KeySource is implemented by an IO that reports the control keys pressed in the console: on Keys between prompts, to the handler during one.

type LineSource

type LineSource interface {
	Lines() <-chan string
}

LineSource is implemented by an IO that also delivers the lines the human types between requests (console commands such as :review).

type Loop

type Loop struct {
	IO IO
	// Handle serves one request (required).
	Handle func(ctx context.Context, req ipc.Request) ipc.Response
	// Command runs a line typed by the human between requests.
	Command func(ctx context.Context, line string)
	// Key handles a control key pressed between prompts; with a KeySource
	// IO it also handles the keys pressed during a prompt.
	Key func(Key)
	// Tick runs every second (session timeouts).
	Tick func()
	// Poll runs every PolicyPoll (config file changes).
	Poll func()
	// Ended reports the end of the session (required); End ends it.
	Ended func() bool
	End   func(reason string)
	// Refresh runs after every event (the status row); Clear on return.
	Refresh func()
	Clear   func()
	// PeerAllowed decides which clients are served, from the kernel's
	// credentials of the peer. Nil serves the console's own account only.
	PeerAllowed func(ipc.Cred) bool
}

Loop is the console's request loop: it accepts clients, checks their peer credentials and serves their requests one at a time on one goroutine, between the lines and control keys the human types and the periodic ticks. A request whose client goes away has its context cancelled, which cancels a pending approval prompt.

func (*Loop) Serve

func (l *Loop) Serve(ctx context.Context, ln net.Listener, lines <-chan string) error

Serve accepts clients on ln and serves their requests one at a time until ctx ends or the session ends (logout, timeouts, :quit). lines are the console commands typed by the human (nil for none); the control keys come from the IO when it is a KeySource. ln is closed on return, which removes the socket.

type Options

type Options struct {
	// Profile is the profile to serve; ProjectRoot the project whose
	// config holds it ("" for the user config).
	Profile     string
	ProjectRoot string
	IO          IO
	// Auto, AllowUnmask and ShowResults are the start-up switches (refused
	// at level 2).
	Auto, AllowUnmask, ShowResults bool
	// StateDir is the state base (kit.StateBase() when empty).
	StateDir string
	// TTY is the console's terminal device (nil when unknown).
	TTY *os.File
	// Now defaults to time.Now.
	Now func() time.Time
	// Quantum levels response times (Env.Level): 0 is ResponseQuantum, a
	// negative value none.
	Quantum time.Duration
	// Sanitizer scrubs error text; nil is sanitize.New().
	Sanitizer *sanitize.Sanitizer
	// Isolation replaces the system the isolation checks read (tests and
	// a product's test build); nil is RealIsolationEnv.
	Isolation *IsolationEnv
	// Harden replaces Harden (tests).
	Harden func() error
	// VaultRead replaces secrets.VaultRead (tests).
	VaultRead func(ctx context.Context, path string) (*secrets.VaultCredentials, error)
	// Keychain replaces the product's OS keychain (tests).
	Keychain *secrets.Keychain
	// Ready, when set, is told the socket path once the console listens.
	Ready func(socket string)
}

Options configure Run.

type Outcome

type Outcome int

Outcome of an approval.

const (
	Approved Outcome = iota
	// Denied is a typed answer that is not the expected one; Answer holds it.
	Denied
	// Unanswered: no answer within the timeout, or ctx ended.
	Unanswered
	RetypeRequested
	// NoFinger: desktop mode, typed yes, then no match, timeout or reader
	// error; Err holds it.
	NoFinger
)

type PIIBackend

type PIIBackend interface {
	// PIIRules returns the mask and allow rules of p.
	PIIRules(p Policy) (mask, allow []string)
	// CheckMask validates a pattern sent by a client and returns its
	// canonical form; an error is shown to the client (invalid params).
	CheckMask(pattern string) (string, error)
	// SaveMask adds the canonical pattern to the config file as it is on
	// disk, so the human's unconfirmed edits are neither lost nor applied.
	SaveMask(pattern string) error
	// WithMask returns p with the canonical pattern added (canonical form,
	// sorted lists); p itself is not changed.
	WithMask(p Policy, pattern string) Policy
}

PIIBackend is implemented by a backend whose policy has mask rules: the console then serves pii.list and pii.add for it. A pattern's syntax (db.table.column, a JSON path, a header name) is the product's. Adding a mask rule only tightens the policy, so it applies at once.

type Policy

type Policy interface {
	config.Policy
	Profile() Profile
}

Policy is a product's policy: the diff and description of config.Policy, plus the profile the console needs. Its JSON encoding is what the console fingerprints and stores as the approved policy, so it must be canonical (sorted lists, defaults applied) and decode back into the same type.

type Profile

type Profile struct {
	Name       string
	Production bool
	// Where names the backend for the human (host:port, a path): it is
	// shown escaped.
	Where string
	// Host, Port and DefaultPort name the OS keychain item of the secret
	// ("<profile>@<host>:<port>").
	Host              string
	Port, DefaultPort int
	// User is the backend account; "" asks the human (or comes from Vault).
	User string
	// Credentials is config.CredentialsAsk, CredentialsKeychain,
	// CredentialsVault or CredentialsNone; VaultPath is read for "vault".
	Credentials string
	VaultPath   string
	// CredentialsTTL asks for the secret again (and reconnects) once the
	// connection is that old; 0 keeps it for the session.
	CredentialsTTL time.Duration
	// IdleTimeout ends the session after that long without activity,
	// MaxSession after that long in all. 0 takes 20m and 4h.
	IdleTimeout, MaxSession time.Duration
}

Profile is what the console needs to know of a profile.

type Result

type Result struct {
	Outcome Outcome
	Answer  string
	Err     error
}

Result of an approval.

type Screen

type Screen struct {
	// Header follows the profile name on the top line ("db / shop · tier read").
	Header string
	// Requester names the client; "" takes the peer behind the request.
	Requester string
	// Note is the agent's one-line reason (untrusted, escaped, audited).
	Note string
	// Body are the lines of the request; Warnings are shown in red.
	Body     []string
	Warnings []string
	// Production forces the production prompt; the profile's own flag
	// always does.
	Production bool
	// NeverAuto is never approved by auto mode (an unmasked read, a
	// destructive-looking operation): the human answers every time.
	NeverAuto bool
	// Red frames the screen in red outside production too.
	Red bool
	// Retype, when set, is an answer that asks to retype values; Approve
	// then returns Approval.Retype.
	Retype string
	// Record, when set, turns the decision record into the product's own
	// (with its fields); nil writes the audit.Base.
	Record func(audit.Base) audit.Record
}

Screen is the content of an approval screen, entirely supplied by the product. The console frames it (red on production), closes it with the prompt and keeps the rules: typed profile name on production, timeout, fingerprint at level 2, audit of the decision.

type Server

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

Server serves client requests one at a time against one session. It is not safe for concurrent use: Serve runs every request on one goroutine.

func NewServer

func NewServer(cfg ServerConfig) (*Server, error)

NewServer checks cfg and returns a server enforcing cfg.Policy.

func (*Server) Approve

func (s *Server) Approve(ctx context.Context, id int64, sc Screen) Approval

Approve implements Env: it frames sc, approves it in auto mode or asks the human, and audits the decision.

func (*Server) Audit

func (s *Server) Audit(rec audit.Record)

Audit implements Env.

func (*Server) AuditBase

func (s *Server) AuditBase(ctx context.Context, b audit.Base)

AuditBase implements Env.

func (*Server) Auto

func (s *Server) Auto() bool

Auto implements Env.

func (*Server) CheckPolicy

func (s *Server) CheckPolicy()

CheckPolicy re-reads the policy from the config files. Changes that only tighten are applied at once; a loosening waits for :review.

func (*Server) Close

func (s *Server) Close()

Close closes the session.

func (*Server) Command

func (s *Server) Command(ctx context.Context, line string)

Command runs a console command typed by the human between requests.

func (*Server) End

func (s *Server) End(reason string)

End stops the session after the current request.

func (*Server) Ended

func (s *Server) Ended() (string, bool)

Ended reports whether the session is over, and why.

func (*Server) Handle

func (s *Server) Handle(ctx context.Context, req ipc.Request) ipc.Response

Handle serves one request. None of the console's methods loosens the policy.

func (*Server) Key

func (s *Server) Key(k Key)

Key handles a control key pressed in the console.

func (*Server) Level

func (s *Server) Level(ctx context.Context, start time.Time)

Level implements Env.

func (*Server) Loop

func (s *Server) Loop() *Loop

Loop is the request loop of s, for Serve.

func (*Server) Lost

func (s *Server) Lost()

Lost implements Env.

func (*Server) Policy

func (s *Server) Policy() Policy

Policy implements Env.

func (*Server) PolicyPending

func (s *Server) PolicyPending() bool

PolicyPending reports a loosening awaiting :review: a product refuses requests meanwhile (ipc.CodePolicyPending) when its policy matters to them.

func (*Server) Println

func (s *Server) Println(line string)

Println implements Env.

func (*Server) Safe

func (s *Server) Safe(text string) string

Safe implements Env.

func (*Server) Sanitizer

func (s *Server) Sanitizer() *sanitize.Sanitizer

Sanitizer implements Env.

func (*Server) Session

func (s *Server) Session(ctx context.Context) (Session, error)

Session implements Env.

func (*Server) ShowResults

func (s *Server) ShowResults() bool

ShowResults implements Env.

func (*Server) Status

func (s *Server) Status() StatusResult

Status is the status method's answer.

func (*Server) Tick

func (s *Server) Tick()

Tick ends the session on idle or maximum session timeout, as set by the policy in force.

func (*Server) UnmaskAllowed

func (s *Server) UnmaskAllowed() bool

UnmaskAllowed implements Env.

type ServerConfig

type ServerConfig struct {
	Product kit.Product
	Backend Backend
	// Policy is the approved policy the server enforces.
	Policy Policy
	// StateDir and ApprovedKey locate the approved policy file ("" keeps
	// adoptions in memory).
	StateDir    string
	ApprovedKey string
	Session     Session
	Audit       *audit.Log
	IO          IO
	// Approver takes every human decision; nil means typed approvals.
	Approver Approver
	// Level is the isolation level (2: every decision takes a finger, and
	// a typed answer alone never widens anything).
	Level int
	// Boundary names the desktop boundary for the status screen.
	Boundary string
	Now      func() time.Time
	// Auto, AllowUnmask and ShowResults are the switches at start.
	Auto, AllowUnmask, ShowResults bool
	// Reconnect opens a new session (credentials, Connect, Checks) under
	// the profile in force; nil means a lost connection ends the console.
	Reconnect func(ctx context.Context, p Profile) (Session, error)
	// Quantum levels response times (Env.Level).
	Quantum time.Duration
	// PeerAllowed decides which clients are served; nil: the console's own
	// account.
	PeerAllowed func(ipc.Cred) bool
	// Sanitizer scrubs error text; nil is sanitize.New().
	Sanitizer *sanitize.Sanitizer
	// Refused is the fingerprint of a loosening the human refused at start,
	// not announced again until the files change.
	Refused string
}

ServerConfig holds what a Server needs once the start-up sequence is done. Run builds it; tests build it directly.

type Session

type Session interface {
	Close() error
}

Session is one connection of a backend.

type Sized

type Sized interface {
	Width() int
}

Sized is implemented by an IO that knows its width in columns.

type StatusLine

type StatusLine interface {
	SetStatus(string)
}

StatusLine is implemented by an IO that keeps a status row on screen.

type StatusResult

type StatusResult struct {
	Profile        string `json:"profile"`
	Production     bool   `json:"production"`
	AutoApprove    bool   `json:"auto_approve"`
	AllowUnmask    bool   `json:"allow_unmask"`
	ShowResults    bool   `json:"show_results"`
	IdleTimeoutInS int    `json:"idle_timeout_in_s"`
	SessionEndsInS int    `json:"session_ends_in_s"`
	// Level is the security level: 0 session shape, 1 or 2 desktop shape.
	Level   int `json:"level"`
	Product any `json:"product,omitempty"`
}

StatusResult answers the status method: the console's part, and the product's (Backend.Status) under "product".

type Terminal

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

Terminal is the IO of a real console: prompts and messages go to out, answers and console commands are read from in, which must be the controlling terminal.

Interactive puts the terminal in cbreak mode: the Terminal does the line editing itself, so that it also sees the control keys (Shift+Tab, Ctrl-O) and can keep a status line on the bottom row, above which the output scrolls. One goroutine reads in from the first prompt on, so that the console can wait for a request and for a typed command at the same time. Before each prompt the kernel's pending input is flushed and the lines already read are dropped: type-ahead never answers a prompt.

func NewTerminal

func NewTerminal(in *os.File, out io.Writer) *Terminal

NewTerminal returns the IO over in and out (normally os.Stdin and os.Stdout).

func (*Terminal) Ask

func (t *Terminal) Ask(ctx context.Context, prompt string, timeout time.Duration) (string, bool)

Ask flushes pending input, prints prompt and waits for one line.

func (*Terminal) AskSecret

func (t *Terminal) AskSecret(ctx context.Context, prompt string) ([]byte, error)

AskSecret reads a line with echo off. Before the reader goroutine runs it uses secrets.PromptPassword; in cbreak mode it stops echoing itself; otherwise it turns the kernel echo off on in.

func (*Terminal) Bell

func (t *Terminal) Bell()

Bell rings the terminal bell.

func (*Terminal) Interactive

func (t *Terminal) Interactive() error

Interactive puts the terminal in cbreak mode (no line discipline, no kernel echo; signals and output post-processing kept) and starts reading it. Restore undoes it. An input that is not a terminal (a test pipe) stays as it is: lines are read, keys and the status line are off.

func (*Terminal) Keys

func (t *Terminal) Keys() <-chan Key

Keys delivers the control keys pressed while no prompt is active.

func (*Terminal) Lines

func (t *Terminal) Lines() <-chan string

Lines delivers the lines typed while no prompt is active.

func (*Terminal) Println

func (t *Terminal) Println(s string)

Println writes one line.

func (*Terminal) Restore

func (t *Terminal) Restore()

Restore ends the status line and puts the terminal back in the mode it had before Interactive.

func (*Terminal) SetKeyHandler

func (t *Terminal) SetKeyHandler(f func(Key))

SetKeyHandler sets the function that handles a control key pressed while a prompt waits. It runs on the goroutine that asked.

func (*Terminal) SetStatus

func (t *Terminal) SetStatus(s string)

SetStatus shows s on the bottom row of the terminal, above which the output scrolls; "" removes the row. Nothing happens before Interactive.

func (*Terminal) Width

func (t *Terminal) Width() int

Width is the terminal width in columns (80 when unknown).

Jump to

Keyboard shortcuts

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