Tenant isolation
Every tenant’s data is isolated inside PostgreSQL with row-level security policies the database evaluates independently of the application; the application’s role cannot bypass them, and the audit and Permit event tables are append-only at the grant level. Every API request is additionally scoped to a tenant at the middleware layer before any handler runs. Requests without a valid tenant context are rejected. A credential from one tenant cannot read, infer or act on another tenant’s environments, changes, Permits or outcomes — and the customer-security gate exercises exactly that refusal through the real UI on every build.Environment isolation
Reading and acting are separate identities, and acting is identified per environment. Every execution object carries the environment’s instance —<environment-slug>-<10 hex of a hash of tenant and environment>:
Because the names depend only on (tenant, environment), two environments on one physical cluster — even two tenants in one AWS account — never share a role ARN, an access entry or a write-authority group. The invariant enforced at Permit issuance and again at consume:
Permit environment = AWS execution identity environment = Kubernetes execution identity environment
- A role bound to another environment, even in the same account on the same cluster, is refused (
target_identity_changed) and the Permit revoked. - A role in another tenant or another account with a matching name is refused; binding is server-side, by registration on an STS-verified connection, never by name.
- A Permit signs the cluster’s identity, endpoint and certificate authority; a swapped endpoint never reaches AWS.
- A Local Executor install for one environment never touches another environment’s objects, and
kubectl delete -fof its manifest removes only its own. - An execution role not bound to an environment authorizes nothing and fails closed until the environment registers its own.
Environment-scoped AWS identity
One execution role per environment, in your account, with no AWS permissions. Its trust policy admits only ChangeGuard’s Cloud Executor principal with a ChangeGuard-issued external ID that is never caller-chosen. The Cloud Executor assumes it for 15 minutes, in a session named after the Permit, and mints a 60-second EKS token bound to exactly that cluster. The read-only Fleet role is a different role, limited to EKS discovery, used with sessions minted per operation. There are no long-lived AWS access keys anywhere in the model.Environment-scoped Kubernetes identity
The access entry maps the execution role to the environment’s group with no access policy attached; authority comes only from the RoleBindings you applied in the namespaces you chose.kube-* namespaces are never granted. Verify execution access proves, per environment, that the identity holds exactly the required verbs in exactly those namespaces and is denied everything else — delete, create, exec, Secrets, impersonation, RBAC writes, nodes, wildcards, cluster-admin.