heliograph

Debug a machine you cannot log into, through someone who cannot debug it

License: MIT Claude Code Platform

A free, open-source tool by DBHQ


Does this sound familiar

  • You have no SSH access to production, and you are not going to be given any.
  • The environment is air-gapped, or behind a bastion, a jump host or a VPN you are not on.
  • It is a client-owned or customer-managed estate. Only their staff can log in.
  • An MSP, an offshore team or a colleague on site owns the console, and they have their own work to do.
  • Access is blocked by policy, not capability: regulated, restricted, change-controlled.
  • You are on the fourth round of "can you run this and paste the output", and what came back was a screenshot of half a terminal.
  • You are an AI coding agent driving an investigation, and you need the evidence rather than somebody's summary of it.

If you can just SSH in, you do not need this.

What it does

A heliograph signals across a valley by flashing sunlight off a mirror. No wire, no connection, just a message that gets across a gap and an answer that comes back the same way. This is that, for operations work: git is the transport in both directions.

You write the step. Someone on the far side runs one command they never have to change. The whole run comes back as a log: every line timestamped in UTC, ANSI stripped, obvious secrets masked, committed and pushed.

you        push a step ──────────────────────────────▶ transport repo
operator                                        ──────▶ git pull && ./run.sh
           the log is captured and pushed ──────▶ transport repo
you        git pull, read ops-logs/<step>-<UTC>.txt ◀──

It is deliberately generic. Ansible, Terraform, Terragrunt, a Kubernetes cluster that will not form, a Windows Server box over SSH, a hung service, a failing cluster join: anything you can express as a command.

Better still, nobody relays anything. The operator starts ./start.sh once and walks away. It proves the machine can capture and push, then hands off to the agent, which watches the branch, runs what you ask for, and pushes the log back. After that the loop is git in both directions, and nobody has to be sitting on the far side.

That loop is read-only unless the operator said otherwise. Every step declares itself (# heliograph-mode: read-only or action) in its own file, one that declares neither does not run, and the unattended agent refuses an action unless it was started with --allow-actions. It will not run as root either: the account is the whole blast radius, because this toolkit holds no credentials of its own. SECURITY.md is honest about where that stops.

Two properties that make a log-only loop workable

Every line carries a UTC timestamp, so a hang shows up as a gap. After the fact, in an untimed log, a hang and slow progress are indistinguishable. This is the single most useful property of these logs.

The exit code survives and the log is pushed even on failure, so a failed run reads as clearly as a successful one and a round trip is never wasted.

Install

As a Claude Code plugin (recommended)

/plugin marketplace add dbhq-uk/marketplace
/plugin install heliograph@dbhq

Any agent (Cursor, Copilot, Windsurf, Gemini, Cline and more)

npx skills add dbhq-uk/heliograph-skill

The skills.sh CLI installs into whichever agent directories it finds, so this works outside Claude Code and Codex too.

Local install (Claude Code or Codex)

git clone https://github.com/dbhq-uk/heliograph-skill.git
cd heliograph-skill
./install.sh          # Claude Code: symlinks into ~/.claude/skills (edits are live)
./install-codex.sh    # Codex: installs into ~/.codex/skills

Without an agent at all

The toolkit is plain bash and stands on its own. Copy it into a fresh private repo and drive it by hand:

git clone https://github.com/dbhq-uk/heliograph-skill.git
./heliograph-skill/skills/heliograph/scripts/bootstrap.sh ~/my-investigation

Bash 4+, git and GNU coreutils. No packages, no credentials, no network beyond the git remote itself. The GNU spellings matter: sed -u is what keeps the capture unbuffered, so each line is stamped when it is produced rather than when the block flushes. On macOS, install coreutils and gnu-sed and put them first on PATH.

Usage

Ask in any session:

"heliograph: set up a transport repo for the payments cluster"
"heliograph a step that proves whether node A can reach node B on 5985"
"read the log that just came back and tell me what it measured"

The skill bootstraps the transport repo, writes the steps, drives the runner and reads the logs. What you decide is which question to ask next.

How it works

  1. Bootstrap a transport repo. Private, its own repo, cloned on the control node by whoever can reach it
  2. Baseline first. ./run.sh env answers what that box actually is: OS, tools, sudo, proxy, DNS, cloud auth, and which commit of the repo is checked out
  3. One branch per investigation. TASK.md holds the question, the measurements and the conclusions, and keeps the last two apart
  4. One step per question. Copy the template, write the probes, register it, push
  5. The operator pulls and runs, or ./start.sh checks the machine and starts the agent unattended
  6. The log comes back over git, timestamped and complete, whether the step passed or failed

What ships

start.sh the first command on a new machine: proves it can capture and push, then starts the agent
run.sh the step runner. The operator's one command
agent.sh the unattended loop: watches for a request, runs it, pushes. Cancellable mid-run
caprun.sh wrap any ad-hoc command in the same capture and push
caplib.sh the shared capture, log and push functions
azure/ eight templates for running the agent in Azure: ACI, Web App for Containers, Container Apps Job and a VM, each in bicep and Terraform
secret.sh carry a value the other way, as ciphertext, when the far side needs one
steps/env-snapshot.sh control-node baseline: OS, tools, auth, proxy, DNS, git
steps/net-probe.sh DNS, ICMP and a TCP matrix, in both directions
lib/ probe.sh, remote.sh (SSH and Windows), terraform.sh, ansible.sh, tfguard.sh
docker/ Dockerfile, entrypoint.sh, heliograph.sh - run the control node in a container instead
ops-logs/ the captured runs. Tracked deliberately

Plus the half that is not code: the method the tooling exists to serve. Measure rather than infer, keep a control, run it in all directions, never truncate. See references/method.md, which is worth reading before a hard investigation.

Running it in a container

For a control node where installing bash, git and coreutils by hand is its own change request:

skills/heliograph/toolkit/docker/heliograph.sh <transport-repo-url>

It builds the image if it is missing, clones the transport repo, and hands over to that repo's own start.sh - nothing about the loop itself changes. The image ships two ways: on a pushed version tag, so an estate can say exactly which one they ran, or built locally from the same Dockerfile for an estate that will not pull a third-party image. The user inside is unprivileged but has passwordless sudo, which is not a security boundary - see references/container.md for what it buys instead, and for what has and has not actually been proven about the publishing side of this.

Logs and secrets

ops-logs/*.txt is tracked, not ignored. The log is the deliverable, and committing it is how a run escapes a machine nobody can reach.

Because logs are committed and pushed, anything a command prints is in git history permanently. cap_redact masks two kinds of shape on the way out: by position (password=, an Authorization: header with any scheme, a credential carried in a URL, private keys) and by the prefixes vendors publish so that scanners can spot them (ghp_, github_pat_, AKIA, xox, glpat-, sk-, JWTs). It is a safety net, not a guarantee. Do not run things that print secrets, and keep the transport repo private.

Occasionally a value has to travel the other way: something the far side needs and cannot fetch for itself. secret.sh carries it as ciphertext, with the passphrase defined by a human on both machines and never committed, so git carries something useless on its own. It is transport rather than storage, and references/secrets.md is honest about the limits of that.

What this will not do

Give you access you do not have. It does not tunnel, proxy or hold a connection open, and there is nothing here to punch through a firewall with. Every command runs on the far side because someone with legitimate access chose to run it, and the only thing that crosses the gap is a git commit.

It will not resolve a merge conflict, force a push, or discard the operator's local work either. When the loop cannot proceed it says so, keeps the log, and carries on polling.

Development

CONTRIBUTING.md covers working on it and AGENTS.md is for an AI agent doing so. The skill itself is skills/heliograph/SKILL.md, and docs/dev-setup.md sets it up from source with live edits.

License

MIT © 2026 DBHQ Consulting Ltd