backend

package
v0.13.0 Latest Latest
Warning

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

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

Documentation

Overview

Package backend obtains a reachable local CDP/VNC endpoint for a browser that runs in one of four places: a local docker container, a Kubernetes Deployment, docker on an ssh host, or a pre-exposed direct URL. Every CDP/VNC-facing operation runs against the Endpoint a backend yields, so the rest of cuttle is transport-agnostic.

Index

Constants

View Source
const DefaultContainerName = "cuttle"

DefaultContainerName is the docker container name used when --name is not set. The CLI's default flows through here so a caller can distinguish the default from an explicit --name (which keys a per-instance tunnel identity below).

View Source
const SuperviseTunnelSubcmd = "__supervise-tunnel"

SuperviseTunnelSubcmd is the hidden CLI subcommand spawnTunnel re-execs cuttle into: `cuttle __supervise-tunnel <forward-cmd> <args...>`. Keeping auto-reconnect in the cuttle binary itself avoids an external dependency like autossh.

Variables

This section is empty.

Functions

func SuperviseTunnel added in v0.10.3

func SuperviseTunnel(name string, args []string)

SuperviseTunnel runs the forward (name + args) in a restart loop until SIGTERM/ SIGINT, with capped exponential backoff that resets after a healthy run, so a dropped forward re-establishes on its own instead of staying down until the next `cuttle status`. It is the body of the hidden SuperviseTunnelSubcmd that spawnTunnel re-execs. exec.CommandContext ties each forward to ctx, so the signal that cancels ctx also kills the in-flight child; the child shares this supervisor's process group, so `cuttle down`'s killTunnel(-pid) stops both.

Types

type Backend

type Backend interface {
	State(ctx context.Context) (State, error)
	Start(ctx context.Context, opts StartOpts) error
	Stop(ctx context.Context, purge bool) error
	// Reach yields a local endpoint plus a release func that tears down any
	// tunnel opened to reach it. release is always safe to call (no-op for
	// direct/local). cdpPort/vncPort request specific local ports for a tunneled
	// backend (k8s/ssh) so a held forward is deterministic and a driver can attach
	// to it; 0 auto-picks a free port. The forward Reach opens is ephemeral by
	// design - it lives only until release is called (the CLI exit) - and is the
	// internal fallback for the short-lived open/login flows. A backend that also
	// implements Tunneler (k8s/ssh) additionally offers a detached, standing
	// forward that outlives the CLI (see tunnel.go); that is what up/status
	// advertise so the briefing endpoint is stable across invocations.
	Reach(ctx context.Context, cdpPort, vncPort int) (Endpoint, func(), error)
}

Backend manages one browser's lifecycle and reachability.

func New

func New(name, ctxName string, ctx config.Context, r Runner, cdpPort, vncPort int, image string) (Backend, error)

New builds the backend for a resolved context. Ports are the host-side CDP/VNC ports for the local backend (and the remote container ports for ssh). ctxName is the resolved context name; tunneled backends use it as the standing-tunnel pidfile identity - a non-default container name (--name) is folded in so two named instances sharing one ssh context get distinct tunnels.

type Direct

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

Direct targets a pre-exposed CDP/VNC endpoint from config as-is. It is the escape hatch for any browser cuttle does not itself manage (e.g. reached over a tailnet), so Start/Stop are errors.

func (*Direct) Reach

func (d *Direct) Reach(context.Context, int, int) (Endpoint, func(), error)

Reach uses the configured URLs as-is; there is no tunnel to release.

func (*Direct) Start

func (d *Direct) Start(context.Context, StartOpts) error

func (*Direct) State

func (d *Direct) State(ctx context.Context) (State, error)

func (*Direct) Stop

func (d *Direct) Stop(context.Context, bool) error

type Endpoint

type Endpoint struct {
	CDPHost string
	CDPPort int
	VNCHost string
	VNCPort int // 0 = no VNC
}

Endpoint is a reachable CDP (and optional VNC) address. For tunneled backends the host is loopback and the ports are auto-picked local forwards; for direct it is the configured host/port as-is.

type ExecRunner

type ExecRunner struct{}

ExecRunner is the production Runner backed by os/exec.

func (ExecRunner) LookPath

func (ExecRunner) LookPath(name string) (string, error)

func (ExecRunner) Output

func (ExecRunner) Output(ctx context.Context, name string, args ...string) (Result, error)

func (ExecRunner) Start

func (ExecRunner) Start(ctx context.Context, name string, args ...string) (Process, error)

type K8s

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

K8s runs the browser as a Helm-managed Deployment in a cluster, reached via kubectl port-forward. It shells out to kubectl/helm and inherits the user's kube context (and thus their routing) with zero cuttle-specific setup.

func (*K8s) EnsureTunnel added in v0.7.0

func (k *K8s) EnsureTunnel(ctx context.Context, cdpPort, vncPort int) (Endpoint, error)

EnsureTunnel establishes (or reuses) a detached `kubectl port-forward` on the fixed cdp/vnc ports that outlives the CLI. A forward dropped by a pod restart is reconnected by the supervisor (see spawnTunnel); status re-establishes it too on the next health-check if the supervisor itself is gone.

func (*K8s) LogsCommand added in v0.10.2

func (k *K8s) LogsCommand(follow bool) (string, []string)

LogsCommand targets pods by the release's instance label (matching State), so it works whatever the chart names the Deployment. A selector query defaults to 10 trailing lines; --tail=-1 (one token - a separate "-1" could parse as a flag) restores docker's show-everything.

func (*K8s) PurgeProfileVolume added in v0.9.0

func (k *K8s) PurgeProfileVolume(ctx context.Context) error

PurgeProfileVolume deletes the durable profile PVC, the k8s analogue of removing the named Docker volume. `cuttle purge-profile` uninstalls the release first (releasing the RWO claim), so this deletes a now-unbound PVC; a lingering PVC from a prior install is removed too.

func (*K8s) Reach

func (k *K8s) Reach(ctx context.Context, cdpPort, vncPort int) (Endpoint, func(), error)

Reach opens a kubectl port-forward. cdpPort/vncPort pin the local ports (so a held `cuttle connect` forward is deterministic and a driver can attach to it); 0 auto-picks free ports for the ephemeral status/login forwards, which then never collide with a local container already on 9222.

func (*K8s) Start

func (k *K8s) Start(ctx context.Context, opts StartOpts) error

func (*K8s) State

func (k *K8s) State(ctx context.Context) (State, error)

func (*K8s) Stop

func (k *K8s) Stop(ctx context.Context, purge bool) error

func (*K8s) StopTunnel added in v0.7.0

func (k *K8s) StopTunnel() error

type Local

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

Local runs the browser in a docker container on this host. It is a faithful port of the Python cuttle CLI's docker lifecycle, so existing behavior does not regress when no config file is present.

func (*Local) Diagnostics

func (l *Local) Diagnostics(ctx context.Context) []string

Diagnostics returns human-readable triage lines for an unhealthy container: the real host<-container port bindings and a log tail, so triage never needs a raw docker command. It is used by `status` via an optional interface.

func (*Local) DiscoverPorts added in v0.10.2

func (l *Local) DiscoverPorts(ctx context.Context) (int, int, bool)

DiscoverPorts reads the running container's published CDP/VNC host ports.

func (*Local) Image

func (l *Local) Image(ctx context.Context) string

Image reports the image an existing container was created with, or "".

func (*Local) LogsCommand added in v0.10.2

func (l *Local) LogsCommand(follow bool) (string, []string)

LogsCommand returns the docker argv that prints the container's logs.

func (*Local) PurgeProfileVolume added in v0.9.0

func (l *Local) PurgeProfileVolume(ctx context.Context) error

PurgeProfileVolume removes the persistent profile's named volume. The caller (`cuttle purge-profile`) removes the container first so the volume is detached.

func (*Local) Reach

func (l *Local) Reach(_ context.Context, _, _ int) (Endpoint, func(), error)

Reach for local is a direct loopback endpoint on the host-mapped ports; no tunnel, so release is a no-op.

func (*Local) Start

func (l *Local) Start(ctx context.Context, opts StartOpts) error

Start ensures the container is up, idempotently. A stopped container is restarted (profile preserved); a zombie (a run that died before a clean exit) is removed and re-run; --recreate forces a fresh container.

func (*Local) State

func (l *Local) State(ctx context.Context) (State, error)

func (*Local) Stop

func (l *Local) Stop(ctx context.Context, purge bool) error

type LogSource added in v0.10.2

type LogSource interface {
	LogsCommand(follow bool) (string, []string)
}

LogSource is implemented by backends whose browser logs cuttle can surface (local/ssh via docker logs, k8s via kubectl logs). LogsCommand returns the argv that prints them; the CLI execs it with the terminal attached so follow streams and Ctrl-C behave exactly like the underlying tool. The direct backend does not implement it - cuttle does not manage where that browser runs.

type PortDiscoverer added in v0.10.2

type PortDiscoverer interface {
	DiscoverPorts(ctx context.Context) (cdpPort, vncPort int, ok bool)
}

PortDiscoverer reports the host-published CDP/VNC ports of the running instance, so a caller targeting an existing instance need not restate the ports chosen at `up`. Implemented by the docker backends (local/ssh) via `docker port`; k8s (caller-chosen forward ports) and direct (URL-fixed) do not. Returns ok=false when the ports cannot be read - nothing running, or an unparseable mapping - and the caller then keeps its configured/default ports.

type Process

type Process interface {
	Stop() error
}

Process is a running command that can be stopped.

type ProfilePurger added in v0.9.0

type ProfilePurger interface {
	PurgeProfileVolume(ctx context.Context) error
}

ProfilePurger removes the persistent profile's backing store - the named Docker volume on local/ssh, the PVC on k8s - so the next start begins from a clean profile. Implemented by local, ssh, and k8s; the direct backend has no store cuttle manages.

type Result

type Result struct {
	Stdout string
	Stderr string
	Code   int
}

Result is a finished command's captured output and exit code.

type Runner

type Runner interface {
	// Output runs a command to completion and captures its output. A non-zero
	// exit is reported in Result.Code with a nil error (mirroring the Python
	// check=False); a nil error means only that the command ran.
	Output(ctx context.Context, name string, args ...string) (Result, error)
	// Start launches a long-running command (a tunnel) and returns a handle to
	// stop it.
	Start(ctx context.Context, name string, args ...string) (Process, error)
	// LookPath reports the resolved path of an executable, or an error if absent.
	LookPath(name string) (string, error)
}

Runner is the exec seam every backend goes through, so command construction is unit-testable without docker/kubectl/ssh installed.

type SSH

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

SSH runs the browser in docker on a remote host reached over ssh, tunneled to this machine with ssh -L. It inherits ~/.ssh/config (keys, jump hosts, and any routing the user provides), so cuttle needs no ssh setup of its own.

func (*SSH) DiscoverPorts added in v0.10.2

func (s *SSH) DiscoverPorts(ctx context.Context) (int, int, bool)

DiscoverPorts reads the remote container's published CDP/VNC host ports (the ports ssh -L then forwards), so a caller need only pass --context/--name.

func (*SSH) EnsureTunnel added in v0.7.0

func (s *SSH) EnsureTunnel(ctx context.Context, cdpPort, vncPort int) (Endpoint, error)

EnsureTunnel establishes (or reuses) a detached `ssh -N -L` forward on the fixed cdp/vnc ports that outlives the CLI.

func (*SSH) LogsCommand added in v0.10.2

func (s *SSH) LogsCommand(follow bool) (string, []string)

LogsCommand returns the ssh argv that streams the remote container's docker logs, reusing the ControlMaster socket like every other remote call.

func (*SSH) PurgeProfileVolume added in v0.9.0

func (s *SSH) PurgeProfileVolume(ctx context.Context) error

PurgeProfileVolume removes the persistent profile's named volume on the ssh host. The caller (`cuttle purge-profile`) removes the container first so the volume is detached.

func (*SSH) Reach

func (s *SSH) Reach(ctx context.Context, cdpPort, vncPort int) (Endpoint, func(), error)

Reach opens an ssh -L tunnel from local ports to the remote container's published ports, establishing the ControlMaster the other calls reuse. cdpPort/vncPort pin the local ports (so a held `cuttle connect` forward is deterministic and a driver can attach to it); 0 auto-picks free ports for the ephemeral status/login forwards.

func (*SSH) Start

func (s *SSH) Start(ctx context.Context, opts StartOpts) error

func (*SSH) State

func (s *SSH) State(ctx context.Context) (State, error)

func (*SSH) Stop

func (s *SSH) Stop(ctx context.Context, purge bool) error

func (*SSH) StopTunnel added in v0.7.0

func (s *SSH) StopTunnel() error

type StartOpts

type StartOpts struct {
	Image    string
	Recreate bool
	// KeepProfile is the legacy override for the durable-profile behavior:
	// nil = backend default (persist), false = ephemeral. --ephemeral is the
	// preferred opt-out and sets Ephemeral instead. See persistProfile.
	KeepProfile *bool
	// Ephemeral opts out of the persistent default profile: no named volume, a
	// fresh scratch profile that is discarded on recreate/down --purge.
	Ephemeral bool
	// PurgeProfile resets the persistent profile before (re)creating the
	// container/pod: it drops the named volume (docker local/ssh) or the PVC (k8s)
	// so the next start begins from a clean profile.
	PurgeProfile bool
	Proxy        string
	IdleTimeout  string // seconds of idle before a per-seed browser is reaped; "" = off
	Screen       string // "WxH" the browser claims and is sized to (CUTTLE_SCREEN); "" = daemon default
	// Humanize overrides behavioral input humanization: nil = daemon default (on),
	// &false = disable (CUTTLE_HUMANIZE=0), &true = force on. Only the disable case
	// is passed through, since the daemon defaults humanize on.
	Humanize *bool
	// AllowContextCreation lets drivers call Target.createBrowserContext instead of
	// having it rejected. Only the enable case is passed through, since the daemon
	// defaults it off.
	AllowContextCreation bool
}

StartOpts carries the per-invocation choices for Start. Not every field applies to every backend (e.g. Recreate is docker-only); a backend ignores what it does not use.

func (StartOpts) Persistent added in v0.9.0

func (o StartOpts) Persistent() bool

Persistent reports whether the default profile is durable (a named volume / PVC mounted at the container's data dir, plus CUTTLE_KEEP_PROFILE=1 so the daemon treats it as the source of truth). Persist-by-default: --ephemeral (or the legacy --keep-profile=false) opts out. It is the single source of truth for the persist decision - the CLI and every backend derive from it.

type State

type State string

State is a browser's lifecycle state as a backend sees it.

const (
	StateRunning State = "running"
	StateStopped State = "stopped"
	StateAbsent  State = "absent"
)

type Tunneler added in v0.7.0

type Tunneler interface {
	// EnsureTunnel returns a stable local endpoint, (re)spawning the detached
	// forward on the given ports when none is healthy.
	EnsureTunnel(ctx context.Context, cdpPort, vncPort int) (Endpoint, error)
	// StopTunnel tears down the standing forward, if any.
	StopTunnel() error
}

Tunneler is implemented by backends that reach the browser through a local forward (ssh, k8s). Unlike Reach's ephemeral forward, EnsureTunnel establishes a detached, long-lived forward on fixed ports that outlives the CLI process, so the briefing can advertise a stable 127.0.0.1 endpoint across invocations. local/direct do not implement it (their endpoint is already stable and needs no process).

Jump to

Keyboard shortcuts

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