heliograph
View as markdown

What works with what

Three things have to line up before a single command runs: a transport to carry it, a station to run it, and a controller to publish it from. This page is all three, and which combinations actually work.

The tables are generated from one source, so they cannot disagree with each other. Where a row says something is unproven, that is the honest state rather than modesty.

Every transport is one of three shapes

This is the distinction the whole design rests on, and it decides more about an estate's answer than any other fact on this page. The axis is one thing: what is held, and for how long.

beaconA signal left where both can see it. You cannot reach the far side, the far side cannot reach you, and both can reach one agreed place. A message waits in the middle; nobody is ever connected
flareFired straight at a reachable endpoint. One burst, an answer, gone - a transaction, not a drop and not a standing line
beamHeld steady on the far station, live and two-way until it is torn down. The connection itself is what is held

Everything shipped is a beacon. git, the relay, a file share, Azure Blob, an object store and a bundle are all the same shape: something is left somewhere, and collected later by the other side. The beam is designed and not yet built, so nothing below carries it; the shapes are named together because the vocabulary is one thing.

The beam will need a compiled binary on the station, and almost nothing else does. Noise and a wss connection are not things curl and coreutils do, so the beam's station side is designed as a Go component. That is worth knowing before you plan around it, because the far side being plain readable bash is often the reason heliograph is permitted at all.

What needs a binary today is one transport, on one station: the relay, on a bash station, which shells out to heliograph-seal for the encryption. git, a file share, a bundle and an object store need none, the PowerShell station needs none even for the relay, and a beacon or a flare over any of those is a complete product - the same requests, the same gates, the same logs. An estate that permits no compiled code loses two shapes, not the tool.

There is one such binary rather than one per feature, and that is deliberate: anything else a bash station later needs signing or verifying is expected to reuse heliograph-seal rather than arrive as a second thing to audit and checksum. The list is station/FAR-SIDE-BINARIES, it is enforced in CI, and adding to it is a deliberate edit rather than a side effect.

The price of every far-side binary is a build you can reproduce and a checksum you can check for yourself, which is on provenance.

Why the shape matters more than the speed

The obvious difference is latency, and it is the less important one.

A beacon keeps the gates where they belong. The step is already on the far side, the station reads a request that names it, and the station decides whether to run it. # heliograph-mode: read-only is a property of a file the operator can read before anything happens.

On a flare the script travels with the request. There is no git on the far side to have planted it, so the caller ships the thing to be run - and at that moment the mode header stops being a control and becomes a claim the caller makes about its own file. What is left is the function key and an IP allowlist. That is a real trade and the flare page makes it in full; it is not a worse transport, it is a different security model.

A beacon also needs nothing to be reachable, ever. No inbound port, no endpoint, no tunnel. That single property is what makes this permissible in estates where a reverse connection would be a breach, and it is why a raw TCP transport was dropped rather than built.

Pick what you have

Shape
Drive it from
Show

37 of 70 run

Beacon: a dead letter drop. You cannot reach the far side, it cannot reach you, and both reach one agreed place. No inbound port, ever. More

Flare: you can reach the station directly, so the script travels WITH the request - and the mode header stops being a gate and becomes a claim the caller makes. More

Which transport works on which station
stationtransportgitbeacrelaybeacsharebeacblobbeacS3beacbundlebeacflareflar
Terminalbash, PowerShell
Dockerbash
Kubernetesbash
As a servicebash
Windows taskPowerShell
A pipelinebash
Azure containersbash
Azure VMbash
Function Appbash
Your own hostbash

  • Proven
  • Works
  • Needs a step
  • Never checked
  • Cannot
Every transport, in full
TransportShapeYour sideThe far sideWhat it is
gitbeaconprovenprovenA private repository is the channel in both directions. The only transport that can bring the station a newer copy of itself.
relaybeaconprovenworksBoth sides dial out over ordinary HTTPS. No git host, no storage account, no VNet. The bash station needs heliograph-seal beside it; the PowerShell one needs no binary at all - its seal is managed C# that ships as source.
file sharebeaconprovenprovenThe cheapest there is, where both machines already mount the same directory. The mount is the credential, and that is the whole security model.
Azure Blobbeaconwith a conditionworksReached through drop.sh in the station payload rather than the heliograph binary. A VNet-local private endpoint is often the only thing reachable.
object storebeaconworksworksS3-compatible: AWS, R2, MinIO, B2, Spaces, Ceph. The station signs with SigV4 in bash, over openssl and curl - no AWS CLI and no binary.
bundlebeaconworksworksThe only thing that makes air-gapped literally true: a person carries the file, and no path between the two machines is needed at all. Both halves work; a round trip takes as long as somebody takes to walk.
flareflareworkswith a conditionThe one case where you CAN reach the station. The script travels with the request, so heliograph-mode stops being a control and becomes a claim the caller makes about its own file.
Every station, in full
StationStatusFlavourWhat it is
An operator's terminalprovenbash, PowerShellStill the best host when there is a willing person: no infrastructure request, and start.sh prints its own preflight to somebody who can read it.
DockerprovenbashCI builds the image and runs a loop inside it. The image plants the payload itself, so a transport with nothing to clone still has one.
KubernetesprovenbashThe same image, one replica. CI applies the manifest to a real cluster on every run.
systemd, launchd, setsidprovenbashSurvives a logout. A detached process inherits nothing from the shell that installed it, so the transport's variables go in .station-env.
Windows scheduled taskprovenPowerShellCI registers the task and reads it back. .station-env is the only way to hand it a token: there is no EnvironmentFile on Windows.
GitHub Actions, Azure Pipelineswritten, never runbashOften the one machine in an estate that can already reach both sides. Git-only by design: the push is the trigger, and that is what keeps the latency down to however long an agent takes to start.
Azure ACI, Web App, Container Apps JobprovenbashDeployed live, then torn down. The templates take a transport through two maps rather than a parameter per transport, so a new transport does not date five templates at once.
Azure VMprovenbashDeployed live on a Standard_D2s_v3. The exception to everything else here: a bare VM has no image, so git clone is how the toolkit arrives whatever transport then carries the logs.
Azure Function Appwritten, never runbashA timer, not a loop. Validated and never deployed. It is the host the flare was written for, because a Function App has a public endpoint while sitting inside the VNet.
ECS Fargate, Cloud Run, anything elsenone yetbashRecipes against the host contract, not templates. Issue #5 settled that deliberately: a template that has never started a station spends the credibility of the ones that have.
Every controller, in full
ControllerStatusTransportsWhat it is
The CLI, on Linux, macOS or Windowsprovengit, relay, file share, object store, bundle, flareOne static binary, amd64 or arm64, no runtime. Only the git transport shells out to anything - the rest are the binary alone.
An AI agent, over MCPprovengit, relay, file share, object store, bundleheliograph mcp serves the same commands as typed tools. The gates do not move: a tool call publishes a request, and the station still decides whether to run it.
git and no binary at allnever checkedgitAlready possible and documented nowhere. A request is a key: value text file, so a laptop that permits git and refuses new binaries can write it, push it, and read the log back out of the repository.
Claude Code on the webnever checkedgit, relayA cloud sandbox is a real shell, so the binary simply runs. The session proxy decides the rest: private and self-hosted hosts are refused by the allowlist.
Android, through Termuxnever checkedgit, relay, file share, object storeThe arm64 Linux binary is already built and git is a Termux package, so this is a check rather than a port. Nobody has run it.