Esc

How it reaches Pebble

The trick that makes borescope work against a rock with no shell, and the two ways it connects.

The problem

A Kubernetes charm runs its workload in a separate container called a rock. The rock typically contains only the workload: no shell, no ls, no cat, sometimes a single static binary and nothing else. So when something breaks, juju ssh --container=workload myapp/0 either fails outright or drops you into a container where you can't run a single command. The very thing you'd reach for to debug isn't there.

The key insight

Every Kubernetes charm workload is supervised by Pebble, and Pebble exposes a complete API: list and control services, read the plan, follow logs, run health checks, and, importantly, read and write files and execute processes in the container. Anything you'd want a shell for, Pebble can already do for you.

So borescope doesn't need a shell in the rock. It maps familiar commands onto Pebble's API: ls becomes a files-list call, cat a files-pull, exec a Pebble exec. The rock can be completely empty and ls /var/log still works.

The remaining question is how to reach that Pebble. borescope has two transports for it.

Through the charm container (the default)

The Pebble you care about is the workload's, but the workload container has no shell to run a client from. The charm container, however, always has a normal filesystem and shell, and Juju mounts the workload's Pebble socket into it at /charm/containers/<name>/pebble.socket.

So borescope's default CliTransport does this: juju ssh into the charm container (which works, because that container has a shell), and there run the pebble CLI pointed at the workload's mounted socket. It drives that CLI with shimmer, a drop-in ops.pebble.Client that speaks to the Pebble binary instead of an HTTP socket, so the rest of borescope sees an ordinary Client.

This is what lets borescope debug a shell-less rock from your workstation: the shell it borrows lives in the charm container, not the workload, and the only access it needs is the juju ssh you already have.

Direct to the socket

When the Pebble socket is directly reachable, there's no need to go through Juju at all. SocketTransport uses the real ops.pebble.Client HTTP API over the Unix socket. This is the faster path, and borescope uses it when you run:

See Run inside the charm container for how to pick this path.

ssh vs exec relay

When going through Juju (the default mode), borescope can use one of two relays, chosen with --via:

Both reach the same workload Pebble through the charm container; they differ only in the Juju mechanism used to get there.

Authority, not privilege

Notice what borescope never does: it doesn't touch kubectl, doesn't read a kubeconfig, and doesn't need cluster-admin. Every path to the container goes through Juju (juju status, juju ssh, juju exec), so borescope operates strictly within the authority Juju already grants you for that model. It can't reach anything you couldn't reach by hand, and it fails at exactly the same boundaries. That's a deliberate design choice, not a limitation to work around; see Scope and philosophy.