Policy engine for the Local IAM Control Plane — Evaluate permissions before data access, make emulators fail for the same authorization reasons production would.
This is the brain of the Blackwell Local IAM Control Plane. It evaluates IAM policies and tells service emulators (Secret Manager, KMS) whether to allow or deny requests.
Unlike mocks (which allow everything) or observers (which record after the fact), this actively enforces policies before requests reach data planes.
# policy.yaml - Define exactly what your tests need
roles:
roles/custom.ciRunner:
permissions:
- secretmanager.secrets.get
- secretmanager.versions.access
- cloudkms.cryptoKeys.encrypt
groups:
developers:
members:
- user:alice@example.com
- user:bob@example.com
projects:
test-project:
bindings:
- role: roles/viewer
members:
- group:developers
- role: roles/custom.ciRunner
members:
- serviceAccount:ci@test-project.iam.gserviceaccount.com
condition:
expression: 'resource.name.startsWith("projects/test-project/secrets/prod-")'
title: "CI limited to production secrets"# Start with strict mode (default - catches misconfigurations)
server --config policy.yaml --trace
# HTTP REST API
server --config policy.yaml --http-port 8081
# Hot reload on config changes
server --config policy.yaml --watchResult: Local IAM decisions matching your policy, offline testing of authorization logic, CI-ready without GCP credentials.
Standalone - Run independently as a policy engine:
server --config policy.yaml
# Single IAM server, use for custom emulatorsUnified Ecosystem - Use with gcp-emulator for unified multi-service testing in a single process:
go install github.com/blackwell-systems/gcp-emulator/cmd/gcp-emulator@latest
gcp-emulator --policy policy.yaml --iam-mode strict
# IAM + Secret Manager + KMS + Eventarc
# Single process, single port, single policy fileChoose standalone for custom integrations, gcp-emulator for the complete GCP emulator stack.
The emulator needs to know who is making each request. Provide identity via metadata/headers:
import "google.golang.org/grpc/metadata"
// Inject principal identity
md := metadata.Pairs("x-emulator-principal", "serviceAccount:ci@project.iam.gserviceaccount.com")
ctx = metadata.NewOutgoingContext(context.Background(), md)
// Now use ctx for API calls - emulator sees the principal
client.GetSecretVersion(ctx, &secretmanagerpb.GetSecretVersionRequest{...})curl -X POST http://localhost:8081/v1/projects/test/secrets/api-key:testIamPermissions \
-H "X-Emulator-Principal: serviceAccount:ci@project.iam.gserviceaccount.com" \
-H "Content-Type: application/json" \
-d '{"permissions": ["secretmanager.secrets.get"]}'- Service accounts:
serviceAccount:name@project.iam.gserviceaccount.com - Users:
user:alice@example.com - Groups:
group:eng-team@example.com(define groups in policy.yaml) - All authenticated:
allAuthenticatedUsers - Public:
allUsers
When using with Secret Manager / KMS emulators, the data plane emulators automatically forward the principal to the IAM control plane:
// Your test code sets principal once
ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(
"x-emulator-principal", "serviceAccount:ci@test.iam.gserviceaccount.com",
))
// Secret Manager emulator forwards principal to IAM emulator automatically
client.AccessSecretVersion(ctx, &secretmanagerpb.AccessSecretVersionRequest{...})
// ↓ Internally: IAM checks if ci@test.iam can secretmanager.versions.accessNo additional configuration needed - the emulator ecosystem handles principal propagation.
┌─────────────────────────────────────────┐
│ Your Application Code │
│ (GCP client libraries) │
└────────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ DATA PLANES │
│ • Secret Manager Emulator │
│ • KMS Emulator │
│ • (Future: Tasks, Pub/Sub, Storage) │
│ │
│ Each checks IAM before data access │
└────────────────┬────────────────────────┘
│
│ CheckPermission(principal, resource, permission)
▼
┌─────────────────────────────────────────┐
│ CONTROL PLANE (THIS REPO) │◄── You are here
│ IAM Emulator — Policy Engine │
│ │
│ Evaluates: │
│ • Role bindings │
│ • Group memberships │
│ • Conditional policies (CEL) │
│ • Resource-level policy evaluation │
│ │
│ Returns: Allow / Deny │
└─────────────────────────────────────────┘
This is the enforcement boundary. Requests are authorized here before data access.
| Approach | Example | When | Behavior |
|---|---|---|---|
| Mock | Standard emulators | Never | Always allows |
| Observer | Post-execution analysis | After | Records what you used |
| Control Plane | Blackwell IAM | Before | Denies unauthorized |
The Problem with Real GCP IAM:
GCP IAM is eventually consistent. When you create or update a policy binding:
- Changes can take 1-60 seconds to propagate globally
- Your tests become flaky (sometimes pass, sometimes fail)
- CI/CD pipelines have random failures you can't reproduce
The Blackwell Difference:
This emulator is strongly consistent. Policy changes are:
- Instant (0ms propagation delay)
- Deterministic (same input = same output, every time)
- Repeatable (run the test 1000 times, get the same result)
| Dimension | Real GCP IAM | Blackwell IAM Emulator |
|---|---|---|
| Consistency Model | Eventually consistent | Strongly consistent |
| Propagation Delay | 1-60 seconds | 0ms (immediate) |
| Test Flakiness | High (timing-dependent) | Zero (deterministic) |
| CI/CD Reliability | Unpredictable | Fully repeatable |
| Network Dependency | Required | None |
| Cost per Test Run | API charges | Free (local) |
"Eventual consistency is the enemy of CI/CD. This emulator gives you instant, deterministic IAM testing—no more flaky tests, no more waiting for propagation, no more Friday night debugging."
GCP IAM Emulator is a deterministic, local policy engine for testing cloud authorization logic.
Scope note: This emulator is a deterministic IAMPolicy engine for CI testing. It does not attempt full Google Cloud IAM parity (org/folder hierarchy, deny policies, full CEL).
It is not a full reimplementation of Google Cloud IAM, and it does not attempt perfect fidelity.
Instead, it provides:
- Deterministic permission evaluation - Test "who can access what" locally (within your policy.yaml-defined permission universe)
- Strict, offline policy modeling - No GCP credentials or network required
- Composable auth layer - Foundation for local GCP emulator ecosystems
Users define their own permission universe. The emulator enforces it.
Goal: Catch authorization bugs in CI (missing permissions, wrong roles, misconfigured principals).
Non-goal: Mirror every edge case of Google IAM. If your test passes locally but fails in real GCP, refine your custom role definitions.
The built-in roles are intentionally small. The emulator is infinite via custom roles.
This IAM emulator is deliberately scoped for authorization testing, not comprehensive IAM replication. We model a small set of built-in roles (primitives + Secret Manager + KMS) plus unlimited custom role definitions to catch the bugs that actually break production: missing permissions, wrong role assignments, and misconfigured principals. This curated-first approach catches 95% of real-world authorization bugs while maintaining hermetic execution (no GCP credentials required), deterministic behavior (0ms propagation delay vs 1-60s in real GCP), and zero maintenance burden from tracking GCP's evolving role catalog. If you need to test additional GCP services or permissions, define them explicitly in policy.yaml as custom roles — this explicit approach is simpler, more reliable, and avoids the catalog staleness problem that plagues comprehensive IAM emulation. We optimize for authorization failures that matter, not theoretical IAM completeness.
- Complete IAMPolicy API surface - SetIamPolicy, GetIamPolicy, TestIamPermissions (gRPC + REST)
- Deterministic Permission Evaluation - Explicit role→permission definitions (built-in bootstrap roles + YAML-defined custom roles)
- Conditional Bindings - CEL expression support for resource-based access control
- Groups Support - Define reusable groups with nested membership (1 level)
- Policy Schema v3 - Full support for etag, version, auditConfigs, conditions
- Enhanced Trace Mode - JSON output, verbose logging, duration metrics
- Custom Roles - Define any GCP permission in YAML (extensible, not hardcoded)
- Small Built-in Core - Primitive roles + Secret Manager + KMS (bootstrap only)
- No GCP Credentials - Works entirely offline without authentication
- Fast & Lightweight - In-memory storage, starts in milliseconds
- Thread-Safe - Concurrent access with proper synchronization
- Integrates with Emulators - Works with gcp-secret-manager-emulator, gcp-kms-emulator
SetIamPolicy- Set IAM policy on any resourceGetIamPolicy- Retrieve IAM policy for a resourceTestIamPermissions- Check which permissions are granted
The emulator includes a small built-in set for immediate use. For production tests, define custom roles in YAML.
Primitive roles:
roles/owner- Full access to all resourcesroles/editor- Read/write access (no IAM management, no delete)roles/viewer- Read-only access
Secret Manager roles:
roles/secretmanager.admin- Full secret managementroles/secretmanager.secretAccessor- Read secret values onlyroles/secretmanager.secretVersionManager- Manage versions
KMS roles:
roles/cloudkms.admin- Full KMS managementroles/cloudkms.cryptoKeyEncrypterDecrypter- Encrypt/decrypt onlyroles/cloudkms.viewer- Read-only KMS access
Total: 10 built-in roles, 26 permissions
Need more services? Define custom roles in YAML - see Custom Roles section below.
go install github.com/blackwell-systems/gcp-iam-emulator/cmd/server@latestBasic:
# Start on default port 8080
server
# Custom port
server --port 9090With policy config (recommended for CI):
# Load policies from YAML
server --config policy.yaml
# Enable trace mode for debugging
server --config policy.yaml --trace
# Enable HTTP REST API
server --config policy.yaml --http-port 8081
# Enable verbose trace with JSON output
server --config policy.yaml --explain --trace-output trace.json
# Hot reload policies on file changes
server --config policy.yaml --watchDocker:
# Run with mounted config
docker run -p 8080:8080 -v $(pwd)/policy.yaml:/policy.yaml \
ghcr.io/blackwell-systems/gcp-iam-emulator:latest --config /policy.yaml
# Run with trace mode
docker run -p 8080:8080 \
ghcr.io/blackwell-systems/gcp-iam-emulator:latest --traceCreate policy.yaml:
# Define custom roles for your test environment
roles:
roles/custom.dataReader:
permissions:
- bigquery.datasets.get
- bigquery.tables.list
projects:
test-project:
bindings:
- role: roles/owner
members:
- user:admin@example.com
- role: roles/custom.dataReader
members:
- user:analyst@example.com
- role: roles/secretmanager.secretAccessor
members:
- serviceAccount:ci@test-project.iam.gserviceaccount.com
resources:
secrets/db-password:
bindings:
- role: roles/secretmanager.secretAccessor
members:
- serviceAccount:app@test-project.iam.gserviceaccount.comNote: The emulator includes built-in roles for primitives + Secret Manager + KMS. For other GCP services, define custom roles as shown above.
Go client with principal injection:
package main
import (
"context"
iampb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
func main() {
ctx := context.Background()
conn, _ := grpc.NewClient(
"localhost:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
defer conn.Close()
client := iampb.NewIAMPolicyClient(conn)
// Inject principal identity via metadata
md := metadata.Pairs("x-emulator-principal", "serviceAccount:ci@test-project.iam.gserviceaccount.com")
ctx = metadata.NewOutgoingContext(ctx, md)
// Test permissions (checks if this principal has access)
resp, _ := client.TestIamPermissions(ctx, &iampb.TestIamPermissionsRequest{
Resource: "projects/test-project/secrets/db-password",
Permissions: []string{
"secretmanager.versions.access",
"secretmanager.secrets.delete",
},
})
// resp.Permissions = ["secretmanager.versions.access"]
// (delete denied - secretAccessor role doesn't include it)
}Setting policies dynamically (no config file):
// Set policy via API
policy := &iampb.Policy{
Bindings: []*iampb.Binding{
{
Role: "roles/secretmanager.secretAccessor",
Members: []string{
"serviceAccount:ci@my-project.iam.gserviceaccount.com",
},
},
},
}
client.SetIamPolicy(ctx, &iampb.SetIamPolicyRequest{
Resource: "projects/my-project/secrets/my-secret",
Policy: policy,
})- CI/CD Pipelines - Drop-in IAM for hermetic testing without GCP credentials
- Policy Development - Iterate on IAM policies locally with instant feedback
- Security Testing - Validate "who can access what" before production
- Permission Debugging - Trace mode explains why access was granted/denied
- Integration Testing - Real permission evaluation with Secret Manager + KMS emulators
- Cost Reduction - Avoid GCP API charges during development
name: Integration Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
iam-emulator:
image: ghcr.io/blackwell-systems/gcp-iam-emulator:latest
ports:
- 8080:8080
options: --mount type=bind,source=${{ github.workspace }}/policy.yaml,target=/policy.yaml
args: --config /policy.yaml --trace
steps:
- uses: actions/checkout@v4
- name: Run integration tests
env:
IAM_EMULATOR_HOST: localhost:8080
run: |
go test ./...Full GCP emulator stack:
# docker-compose.yml
services:
iam:
image: ghcr.io/blackwell-systems/gcp-iam-emulator:latest
ports:
- "8080:8080"
volumes:
- ./policy.yaml:/policy.yaml
command: --config /policy.yaml --trace
secret-manager:
image: ghcr.io/blackwell-systems/gcp-secret-manager-emulator:latest
environment:
IAM_EMULATOR_HOST: iam:8080
ports:
- "9090:9090"
depends_on:
- iam
kms:
image: ghcr.io/blackwell-systems/gcp-kms-emulator:latest
environment:
IAM_EMULATOR_HOST: iam:8080
ports:
- "9091:9090"
depends_on:
- iamRun:
docker-compose up
# Your tests connect to localhost:8080 (IAM), localhost:9090 (Secret Manager), localhost:9091 (KMS)Enable trace mode to debug authorization decisions:
server --config policy.yaml --traceExample output:
2026/01/26 10:30:15 GCP IAM Emulator v0.2.0
2026/01/26 10:30:15 Loading policy config from policy.yaml
2026/01/26 10:30:15 Loaded 3 policies from config
2026/01/26 10:30:15 Trace mode: ENABLED (authz decisions will be logged)
2026/01/26 10:30:15 Server ready - listening on :8080
level=INFO msg="authz decision" decision=ALLOW principal=serviceAccount:ci@test.iam.gserviceaccount.com resource=projects/test/secrets/api-key permission=secretmanager.versions.access reason="matched binding: role=roles/secretmanager.secretAccessor member=serviceAccount:ci@test.iam.gserviceaccount.com"
level=INFO msg="authz decision" decision=DENY principal=user:dev@example.com resource=projects/test/secrets/db-password permission=secretmanager.secrets.delete reason="no matching binding found for principal"
Use trace mode to:
- Understand why access was denied
- Debug policy inheritance
- Verify principal matching
- Audit authz decisions in local testing
Enhanced trace mode (v0.3.0+):
# Verbose logging with --explain
server --config policy.yaml --explain
# JSON output to file
server --config policy.yaml --trace-output trace.jsonJSON trace format:
{
"time":"2026-01-26T10:30:15Z",
"level":"INFO",
"msg":"permission_check",
"resource":"projects/test/secrets/api-key",
"principal":"serviceAccount:ci@test.iam.gserviceaccount.com",
"allowed_permissions":["secretmanager.versions.access"],
"duration_ms":2,
"timestamp":"2026-01-26T10:30:15Z"
}Structured logging of IAM decisions for debugging, auditing, and testing.
# Emit structured traces to file
IAM_TRACE_OUTPUT=./authz-trace.jsonl ./server --config policy.yaml
# Or to stdout for debugging
IAM_TRACE_OUTPUT=stdout ./server --config policy.yamlDebug Permission Denials:
# See exactly why access was denied
cat authz-trace.jsonl | jq 'select(.decision.outcome=="DENY")'
# Output shows principal, resource, permission, and reasonAudit Test Coverage:
# List all permissions your tests actually exercised
cat authz-trace.jsonl | jq -r '.action.permission' | sort -u
# See which principals were tested
cat authz-trace.jsonl | jq -r '.actor.principal' | sort -uValidate Policy Changes:
# Before policy change
IAM_TRACE_OUTPUT=./before.jsonl go test ./...
# After policy change
IAM_TRACE_OUTPUT=./after.jsonl go test ./...
# Compare outcomes (detect regressions)
diff <(jq -r '.decision.outcome' before.jsonl | sort) \
<(jq -r '.decision.outcome' after.jsonl | sort)CI/CD Compliance:
# Prove CI only accessed allowed resources
cat ci-audit.jsonl | \
jq -r 'select(.decision.outcome=="ALLOW") | .target.resource' | \
grep -v "projects/prod/" && echo "❌ Unauthorized access" || echo "✅ Compliant"Each trace event is a single JSON line with:
- Actor:
actor.principal(who) - Target:
target.resource(what) - Action:
action.permission(which permission) - Decision:
decision.outcome(ALLOW or DENY) - Reason:
decision.reason(why) - Timing:
decision.latency_ms(performance)
Example event:
{"schema_version":"1.0","event_type":"authz_check","timestamp":"2026-01-28T10:15:23.483Z","actor":{"principal":"user:alice@example.com"},"target":{"resource":"projects/test/secrets/db-password"},"action":{"permission":"secretmanager.secrets.get"},"decision":{"outcome":"ALLOW","reason":"binding_match","latency_ms":3}}See gcp-emulator-auth/pkg/trace for complete schema definition.
Use CEL expressions to restrict access based on resource properties:
projects:
test-project:
bindings:
# CI can only access production secrets
- role: roles/secretmanager.secretAccessor
members:
- serviceAccount:ci@test-project.iam.gserviceaccount.com
condition:
expression: 'resource.name.startsWith("projects/test-project/secrets/prod-")'
title: "Production secrets only"
description: "CI service account restricted to production secrets"
# Time-based access
- role: roles/cloudkms.cryptoKeyEncrypterDecrypter
members:
- serviceAccount:temp-access@test-project.iam.gserviceaccount.com
condition:
expression: 'request.time < timestamp("2026-12-31T23:59:59Z")'
title: "Temporary access"Supported CEL expressions:
resource.name.startsWith("prefix")- Match resource name prefixresource.type == "SECRET"- Match resource type (SECRET, CRYPTO_KEY, KEY_RING)request.time < timestamp("2026-12-31T00:00:00Z")- Time-based access
Define reusable groups to reduce duplication:
groups:
developers:
members:
- user:alice@example.com
- user:bob@example.com
- serviceAccount:dev-bot@test-project.iam.gserviceaccount.com
operators:
members:
- user:ops@example.com
- group:oncall # Nested groups (1 level supported)
oncall:
members:
- user:charlie@example.com
- user:diana@example.com
projects:
test-project:
bindings:
- role: roles/viewer
members:
- group:developers # Reference groupHTTP REST gateway for all IAM operations:
# Start with REST API
server --config policy.yaml --http-port 8081Example requests:
# Set IAM policy
curl -X POST http://localhost:8081/v1/projects/test-project:setIamPolicy \
-H "Content-Type: application/json" \
-d '{
"policy": {
"bindings": [{
"role": "roles/viewer",
"members": ["user:dev@example.com"]
}]
}
}'
# Get IAM policy
curl http://localhost:8081/v1/projects/test-project:getIamPolicy
# Test permissions
curl -X POST http://localhost:8081/v1/projects/test-project/secrets/api-key:testIamPermissions \
-H "Content-Type: application/json" \
-H "X-Emulator-Principal: serviceAccount:ci@test.iam.gserviceaccount.com" \
-d '{
"permissions": [
"secretmanager.versions.access",
"secretmanager.secrets.delete"
]
}'Response:
{
"permissions": ["secretmanager.versions.access"]
}Full support for IAM Policy v3 features:
- etag - Optimistic concurrency control (SHA256-based)
- version - Policy format version (1=basic, 3=with conditions)
- auditConfigs - Audit logging configuration
- bindings[].condition - Conditional role bindings
projects:
test-project:
auditConfigs:
- service: secretmanager.googleapis.com
auditLogConfigs:
- logType: ADMIN_READ
- logType: DATA_READ
- logType: DATA_WRITE
exemptedMembers:
- serviceAccount:logging@test-project.iam.gserviceaccount.comDefine your own role-to-permission mappings for any GCP service:
roles:
roles/custom.dataReader:
permissions:
- bigquery.datasets.get
- bigquery.tables.list
- bigquery.tables.getData
roles/custom.pubsubPublisher:
permissions:
- pubsub.topics.publish
- pubsub.topics.get
roles/custom.storageAdmin:
permissions:
- storage.buckets.create
- storage.buckets.delete
- storage.objects.create
- storage.objects.delete
projects:
test-project:
bindings:
- role: roles/custom.dataReader
members:
- user:analyst@example.comFeatures:
- Extensible - Define permissions for any GCP service
- Override built-in roles - Custom roles take precedence
- Strict mode by default - Unknown roles denied (catches misconfigurations)
- Compat mode available - Wildcard fallback with
--allow-unknown-rolesroles/secretmanager.customRole→ grantssecretmanager.*roles/cloudkms.encryptOnly→ grantscloudkms.*
Why this matters:
- GCP has thousands of permissions - hardcoding doesn't scale
- Each test environment needs different permissions
- Keeps emulator offline, deterministic, and CI-friendly
Modes:
Strict mode (default, recommended):
server --config policy.yaml- Unknown roles → DENIED
- Custom roles → allowed
- Built-in roles → allowed
- Catches bugs: Tests fail if you use a role you haven't defined
Compat mode (less strict):
server --config policy.yaml --allow-unknown-roles- Unknown roles → wildcard match (if service prefix matches)
- More permissive, but can hide bugs
- Use when migrating existing tests
Decision order:
- Custom roles (highest priority)
- Built-in roles (primitives + Secret Manager + KMS)
- Wildcard match (only in compat mode)
- Deny (strict mode default)
In-memory policy storage with thread-safe concurrent access. Simple permission engine mapping roles to permissions. Resource-level policies (no organization/folder hierarchy in MVP). No token minting (pure policy evaluation only).
Future Considerations:
Role Packs (optional convenience):
- Optional import packs like
packs/pubsub.yaml,packs/bigquery.yaml - Users import only what they need
- Community-maintained, not hardcoded in emulator
- Example:
roles: !include packs/storage.yaml
NOT planned:
- Large built-in permission database (creates maintenance hell)
- Auto-syncing with GCP IAM API (adds network dependency)
- Perfect GCP IAM fidelity (not the goal)
Strategy: Keep the emulator focused and sustainable. Users define what they need via custom roles. The built-in set stays small (primitives + Secret Manager + KMS only).
See ROADMAP.md for full details.
Methods:
SetIamPolicy- Full implementationGetIamPolicy- Full implementationTestIamPermissions- Full implementation with principal matching
Policy Fields:
bindings[]- Role assignments with membersrole- IAM role stringmembers[]- Principal identifiers
version- Policy format version (1=basic, 3=conditions)etag- Optimistic concurrency control (SHA256-based)auditConfigs[]- Audit logging configurationbindings[].condition- Conditional role bindings (CEL expressions)
Features:
- Principal injection via gRPC metadata
- Resource hierarchy policy inheritance
- Custom roles (extensible to any GCP service)
- Conditional bindings (CEL expressions)
- Groups support (nested, 1 level)
- REST API gateway (HTTP/JSON)
- Enhanced trace mode (JSON output, duration metrics)
- Strict mode (unknown roles denied by default)
- No organization/folder hierarchy (project is root)
- No service accounts or token minting
- No audit logging enforcement (auditConfigs accepted but not enforced)
- CEL expressions: basic subset only (startsWith, type equality, time comparisons)
Current scope: Core IAM policy operations for CI/CD testing with emulators
Extracted as the strategic "keystone" auth layer to enable complete GCP emulator ecosystem testing.
This project is not affiliated with, endorsed by, or sponsored by Google LLC or Google Cloud Platform. "Google Cloud", "IAM", and related trademarks are property of Google LLC. This is an independent open-source implementation for testing and development purposes.
Maintained by Dayna Blackwell — founder of Blackwell Systems, building reference infrastructure for cloud-native development.
Blackwell Systems™ and the Blackwell Systems logo are trademarks of Dayna Blackwell. You may use the name "Blackwell Systems" to refer to this project, but you may not use the name or logo in a way that suggests endorsement or official affiliation without prior written permission. See BRAND.md for usage guidelines.
- gcp-emulator - Unified single-process runtime (IAM + Secret Manager + KMS + Eventarc)
- GCP Secret Manager Emulator - IAM-enforced Secret Manager data plane
- GCP KMS Emulator - IAM-enforced KMS data plane
- GCP Eventarc Emulator - IAM-enforced Eventarc data plane
- gcp-emulator-auth - Enforcement proxy library (the guard)
If you're using this IAM emulator — in CI, locally, or in a test harness — I'd love to hear how you're using it.
- What authorization bugs did you catch? (missing bindings, wrong role definitions, conditional policy issues)
- How are you defining roles? (mostly built-in, mostly custom YAML, mix of both)
- What's your setup? (standalone policy engine, orchestrated with data plane emulators, custom integration)
- What's still friction? (policy.yaml complexity, trace mode integration, missing GCP role equivalents)
Open an issue, start a discussion, or reach out directly:
This helps shape the roadmap and ensures the project stays aligned with real-world needs.
Apache License 2.0 - See LICENSE for details.