{"id":51695696,"url":"https://github.com/tonyandrewmeyer/borescope","last_synced_at":"2026-07-16T05:04:46.937Z","repository":{"id":361385361,"uuid":"1254261654","full_name":"tonyandrewmeyer/borescope","owner":"tonyandrewmeyer","description":"A shell for containers that have no shell","archived":false,"fork":false,"pushed_at":"2026-07-15T21:00:16.000Z","size":5275,"stargazers_count":4,"open_issues_count":11,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-15T22:28:36.864Z","etag":null,"topics":["debugging","juju","kubernetes","pebble","rocks","shell"],"latest_commit_sha":null,"homepage":"https://tonyandrewmeyer.github.io/borescope/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tonyandrewmeyer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-30T10:48:54.000Z","updated_at":"2026-07-15T21:00:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tonyandrewmeyer/borescope","commit_stats":null,"previous_names":["tonyandrewmeyer/borescope"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/tonyandrewmeyer/borescope","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyandrewmeyer%2Fborescope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyandrewmeyer%2Fborescope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyandrewmeyer%2Fborescope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyandrewmeyer%2Fborescope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tonyandrewmeyer","download_url":"https://codeload.github.com/tonyandrewmeyer/borescope/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyandrewmeyer%2Fborescope/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35531168,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-16T02:00:06.687Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["debugging","juju","kubernetes","pebble","rocks","shell"],"created_at":"2026-07-16T05:04:46.237Z","updated_at":"2026-07-16T05:04:46.932Z","avatar_url":"https://github.com/tonyandrewmeyer.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# borescope\n\nA natural shell for debugging Juju Kubernetes **workload** containers.\n\nKubernetes charm workload containers usually run a [rock](https://documentation.ubuntu.com/rockcraft/)\nwith no shell — so when something breaks, `juju ssh --container=workload …` drops\nyou nowhere useful. borescope gives you a prompt that *feels* like `bash` but talks to\nthe container's [Pebble](https://documentation.ubuntu.com/pebble/) instead of a real shell:\n\n```console\n$ borescope myapp/0\npebble:/# ls /var/log/myapp\npebble:/# tail -f /var/log/myapp/error.log\npebble:/# services\npebble:/# logs --follow myapp\npebble:/# plan\npebble:/# exit\n```\n\nNo setup ceremony: borescope picks up your current Juju controller/model and uses your\nexisting `juju` authority — if you can `juju ssh` to the unit, borescope works; if you\ncan't, it fails the same way.\n\n## Install\n\nFrom the [snap store](https://snapcraft.io/borescope):\n\n```console\nsudo snap install borescope\nsudo snap connect borescope:juju-client-observe\nsudo snap connect borescope:ssh-keys\n```\n\nThe two `snap connect` calls are required today and will go away once\nthe snap store grants the auto-connect declarations\n([#31](https://github.com/tonyandrewmeyer/borescope/issues/31)).\nWithout them, borescope can't read your `~/.local/share/juju` to find\nyour controller, and `juju ssh` can't see your SSH keys.\n\nA few things to know about the snap:\n\n- It bundles its own juju (currently `juju/4/stable`), so it works\n  even without juju installed on the host.\n- It reads your `~/.local/share/juju` (JUJU_DATA) read-only via the\n  `juju-client-observe` interface, then copies it into a writable\n  per-snap directory at startup. **Run `juju login` / `juju switch`\n  outside borescope** — changes made inside a borescope session don't\n  propagate back to the host JUJU_DATA.\n\nOr from [PyPI](https://pypi.org/project/borescope/):\n\n```console\nuv tool install borescope    # or: uvx borescope, pipx install borescope\n```\n\n## Usage\n\n```console\nborescope \u003cunit\u003e                       # default (first) workload container\nborescope \u003cunit\u003e --container=\u003cname\u003e    # a specific workload container\nborescope --model \u003cmodel\u003e \u003cunit\u003e\nborescope \u003cunit\u003e --command \"services\"  # one-shot, no REPL (for scripts)\nborescope \u003cunit\u003e --snapshot            # dump container state as JSON\n```\n\n## Documentation\n\nFull documentation — a tutorial, how-to guides, and CLI/command reference — is\nat **\u003chttps://tonyandrewmeyer.github.io/borescope/\u003e**.\n\nThe docs are plain Markdown under [`docs/src/`](docs/src/), built into static\nHTML with a small script (no docs framework). To build them locally:\n\n```console\nuv run python docs/src/_build.py     # or: tox -e docs\n```\n\nSee [`docs/README.md`](docs/README.md) for the authoring rules.\n\n## How it works\n\nborescope is three thin, independently-testable layers:\n\n- **Transport** — talks to a Pebble. The primary backend (`CliTransport`) reaches the\n  workload's Pebble *through the charm container* — `juju ssh \u003cunit\u003e` (the charm\n  container always has a shell) pointed at the workload's socket, which Juju mounts\n  there at `/charm/containers/\u003cname\u003e/pebble.socket`. This works even against rocks\n  with **no shell** (the shell lives in the charm container, not the rock) and stays\n  entirely within your Juju authority — no `kubectl` or cluster-admin. It drives\n  `pebble` via [shimmer](https://github.com/tonyandrewmeyer/shimmer) (a drop-in\n  `ops.pebble.Client` over the Pebble CLI). When the Pebble socket is directly\n  reachable (running inside the charm, or a local Pebble), `SocketTransport` uses the\n  real `ops.pebble.Client` HTTP API instead.\n- **Discovery** — turns a unit reference into the right Pebble: confirms the unit,\n  reads the charm's `metadata.yaml` for workload container names, and sanity-checks\n  the container is alive. Everything uses your Juju model access — no `kubectl` /\n  cluster-admin.\n- **Shell** — a small REPL: `cd`/`pwd`, path-aware tab completion, history, and a\n  minimal command set. Pebble's own vocabulary (`services`, `logs`, `plan`, …) is\n  first-class, not hidden behind a `pebble` prefix. For anything else, `exec \u003ccmd\u003e`\n  runs a binary that's already in the container.\n\n## Scope\n\nborescope is for **Kubernetes** charms (which run Pebble). Machine charms already have\na real shell and are out of scope. It deliberately ships a *minimal* command set and\ngrows on request — if a tool exists in the container, reach it with `exec`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftonyandrewmeyer%2Fborescope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftonyandrewmeyer%2Fborescope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftonyandrewmeyer%2Fborescope/lists"}