Run identity
Give Runs short-lived OIDC credentials for AWS, GCP, Azure, or OpenBao/Vault — with read-only trust for plans and write trust for applies — so no cloud keys ever rest with Longbridge.
Every Run can prove exactly what it is — this organization, this Stack, this phase — with a short-lived OpenID Connect token minted by the Stacks control plane. Your cloud trusts the token instead of a stored key, so your cloud credentials never rest with Longbridge. Static keys in variables still work; this page is how you retire them.
The token
- Issuer:
https://api.stacks.lngbrdg.com(discovery and JWKS are published at the standard/.well-known/…paths). - Subject:
org:<org>:stack:<stack>:run_phase:<plan|apply>— stable and policy-friendly.<stack>is the Stack’s id (shown in the Console), not its display name, so the subject survives a rename — astack_nameclaim carries the human name for readability. The phase in the subject is what lets you grant a plan read-only cloud access and an apply write access. - Claims: the Run’s id, kind, repository, ref, commit, and runner pool — everything asserted by the control plane, never by the runner.
- Lifetime: 10 minutes.
- Audience (
aud): whatever the consumer requests when it mints the token. The request URL below takes an?audience=<aud>parameter, and that value becomes the token’saudclaim (omit it andaudfalls back to the issuer URL). Your trust policy then conditions on that same value — so the two must be set to the same string; they don’t align on their own. The zero-config AWS path requestssts.amazonaws.comfor you, which is exactly why its trust policy conditions onaud = sts.amazonaws.com; OpenBao, GCP, and Azure each pass their own audience.
AWS, the zero-config way
- In your AWS account, create an IAM OIDC identity provider for
https://api.stacks.lngbrdg.comwith audiencests.amazonaws.com. - Create two roles whose trust policies differ only in the subject condition:
{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::<account>:oidc-provider/api.stacks.lngbrdg.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"api.stacks.lngbrdg.com:aud": "sts.amazonaws.com",
"api.stacks.lngbrdg.com:sub": "org:<org>:stack:<stack>:run_phase:plan"
}
}
}
Give the run_phase:plan role read-only policies and the
run_phase:apply role write policies.
-
Set two plain (not sensitive — an ARN is public) environment variables on the Stack:
AWS_PLAN_ROLE_ARN— the read-only roleAWS_APPLY_ROLE_ARN— the write role
That’s it. Each phase’s runner mints a token, and the AWS provider assumes
the phase’s role via AssumeRoleWithWebIdentity — no access keys anywhere.
(A single AWS_ROLE_ARN also works if you don’t want the split.) Role
sessions are named bosun-run-<id>-<phase>, so CloudTrail attributes every
cloud action to a specific Run.
OpenBao / Vault
Enable the jwt auth method against the issuer and bind roles by subject:
bao auth enable jwt
bao write auth/jwt/config oidc_discovery_url="https://api.stacks.lngbrdg.com"
bao write auth/jwt/role/stack-apply \
role_type=jwt user_claim=sub \
bound_audiences="openbao" \
bound_subject="org:<org>:stack:<stack>:run_phase:apply" \
policies=infra-secrets ttl=10m
Inside a Run, request a token for your audience from the environment the runner provides:
curl -sS -H "Authorization: Bearer $BOSUN_ID_TOKEN_REQUEST_TOKEN" \
"$BOSUN_ID_TOKEN_REQUEST_URL?audience=openbao"
The response’s value is the JWT to present to auth/jwt/login.
Anything else that speaks OIDC
GCP Workload Identity Federation and Azure federated credentials follow the same shape: trust the issuer, condition on the subject, map the phase split onto your roles. The request URL + bearer pattern above works for any consumer.