Context
Encore Cloud’s GCP connect flow asks forroles/owner on the project for its own service account. ADR 0172 decision 1
asks for least privilege, and ADR 0401 already replaced roles/owner for the infra service account.
Settings promotion (stackra settings:apply, ADR 0395) and code deploys (deploy, promote, rollback) both change
an environment. Two of them running at once in one environment interleave: a settings change can land against a
release that is halfway rolled out, or two promotions can race.
Decision
- Encore Cloud gets
ENCORE_ROLES, never owner. When an environment’sdestinations.servicesisencore-cloudand the root runway namesproviders.gcp.encore.serviceAccount(an email orref:<path>), the GCP foundation (gcpBaseline) grants that service account one predefined role per kind of resource Encore provisions: Cloud Run, Cloud SQL, Pub/Sub, Storage, Memorystore, Secret Manager, Cloud Scheduler, the VPC (network admin, Serverless VPC Access, private services access), Artifact Registry, service accounts and project IAM, enabling APIs, and reading logs and metrics. A missing role shows as a failed provisioning in Encore’s dashboard and is added to the list in the same change; the grant never widens to owner. - One apply per environment at a time.
withDeployLock(root, environment, lock, work)in@stackra/runwayruns any apply (a code deploy, a promotion, a rollback or a settings promotion) holding the keyrunway:deploy:<project>:<environment>. A held lock refuses at once withRunwayLockedError; it never queues. The lock store is the caller’sLocksfrom@stackra/locking(Postgres or Valkey throughlocking.driver), passed as the structuralDeployLocktype so runway takes no dependency on it. The lease is renewed while the apply runs; its TTL (30 minutes unless the caller passes one) only bounds how long a crashed holder blocks the environment.
Consequences
- Turning Encore Cloud on for a GCP project is runway data: the destination and the service account.
- The role list may lag Encore’s primitives; the first deploy that needs a new resource kind fails visibly.
- The CLI’s
settings:applyand the deploy commands wrap their work inwithDeployLock; until they do, the lock holds only for callers that use it.
Enforcement
planned —packages/infrastructure/infra-gcp/src/node/functions/__tests__/encore-access.function.test.ts and
gcp-baseline.function.test.ts assert the roles and the absence of roles/owner;
packages/developer/runway/src/core/functions/__tests__/with-deploy-lock.function.test.ts asserts the refusal.