remotepc

package
v0.0.0-...-b43479a Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package remotepc runs eta on another machine over SSH and forwards it to a local port, so that machine can be browsed as an ordinary peer.

Almost nothing here is implemented. The system `ssh` binary connects (as VS Code Remote-SSH and Ansible both do, rather than a Go SSH library — so this works exactly when the user's own `ssh <dest>` works). `go install` fetches, builds, pins the version, and verifies integrity against the checksum database. `ssh -L` reaches it, so ssh auth is the only auth and nothing is exposed remotely. --exit-on-stdin-close stops it, so there is no daemon to manage. GOPATH and GOCACHE live in ~/.eta so cleanup is one directory.

What's left is glue: an argv, two ports, two one-line commands, a marker scanner, a health poll.

Full rationale and citations: temp/remote-bootstrap-design.md.

Index

Constants

This section is empty.

Variables

View Source
var ErrAdopted = errors.New("eta on that PC was already running and was not installed from here, so there is nothing here to remove")

ErrAdopted reports an action that only makes sense for an eta this computer installed, attempted on one that was already running.

Functions

func Cleanup

func Cleanup(ctx context.Context, destination string) error

Cleanup removes eta's files from a remote. Separate from Stop: stopping is routine, removing isn't.

Types

type Manager

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

Manager keeps one session per destination. Without it, double-clicking twice starts a second remote eta that fails to bind, for no reason the user could act on.

func NewManager

func NewManager() *Manager

func (*Manager) Connect

func (m *Manager) Connect(ctx context.Context, opts Options) (*Session, error)

Connect returns a live session, establishing one only if needed.

func (*Manager) ConnectAsync

func (m *Manager) ConnectAsync(opts Options) error

ConnectAsync starts a connection and returns straight away. The caller polls Get for progress. Used by the HTTP layer, where a first connect can take minutes and holding a request open for it is not an option.

func (*Manager) Disconnect

func (m *Manager) Disconnect(destination string)

Disconnect stops one session.

func (*Manager) Get

func (m *Manager) Get(destination string) (*Session, bool)

Get returns the live session for a destination, if any.

func (*Manager) LastFailure

func (m *Manager) LastFailure(destination string) (error, bool)

LastFailure returns the memoized Begin-time error for a destination, if one exists. The status handler renders it as a failed setup rather than as a disconnected one, so a user who hit "Go not installed" or "Windows cmd.exe" actually sees that — the wording is the only thing the install path can say, and it used to be lost.

func (*Manager) Pending

func (m *Manager) Pending(destination string) (*Session, bool)

Pending reports a session that exists but is not ready yet, so status can distinguish "still working" from "never started".

func (*Manager) SetSessionForTest

func (m *Manager) SetSessionForTest(destination, url string)

SetSessionForTest inserts a ready Session into m.sessions for a destination, for handler tests that need to drive the status / identity-probe path without going through a real SSH install. The production path is Manager.Connect. The exited channel is pre-closed because a synthetic session has no real process to wait on, and StopAll in t.Cleanup would otherwise block on the unread channel.

func (*Manager) Starting

func (m *Manager) Starting(destination string) bool

Starting reports whether a connect attempt is in progress for a destination but has not yet produced a session. The status handler answers "connecting" for this case, since a poll that lands a few hundred ms after the POST — before Begin's adopt-probe/detectShell completes — would otherwise see no session and report "disconnected" even though a connection is on its way.

func (*Manager) StopAll

func (m *Manager) StopAll()

StopAll ends every session, and so every remote eta this computer started. For shutdown: the remote would exit on its own once stdin closed, but not promptly enough to leave to a timeout.

type Options

type Options struct {
	// Passed to ssh verbatim: hostname, user@host, or a ~/.ssh/config alias.
	Destination string
	// Default to this binary's own, so a remote never runs a different
	// version than the machine that started it.
	Module  string
	Version string
	// Port to look for an eta already running on that PC. Zero means
	// eta's own default. Set in tests so they never probe -- or adopt --
	// a real instance the developer is running on this machine.
	RemotePort int
	// Host is the local address the ssh forward binds to and the host
	// that ends up in the peer's URL. Empty means loopback (127.0.0.1),
	// which only works for a browser on the same machine as the
	// coordinator; the handler passes the request Host so the same
	// forward is reachable from the browser wherever it is.
	Host string
	// AccessHash is the encoded PBKDF2 verifier the coordinator has
	// already derived, forwarded to the remote so the freshly installed
	// eta on that PC has the same access password as the coordinator.
	// Empty means the coordinator has no password configured and the
	// remote is left without one — matching the no-password case the
	// coordinator itself is in.
	AccessHash string
	// RepoURL is the git repository URL used for shallow clone + build
	// instead of `go install`. Empty means use the legacy `go install`
	// path for backward compatibility.
	RepoURL string
}

type Phase

type Phase string

Phase is coarse progress, not a build log. Deliberately few.

const (
	PhaseConnecting Phase = "connecting"
	PhaseChecking   Phase = "checking"
	PhaseInstalling Phase = "installing"
	PhaseStarting   Phase = "starting"
	PhaseReady      Phase = "ready"
	PhaseFailed     Phase = "failed"
)

type Session

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

Session is one running remote eta, reachable at URL while alive.

func Begin

func Begin(ctx context.Context, opts Options) (*Session, error)

Begin connects to a PC, returning as soon as the ssh process is spawned — before it is ready. Callers reporting progress need the session while it converges: a first install compiles from source and can take minutes, and a session that only exists once it works cannot be observed getting there.

An eta already running on that PC is adopted rather than replaced. That is checked first, and if it answers, nothing is installed and nothing is started — see adopt.

func Start

func Start(ctx context.Context, opts Options) (*Session, error)

Start is Begin plus WaitReady: convenient when the caller has nothing to do until the session works.

func (*Session) Adopted

func (s *Session) Adopted() bool

Adopted reports that this session attached to an eta that was already running on that PC, rather than installing and starting one. An adopted eta is not ours: nothing was installed for it, and ending the session leaves it running.

func (*Session) Destination

func (s *Session) Destination() string

Destination is the stable identity: unchanged between sessions.

func (*Session) Err

func (s *Session) Err() error

Err is why the session failed, or nil. Unlike Wait it does not block, so a status request can report a failure without waiting for one.

func (*Session) Phase

func (s *Session) Phase() Phase

func (*Session) Recent

func (s *Session) Recent() []string

func (*Session) Stop

func (s *Session) Stop()

Stop closes stdin (the leash, which stops the remote eta) and kills ssh as a backstop. The kill is explicit because OS parent-death is Linux-only: docker/cli's own pdeathsig_nolinux.go is an empty func.

func (*Session) URL

func (s *Session) URL() string

URL is a forwarded loopback address, valid only while this session lives — so a saved peer must be keyed by Destination, never by this.

func (*Session) Wait

func (s *Session) Wait() error

func (*Session) WaitReady

func (s *Session) WaitReady(ctx context.Context) error

WaitReady polls until the forwarded port serves eta. Decided locally, not by a remote marker or a remote curl: it checks the thing that matters (reachable from here) and the remote needs no HTTP client. Gives up the moment ssh dies rather than waiting out its own timeout.

Safe to call more than once, and returns immediately once ready, so several callers can wait on the same converging session.

Jump to

Keyboard shortcuts

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