# 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.**

| | |
|---|---|
| **beacon** | A 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 |
| **flare** | Fired straight at a reachable endpoint. One burst, an answer, gone - a *transaction*, not a drop and not a standing line |
| **beam** | Held 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](/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`](https://github.com/dbhq-uk/heliograph/blob/main/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](/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](/flare) 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](/security) rather than built.

## Pick what you have

| station | git | relay | file share | Azure Blob | object store | bundle | flare | 
|---|---|---|---|---|---|---|---|
| Terminal | Proven | Proven | Proven | Proven | no | no | Works - only where the station's endpoint is reachable from your side | 
| Docker | Proven | Proven | Proven | Proven | no | no | no | 
| Kubernetes | Proven | Proven | Proven | Proven | no | no | no | 
| As a service | Proven | Proven | Proven | Proven | no | no | no | 
| Windows task | Proven | Proven | Proven | Works - through the bash station, not the PowerShell one | no | no | no | 
| A pipeline | Works - the push is the trigger, which is the entire point | no | no | no | no | no | no | 
| Azure containers | Proven | Needs a step - the two key files have to be put on the host - no template mounts a volume | Needs a step - the share has to be mounted | Proven | no | no | no | 
| Azure VM | Proven | Needs a step - the key files, and a git host reachable once at first boot | Needs a step - the share has to be mounted | Proven | no | no | no | 
| Function App | Needs a step - there is no git in the image | no | no | Works - through pigeonhole.sh, on a timer | no | no | Works - the one host with an endpoint you can reach | 
| Your own host | Never checked - meets the contract, never run | Never checked - meets the contract, never run | Never checked - meets the contract, never run | Never checked - meets the contract, never run | no | no | no | 

**Every transport**

| transport | shape | your side | the far side | what it is |
|---|---|---|---|---|
| git | beacon | proven | proven | A private repository is the channel in both directions. The only transport that can bring the station a newer copy of itself. |
| relay | beacon | proven | works | Both 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 share | beacon | proven | proven | The cheapest there is, where both machines already mount the same directory. The mount is the credential, and that is the whole security model. |
| Azure Blob | beacon | with a condition | works | Reached through drop.sh in the station payload rather than the heliograph binary. A VNet-local private endpoint is often the only thing reachable. |
| object store | beacon | works | works | S3-compatible: AWS, R2, MinIO, B2, Spaces, Ceph. The station signs with SigV4 in bash, over openssl and curl - no AWS CLI and no binary. |
| bundle | beacon | works | works | The 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. |
| flare | flare | works | with a condition | The 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**

| station | status | flavour | what it is |
|---|---|---|---|
| An operator's terminal | proven | bash, PowerShell | Still 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. |
| Docker | proven | bash | CI 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. |
| Kubernetes | proven | bash | The same image, one replica. CI applies the manifest to a real cluster on every run. |
| systemd, launchd, setsid | proven | bash | Survives a logout. A detached process inherits nothing from the shell that installed it, so the transport's variables go in .station-env. |
| Windows scheduled task | proven | PowerShell | CI 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 Pipelines | written, never run | bash | Often 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 Job | proven | bash | Deployed 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 VM | proven | bash | Deployed 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 App | written, never run | bash | A 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 else | none yet | bash | Recipes 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**

| controller | status | transports | what it is |
|---|---|---|---|
| The CLI, on Linux, macOS or Windows | proven | git, relay, file share, object store, bundle, flare | One 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 MCP | proven | git, relay, file share, object store, bundle | heliograph 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 all | never checked | git | Already 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 web | never checked | git, relay | A 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 Termux | never checked | git, relay, file share, object store | The 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. |

