Documentation
¶
Overview ¶
Package provision orchestrates a deploy as discrete steps so the TUI can show progress: validate credentials, mint a Tailscale key, render cloud-init, create the server, and wait for it to join the tailnet.
Index ¶
- func AuthorizeKey(ctx context.Context, user, host, pubkey string) error
- func AuthorizeKeys(ctx context.Context, user, host string, keys []string) (added int, err error)
- func Destroy(ctx context.Context, cfg config.Config) error
- func FetchSSHIDKeys(ctx context.Context, handle string) ([]string, error)
- func ParseAuthorizedKeys(body string) []string
- func ResolveFQDN(ctx context.Context, name string) (fqdn, ip string)
- func ValidHandle(s string) bool
- func ValidPubKey(s string) bool
- type Check
- type Provisioner
- func (p *Provisioner) AdoptServer(ctx context.Context) error
- func (p *Provisioner) Agents() []agents.Agent
- func (p *Provisioner) BootProgress(ctx context.Context) (status, detail string, err error)
- func (p *Provisioner) Connect(ctx context.Context) error
- func (p *Provisioner) CreateServer(ctx context.Context) error
- func (p *Provisioner) Node() (name, ip string)
- func (p *Provisioner) PrepareTailscale(ctx context.Context) error
- func (p *Provisioner) Render() error
- func (p *Provisioner) ResolveHost(ctx context.Context)
- func (p *Provisioner) SSHUserHost() (user, host string)
- func (p *Provisioner) SyncLocal(ctx context.Context) error
- func (p *Provisioner) ValidateCredentials(ctx context.Context) error
- func (p *Provisioner) Verify(ctx context.Context) ([]Check, error)
- func (p *Provisioner) WaitForTailnet(ctx context.Context) (string, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AuthorizeKey ¶
AuthorizeKey appends one pubkey to the box's authorized_keys over SSH, so a second device (your phone) can connect using a key it generated itself — the private half never leaves that device. The laptop's own (already-trusted) key is what logs in to do this. Idempotent: re-adding the same key is a no-op.
func AuthorizeKeys ¶
AuthorizeKeys appends every valid public key in keys to the box's authorized_keys in a single SSH round-trip, idempotently, and reports how many were newly added (already-present keys are skipped). Invalid lines are dropped.
func Destroy ¶
Destroy deletes the server + firewall and revokes the tailnet node. A server pocketdev adopted (rather than created) is never deleted — only its tailnet node is revoked — since the user owned that machine first.
func FetchSSHIDKeys ¶
FetchSSHIDKeys pulls the public keys published for a Termius SSH ID handle from https://sshid.io/<handle> (the same list `curl` returns). Each device's private key stays on that device; only these public halves are ever fetched. Every line is validated before it can be installed.
func ParseAuthorizedKeys ¶
ParseAuthorizedKeys keeps only the lines of body that are valid SSH public keys.
func ResolveFQDN ¶
ResolveFQDN returns the box's full MagicDNS FQDN (no trailing dot) and 100.x IP from the local Tailscale CLI. Empty strings if it can't be found.
func ValidHandle ¶
ValidHandle checks an SSH ID handle is safe to put in a URL path.
func ValidPubKey ¶
ValidPubKey does a cheap structural check on an SSH public key line and, since the value is interpolated into a remote shell command, rejects shell/line metacharacters so it can never break out of the single-quoted context.
Types ¶
type Provisioner ¶
type Provisioner struct {
// contains filtered or unexported fields
}
Provisioner carries state across the deploy steps.
func New ¶
func New(cfg config.Config) (*Provisioner, error)
New validates the config shape and builds the clients.
func (*Provisioner) AdoptServer ¶
func (p *Provisioner) AdoptServer(ctx context.Context) error
AdoptServer bootstraps an existing server over SSH: it pipes a generated script (tailscale join + on-box files + agent install) to the box. Nothing is written to the box's disk except the intended files; the script arrives on stdin.
func (*Provisioner) Agents ¶
func (p *Provisioner) Agents() []agents.Agent
Agents returns the resolved agent set (for the result screen).
func (*Provisioner) BootProgress ¶
func (p *Provisioner) BootProgress(ctx context.Context) (status, detail string, err error)
BootProgress returns one snapshot of cloud-init's state: status is "done"/"running"/"error"/"" and detail is the latest activity (the last output-log line if readable, else cloud-init's own detail field). err is set only when the box can't be reached yet (e.g. sshd not up) — callers keep polling.
func (*Provisioner) Connect ¶
func (p *Provisioner) Connect(ctx context.Context) error
Connect opens an interactive SSH session that runs `pocketdev setup` on the box (agent logins, gh auth, clone) and returns when the user exits it. It also reverse-forwards a local URL-opener so the box's browser-opens (gh/claude auth) pop in the LAPTOP browser instead of failing on the headless box.
func (*Provisioner) CreateServer ¶
func (p *Provisioner) CreateServer(ctx context.Context) error
CreateServer creates the box (idempotent: skips if the name already exists).
func (*Provisioner) Node ¶
func (p *Provisioner) Node() (name, ip string)
Node returns the box's display name (tailnet label, possibly name-N) and IP.
func (*Provisioner) PrepareTailscale ¶
func (p *Provisioner) PrepareTailscale(ctx context.Context) error
PrepareTailscale mints a short-lived tagged key (OAuth path) and revokes any stale node with the same hostname so the new box keeps the clean MagicDNS name.
func (*Provisioner) Render ¶
func (p *Provisioner) Render() error
Render builds the cloud-init document.
func (*Provisioner) ResolveHost ¶
func (p *Provisioner) ResolveHost(ctx context.Context)
ResolveHost refreshes the box's online tailnet IP/name from the local CLI. Best-effort; used between boot-progress polls so we always target the live node.
func (*Provisioner) SSHUserHost ¶
func (p *Provisioner) SSHUserHost() (user, host string)
SSHUserHost returns the login user and the best host (tailnet IP, else name).
func (*Provisioner) SyncLocal ¶
func (p *Provisioner) SyncLocal(ctx context.Context) error
SyncLocal rsyncs the chosen local folder/file to the box over the tailnet, after it has joined. A directory is copied into ~/<name>/; a file into ~/.
func (*Provisioner) ValidateCredentials ¶
func (p *Provisioner) ValidateCredentials(ctx context.Context) error
ValidateCredentials probes Hetzner and (if used) the Tailscale OAuth client.
func (*Provisioner) Verify ¶
func (p *Provisioner) Verify(ctx context.Context) ([]Check, error)
Verify SSHes in and checks that each agent CLI is installed (and the rsync'd folder is present, if applicable).
func (*Provisioner) WaitForTailnet ¶
func (p *Provisioner) WaitForTailnet(ctx context.Context) (string, error)
WaitForTailnet blocks until the box joins the tailnet. With an OAuth token it polls the Tailscale API; otherwise it watches the local `tailscale` CLI. Local detection is best-effort: if the CLI is absent or the node hasn't appeared, it returns ("", nil) rather than failing a deploy that is otherwise fine.