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

bootstrap

package
v0.40.0 Latest Latest
Warning

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

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

Documentation

Overview

Package bootstrap is the exchange between `upterm host` and the daemon it spawns, over the connection the daemon inherits. The child reports and asks; the parent shows, answers and, once the command has started, leaves.

Index

Constants

This section is empty.

Variables

View Source
var ErrDaemonGone = errors.New("the session daemon exited before reporting whether it started")

ErrDaemonGone: the daemon closed the channel before saying whether it started. Its log says why.

View Source
var ErrParentGone = errors.New("the process that started this session went away")

ErrParentGone: the process that started this session closed its end of the channel, or never answered.

Functions

This section is empty.

Types

type Child

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

Child is the daemon's half of the exchange.

One goroutine reads the parent's messages for the Child's life. Replies go to whoever is waiting for one; the protocol is strictly request/reply, so at most one is ever outstanding. A read error — the parent closing its end, or dying — while the Child is armed calls onParentGone, once: that is parent-lifetime cancellation, and Disarm is what ends it.

func NewChild

func NewChild(rw io.ReadWriteCloser, onParentGone func()) *Child

NewChild starts reading rw. It is armed: the parent leaving is abandonment until Disarm is called.

func (*Child) Claimed

func (c *Child) Claimed(m *api.Claimed) error

func (*Child) Close

func (c *Child) Close() error

Close disarms and closes the connection. Disarming first: the close makes the reader return, and a return after Disarm is not abandonment.

func (*Child) Disarm

func (c *Child) Disarm()

Disarm stops treating the parent's departure as abandonment. Call it the instant the command has started, before Started: a parent that exits the moment it is told the session is up must not be read as leaving it.

func (*Child) Failed

func (c *Child) Failed(msg string, nameInUse, abandoned bool)

Failed reports that the session did not start. Nothing is sent after Started: the parent has gone by then, and a session that started and then ended is an outcome the record carries, not a startup failure.

func (*Child) Listening

func (c *Child) Listening(socket string, hostKeys []string) error

func (*Child) Print

func (c *Child) Print(text string)

Print sends text for the parent's stderr. Best-effort: a parent that is gone will be noticed by the next thing that needs an answer.

func (*Child) ReadSecret

func (c *Child) ReadSecret(prompt string) ([]byte, error)

ReadSecret asks the parent's terminal for a line that must not echo.

func (*Child) Reader

func (c *Child) Reader() io.Reader

Reader is the stdin the host-key callback reads its confirmation from.

func (*Child) SessionCreated

func (c *Child) SessionCreated(sess *api.GetSessionResponse) (api.Accept_Decision, error)

SessionCreated reports that the tunnel is up and the session registered, and gets the operator's decision in the same round trip: the parent shows the session and answers with it.

func (*Child) Started

func (c *Child) Started(sessionID, status string, joinState *api.JoinState) error

Started reports that the command is running and the session's record has been published. Disarm first.

status is what the session's record says at this moment, and joinState the join timeout as the daemon holds it, both of which the parent reports rather than assuming: see the Started message in startup.proto.

func (*Child) Writer

func (c *Child) Writer() io.Writer

Writer is an io.Writer whose every Write is a Print — what the host-key callback and the version warning write their prose to.

type Conn

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

Conn frames Startup messages over a byte stream, one varint-delimited protobuf per message. Send is safe for concurrent use; Recv is for one goroutine.

func NewConn

func NewConn(rw io.ReadWriteCloser) *Conn

func (*Conn) Close

func (c *Conn) Close() error

func (*Conn) Recv

func (c *Conn) Recv() (*api.Startup, error)

func (*Conn) Send

func (c *Conn) Send(m *api.Startup) error

type Handlers

type Handlers struct {
	Claimed        func(*api.Claimed)
	SessionCreated func(*api.GetSessionResponse) api.Accept_Decision
	Listening      func(*api.Listening)
}

Handlers receives the child's reports. Each is called on Run's goroutine, between messages; SessionCreated blocks the exchange for as long as the operator takes, which is the point.

type NoAnswerError

type NoAnswerError struct{ Reason string }

NoAnswerError: the parent could not answer a read, and says why — no terminal for a secret, EOF on its stdin. Callers that wrap it in their own message (the host-key callback does) keep the reason.

func (*NoAnswerError) Error

func (e *NoAnswerError) Error() string

type Outcome

type Outcome struct {
	Started *api.Started
	Failed  *api.Failed
}

Outcome is how the exchange ended: exactly one of the two is set.

type Parent

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

Parent is `upterm host`'s half of the exchange.

func NewParent

func NewParent(rw io.ReadWriteCloser, stdin io.Reader, stderr io.Writer, readSecret func(prompt string) ([]byte, error)) *Parent

NewParent wraps the parent's end. stdin answers non-secret reads (nil: none can be answered); readSecret answers secret ones (nil: no terminal).

func (*Parent) Close

func (p *Parent) Close() error

Close closes the connection. Idempotent.

func (*Parent) Run

func (p *Parent) Run(ctx context.Context, h Handlers) (Outcome, error)

Run reads the child's messages until it reports started or failed. ctx ending closes the connection, which is what ends a Recv.

Jump to

Keyboard shortcuts

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