Run a local process as if it were inside your cluster: prefix any command with plug and it resolves the cluster DNS and reaches the cluster services like any workload of the stack - no code change, no proxy configuration in your app. One tiny agent container on the cluster side, one static CLI on yours. Docker, Swarm, Kubernetes and OpenShift.

Profiles & versions

A profile is one cluster: a plain file in ~/.plug/<name>.conf. plug picks the profile automatically, so your daily command carries no -p.

Automatic selection

The installer normally creates a profile for you, named after the cluster host it reads from your ssh get@<host> command, or as you name it on that command (install my-cluster) - so the wizard below is really just the fallback when no profile exists yet. Install from several clusters and each gets its own profile.

  • No profile → a short wizard asks for a name, the cluster host and the agent port (default 2222), saves the profile and uses it immediately.
  • One profile → used as is (plug prints which one, for transparency).
  • Several profiles → interactive numbered choice - or skip the question with -p <name>.
$ plug -s my-app:8080:3000 npm run start:dev
[plug] no profile in /Users/you/.plug - let's create one
profile name [default]:
cluster host: swarm-node.example.com
agent port [2222]:
[plug] profile "default" saved to /Users/you/.plug/default.conf
[plug] running your command

The profile file

Created by the wizard, editable by hand - just a host and a port:

# ~/.plug/staging.conf
host = swarm-node.example.com
port = 2222

What goes to the cluster vs direct (split-horizon)

plug decides per destination by the shape of the name:

  • Single-label names (pdfbox, mongodb) → the cluster, resolved cluster-side.
  • Dotted FQDNs (api.github.com, a LAN host) → resolved and connected directly on your machine.
  • localhost and 127.0.0.1 always stay local.

Several clusters at once

Run the same process against two clusters in parallel - each stays isolated:

plug -p prod -s my-app:8080:3000 npm run start   # → cluster prod
plug -p staging -s my-app:8080:3000 npm run start   # → cluster staging, side by side

The same service names resolve to the right backend in each. How plug keeps the two apart differs by OS (a private resolver per launch on Linux, one tunnel per cluster attributed at connect() on macOS and Windows): see how plug tells them apart and the coverage matrix.

Managing profiles

plug ls                  # list profiles: name  host:port
plug test [profile]      # check an agent is reachable (prints its version)
plug rn default prod     # rename a profile (alias: plug mv)
plug rm staging          # remove a profile

plug test connects to the agent and reports its version - handy to confirm a cluster is reachable before running your app.

One-shot, without a profile

Point plug straight at a cluster with --host (and --port if it isn't 2222) - handy for a quick try or for CI. It overrides the selected profile for that one run:

plug --host swarm-node.example.com --port 2222 -s my-app:8080:3000 npm run start:dev

Versions - the launcher model

plug is a small launcher, like nvm or rustup. The binary on your PATH does almost nothing itself: on each run it asks the agent which version it speaks, then executes that exact version from ~/.plug/versions/ - downloading it once from the cluster if missing.

  • Each cluster runs its own version. A cluster on 1.1 and one on 1.3 both work - no in-place replacement, no upgrade churn on the binary you installed.
  • Correct by construction. The CLI can never drift from the agent it talks to, because it literally runs the agent's version.
  • Cheap. Cached binaries are a few MB each; the first connect to a new version pays one small download.
plug version         # the launcher's own version
plug versions        # launcher + every cached cluster version

The launcher itself almost never needs updating: the download protocol it speaks to the agent (version + <os-arch>) is frozen - if it ever must change, just reinstall it from the cluster.

Updating a cluster

New plug release? plug update walks that chain upstream: the agent moves itself to the newest release, then the launcher refreshes itself from the agent and re-applies its privilege. Live sessions ride the roll out - they reconnect onto the new agent by themselves.

Moving itself means the deployment's tag is rewritten when it pins a release - softwarity/plug:2.3.0 becomes softwarity/plug:2.4.0, majors included. plug is infrastructure carrying your sessions, not an application dependency you hold back for reproducibility; and re-resolving a pinned tag could only ever return the same image, which made update a no-op exactly where it was needed. Each backend applies it its own way: Swarm updates the service's image, Kubernetes patches the Deployment's container image, and a plain container - which cannot recreate itself - pulls the new image and hands you the one command that does.

plug update follows the tag the deployment already carries. To move a cluster to a different one, name it: plug -p neo update tag takes the newest release published, plug -p neo update latest the latest stream, and plug -p neo update feat-09 a branch's tag at whatever it points to now - an exact release (2.3.0) works too, downgrades included. The agent checks the tag exists on the registry before repointing anything: aiming a deployment at a tag nobody published would leave you with an agent that cannot pull.

A moving tag (latest, main, a branch) is left exactly as it is and simply re-pulled: it already resolves to whatever its publisher last pushed, and repointing it would override a deliberate choice. And when a pinned deployment is already on the newest release, plug says so immediately instead of rolling the workload to find out.

Being told a version is there

Once a day, in the background, a session checks whether the registry carries a release the cluster's agent does not, and says so the next time you run plug. What happens then is the profile's policy:

plug config -p neo                  # show that cluster's policy
plug config -p neo update=notify    # default: tell you, and offer to apply
plug config -p local update=auto    # apply it, on a cluster you govern
plug config -p shared update=none   # never look, on one you do not

The policy belongs to the profile, not to the machine, because applying an update rolls the cluster's agent, which every session on that cluster shares: you may govern your own cluster and have no say over a shared one. notify asks only on a real terminal, defaults to no and gives up after a few seconds; a script or a CI job sees the message and never a question. A running command keeps the version it started with; the new one is used at the next launch.

A deployment on a moving tag (latest, a branch) is compared by digest, since its name never changes. When the registry is out of reach from your machine (a proxy, a VPN that splits routes), plug asks the cluster, which reaches it.