Cloud credentials
The AWS and GCP connectors need cloud credentials to read. There are two modes, and they are orthogonal to the secret store: that one decides where secrets live, this one decides how cloud credentials are resolved.
RUNNER_AWS_CRED_MODE=static # defaultRUNNER_AWS_CRED_MODE=chain
RUNNER_GCP_CRED_MODE=static # defaultRUNNER_GCP_CRED_MODE=chainstatic: the default
Section titled “static: the default”Reads AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optionally AWS_SESSION_TOKEN from the secret
store and injects them explicitly into the client.
Serves anyone handed ready-made keys, or keeping them in Vault or Secrets Manager. It’s the default for backward compatibility.
chain: workload identity
Section titled “chain: workload identity”Injects no key at all. The SDK’s default credential chain resolves it: IRSA on EKS, task role on ECS, instance profile on EC2, environment variables, or the SSO cache.
It’s the correct mode when the runner runs inside AWS, and it’s what keeps static keys out of circulation.
RUNNER_AWS_CRED_MODE=chainAWS_PROFILE=my-profile # in development, with aws sso loginA subtlety that causes an invisible connector
Section titled “A subtlety that causes an invisible connector”The served set is secret-aware: a connector whose required keys are missing drops out of the announced
set. But in chain mode the credential doesn’t come from the secret store, so without special
handling, the runner would not announce the aws.* operations even while working perfectly.
That’s why the runtime marks the aws connector as always served in chain mode. Worth knowing
this special case exists, so the behavioral difference between the two modes doesn’t surprise you.
Multiple accounts
Section titled “Multiple accounts”chain mode is single-account by construction. For several AWS accounts, run one runner per
account, each with its own IRSA identity. The graph is merged on the control-plane side.
Context derived at boot
Section titled “Context derived at boot”With RUNNER_CONNECTORS=real, the runner runs a best-effort STS probe at boot to discover the
account and region, and reports that in the handshake. It’s what lets the control-plane resolve the
tenant’s AWS context from the live runner, instead of manual configuration.
The probe never throws. If it fails, the information simply doesn’t go, and the control-plane uses its fallback.
The design is the exact sibling of the AWS side.
static: the default
Section titled “static: the default”Reads the service account’s JSON key (GCP_SA_KEY, the JSON on a single line) from the secret store
and builds the clients with it.
chain: Application Default Credentials
Section titled “chain: Application Default Credentials”Injects no key. ADC resolves it: Workload Identity on GKE, metadata on GCE, the file pointed at by
GOOGLE_APPLICATION_CREDENTIALS, or gcloud.
With chain, GCP_SA_KEY isn’t needed.
RUNNER_GCP_CRED_MODE=chainGCP_PROJECT_ID=my-projectAlso single-project: for several projects, one runner per project, each with its own Workload Identity GSA.
Magalu Cloud
Section titled “Magalu Cloud”Here the design is not a sibling of the other two, and the difference is posture, not convenience.
MGC authenticates with an API key (X-API-Key header). There is no credential-chain analogue —
nothing equivalent to IRSA on AWS or Workload Identity on GCP — so there is no
RUNNER_MGC_CRED_MODE: only the static key, read from the customer’s secret manager. Inventing a
mode that doesn’t exist would be worse than the absence.
MGC_API_KEY=…MGC_REGION=br-se1 # optional; the region lives in the URL, one runner serves one regionMGC_TENANT_ID=… # optional; saves one call at bootMGC_S3_ACCESS_KEY=… # object storage; the `key_pair_id` of the SAME keyMGC_S3_SECRET_KEY=… # object storage; the `key_pair_secret` of the SAME keyScopes are the control, and they’re immutable
Section titled “Scopes are the control, and they’re immutable”An MGC API key carries three components: api_key (what RootPilot sends in the X-API-Key
header), key_pair_id and key_pair_secret — together the Object Storage S3 credential, which
become the MGC_S3_ACCESS_KEY/MGC_S3_SECRET_KEY above.
Creating the key from the console with “everything” checked yields roughly 160 scopes over the
whole account, billing and IAM included. Create it from the CLI with explicit --scopes and grant
read-only — one read scope per product RootPilot will read:
| Product | What RootPilot reads |
|---|---|
Virtual machine (virtual-machine.read) |
instances and their IPs |
Network (network.read) |
VPCs, subnets, security groups, public IPs, NAT, load balancers |
Object storage (object-storage.read) |
buckets and exposure posture |
| Block storage | volumes, attachment and encryption |
| DBaaS | database instances, clusters and replicas |
| Kubernetes (MKE) | clusters, node pools and who can reach the API server |
| Container registry | registries, repositories and images |
| Audit | who did what, when, and to which resource |
The identifiers for the last four come from the console/CLI scope listing itself — do not guess them
from the first three. A key missing one product’s scope does not break RootPilot: only that
product’s reads come back auth_error, and the rest of the connector keeps serving. In practice the
product simply goes invisible without a sound, so it is worth checking the table above against what
you expect to see.
That’s better than it sounds: RootPilot’s read-only posture becomes enforced in the credential, not only in the code. The connector refuses writes, and the key doesn’t even permit them. Scopes aren’t editable afterwards — if the need changes, recreate the key.
Context derived at boot
Section titled “Context derived at boot”The “account” analogue on MGC is the tenant_id, which appears on VPCs, security groups and
public IPs. There is no “project”.
Unlike GCP, it isn’t free: the API key is opaque, and there’s no workload-identity metadata server.
So the runner uses MGC_TENANT_ID when set and otherwise derives it from one VPC listing —
best-effort. With no key, no VPC, or a failing call, no account is announced and the control plane
falls back to configuration. It never invents an id: announcing a non-existent account would route
reads somewhere that doesn’t answer.
Related variables
Section titled “Related variables”| Variable | Notes |
|---|---|
AWS_REGION |
Used by the AWS connectors and required for aws-sts attestation. |
MGC_API_KEY |
Magalu Cloud API key. Create it with read scopes — one per product RootPilot will read (see the table above). |
MGC_REGION |
br-se1 (default) or br-ne1. Lives in the URL: one runner serves one region. |
MGC_TENANT_ID |
Skips the discovery call at boot. Recommended in production. |
MGC_S3_ACCESS_KEY |
The key_pair_id of the same API key. Object storage speaks S3 (SigV4) against a different host — a different credential, same owner. Optional: without it buckets stay invisible, with no boot error. |
MGC_S3_SECRET_KEY |
The key_pair_secret of the same API key. What authorizes bucket reads is the object-storage.read scope on MGC_API_KEY, not this pair. |
EKS_CLUSTER |
Without it, the EKS health check answers skipped: no cluster configured. The connector shows up as unconfigured in Fleet, which is different from broken and equally blind. |
GCP_PROJECT_ID |
Target project. |
GCP_REGION |
Target region. |
GCP_BILLING_EXPORT_TABLE |
BigQuery billing-export table. Without it, the GCP cost operation fails explicitly. |