Reference
Runner tiers, the Run environment, hosted-state wiring, the Run-state lifecycle, the GitHub events Stacks handles, and the OpenTofu version catalog — the lookup tables for sizing, wiring, and debugging.
The lookup tables behind the guides: what a Run’s environment looks like, how big each tier is, the state-lifecycle a Run moves through, which GitHub events trigger what, and how the OpenTofu catalog works.
Runner tiers
A hosted Run executes in a sandbox sized by the Stack’s runner tier. The tier is a platform-curated envelope — you pick one of three, you don’t dial the numbers. The CPU limit equals the request, so the envelope is predictable.
| Tier | vCPU | Memory | Ephemeral disk | Phase timeout |
|---|---|---|---|---|
small (default) | 1 | 1 GiB | 2 GiB | 30 min |
medium | 2 | 2 GiB | 5 GiB | 60 min |
large | 4 | 4 GiB | 10 GiB | 120 min |
The phase timeout is the wall-clock a single plan or apply may run before it
is interrupted — size up a Stack whose applies routinely approach it. Metering
is tier-minutes, billed per tier as separate quantities; a Stack running on
large meters large-minutes. Self-hosted pools have no tier — that compute is
yours. See Core concepts for what’s metered and what’s free.
The Run environment
The runner assembles each phase’s environment from three things: automation flags, the hosted-state wiring, and your Stack’s variables.
Automation flags — always set, so tofu never prompts:
| Variable | Value |
|---|---|
TF_IN_AUTOMATION | 1 |
TF_INPUT | 0 |
Hosted state — the http backend wiring, pointed at
the Run’s own short-lived state credential (not one of your CLI state keys):
TF_HTTP_ADDRESS, TF_HTTP_LOCK_ADDRESS, TF_HTTP_UNLOCK_ADDRESS,
TF_HTTP_LOCK_METHOD=LOCK, TF_HTTP_UNLOCK_METHOD=UNLOCK, TF_HTTP_USERNAME,
TF_HTTP_PASSWORD. A zz_bosun_backend_override.tf file is written into the
working directory so the Run always uses hosted state, whatever the config’s
own backend block says.
Your variables — each Stack variable is injected by its kind:
| Variable kind | How it’s injected |
|---|---|
OpenTofu input (tofu) | TF_VAR_<name> |
Environment (env) | <name> — verbatim |
Run identity — present only when the Stack has Run identity available:
| Variable | Meaning |
|---|---|
BOSUN_ID_TOKEN_REQUEST_URL | GET it with the bearer below to mint a token; pass ?audience=<aud> for the audience your consumer expects |
BOSUN_ID_TOKEN_REQUEST_TOKEN | the bearer for that request (this Run only) |
AWS zero-config federation — set an AWS_PLAN_ROLE_ARN /
AWS_APPLY_ROLE_ARN (or a single AWS_ROLE_ARN) Stack variable and the runner
mints a token for sts.amazonaws.com, writes it to a file, and wires the AWS
SDK to assume the phase’s role — no static keys. See
Run identity for the trust-policy shape.
| Variable | Set by the runner to |
|---|---|
AWS_WEB_IDENTITY_TOKEN_FILE | the path of the minted token |
AWS_ROLE_ARN | the role for this phase (plan or apply) |
AWS_ROLE_SESSION_NAME | bosun-run-<run-id>-<phase> — so CloudTrail attributes every action to one Run |
The Run-state lifecycle
A Run is one resource with phases. It moves:
queued → planning → planned
↘ awaiting_approval → applying → applied
(any phase) ↘ failed | canceled | discarded
| State | Meaning | You can |
|---|---|---|
queued | waiting for a runner slot | cancel |
planning | the plan phase is running | cancel (safe — plans never write state) |
planned | plan finished; terminal for a speculative Run or an empty change plan | — |
awaiting_approval | plan finished with changes; parked for a human | approve, discard, cancel |
applying | the apply phase is running | cancel only with force |
applied | apply succeeded; terminal | — |
failed | a phase errored; terminal | retry |
canceled | stopped by a human or superseded; terminal | retry |
discarded | walked away from at the approval gate; terminal | — |
- Approve and discard act only on
awaiting_approval. An approval binds to that exact plan; if the plan is re-created the approval is void. - Cancel works on
queued,planning, andawaiting_approvalcleanly. Canceling anapplyingRun needs an explicit force — an interrupted apply can leave state behind the infrastructure (the runner gets a graceful wind-down, but “may be incomplete” is the honest contract). - Retry starts a new Run over the failed one’s exact source. Only
failedandcanceledRuns retry; adiscardedRun was a deliberate walk-away.
GitHub events
Stacks reacts to exactly these events; everything else is acknowledged and ignored.
| Event | Action(s) | What happens |
|---|---|---|
| Push | to a tracked branch | a Change Run under merge_then_apply / auto_apply; nothing under branch_apply (it already applied) or manual |
| Pull request | opened, synchronize, reopened | a Speculative plan-only Run + a Longbridge Stacks / <stack> check on the touched Stacks |
| Pull request | closed | cleanup — no Run; on an unmerged close that had applied from its branch, an abandoned-apply warning |
| Check run | requested_action | a click on a plan check’s Apply button (branch apply) |
Ignored: tag pushes, branch/tag deletions, every other pull-request action, and any other event type. Path filtering is prefix-based — a Stack triggers only when a changed file equals or sits under one of its watch paths (which default to the Stack’s own path).
GitHub App permissions
The Longbridge Stacks App requests the minimum it needs:
| Permission | Level | For |
|---|---|---|
| Contents | Read | cloning your configuration |
| Pull requests | Read | reading a PR’s changed files for path filtering |
| Checks | Read & write | the plan/apply checks and the Apply button |
| Issues | Read & write | the post-merge apply comment and the abandoned-apply warning |
| Metadata | Read | mandatory for every App |
It subscribes to the Push, Pull request, and Check run events. Clones use short-lived per-Run installation tokens — never stored deploy keys.
The OpenTofu version catalog
Stacks runs OpenTofu only — it does not execute BSL-licensed Terraform, hosted or self-hosted.
- Versions come from a platform-curated catalog: supported OpenTofu minors, each pinned to an exact patch and its upstream release checksum, fetched from official releases and cached. The runner verifies the checksum before it runs the binary — it trusts the catalog, not the mirror.
- A Stack’s version is chosen from the catalog at creation and defaults to
the latest stable then. It never floats: a bump is an explicit, audited
Stack edit, because a
tofuupgrade can rewrite state format. required_versionin your configuration is honored as a fail-fast check, not a resolver — a Run whose pinned version violates it fails early rather than silently picking another.- Deprecation policy: a retiring version first warns, then is blocked for new selections, and is never force-upgraded — a Stack pinned to it keeps running until you move it.
The live list of currently supported versions is the catalog the Stack’s create and settings screens offer — pick from there rather than pinning a version from memory.
Hosted-state endpoint
The hosted state backend lives at:
https://api.stacks.lngbrdg.com/state/v1/<org>/<stack>
<stack> is the Stack’s id, so the address you bake into a tofu config
survives a Stack rename. It speaks the standard http backend verbs — GET
(read the current state), POST (append an immutable version),
LOCK / UNLOCK (the backend lock) — over HTTP basic auth,
your state key’s id as the username. A contended LOCK answers 423 with the
current holder’s identity as the body; see
Troubleshooting for reading and clearing it.