Esc

How borescope works

borescope is three thin, independently-testable layers. Understanding the seams helps you read its behaviour when something goes wrong.

Three layers

borescope is built as three layers, each with a narrow job and a clean seam to the next:

  1. Transport: talk to a Pebble.
  2. Discovery: find the right Pebble.
  3. Shell: give you a prompt over it.

A session flows top-down: discovery turns your unit reference into a target, the transport opens a connection to that target's Pebble, and the shell runs a REPL whose commands call the transport.

Transport

The transport is the only code that touches Pebble. Everything above it talks to a narrow Transport interface, a structural subset of ops.pebble.Client (services, plan, changes, checks, notices, and the files and exec APIs). There are two backends, both satisfying that interface:

Because the shell only ever sees the Transport interface, the backend choice is invisible to it, and a future reimplementation of just this layer (in Go, say) would sit entirely behind the same seam. How it reaches Pebble covers the backends in detail.

Discovery

Discovery turns a unit reference (myapp/0), plus optional --container and --model, into a fully-resolved target describing exactly which workload container's Pebble to talk to. It:

Crucially, everything here uses only your Juju model access (juju status and juju ssh to read metadata). Never kubectl, never cluster-admin. borescope inherits Juju's authority for free: if you can reach the unit with Juju, discovery succeeds; if you can't, it fails with the same boundary Juju would enforce.

When borescope runs inside a charm container (--here) or against an explicit socket (--socket), discovery short-circuits: there's no unit to resolve, just a socket to point at.

Shell

The shell is a small REPL: a line parser, a current-directory and environment context, path-aware Tab-completion, per-unit history, and a registry of commands. Commands are auto-discovered, each subclasses a common Command base, declaring its name, summary, and usage, so adding one is a small, self-contained change with no registration boilerplate.

The command set splits into three groups: shell built-ins (cd, pwd, …), file commands implemented over the files API (ls, cat, grep, …), and Pebble-native commands (services, logs, plan, …). The exec command is the escape hatch for everything else. See the command reference.

Why the separation

The layering isn't ceremony. It earns its keep: