Production-grade GCP Eventarc emulator — full API surface (47 RPCs), CloudEvent routing with CEL conditions, and multi-protocol support (gRPC + REST + CloudEvents). Run Eventarc locally with no GCP credentials.
Implements the full Eventarc v1 API surface (47 RPCs) plus the Publishing service, including CloudEvent routing, CEL-based trigger matching, and HTTP delivery in binary content mode. Optional IAM enforcement integrates with the local GCP IAM control plane.
Enables local development, integration testing, and CI pipelines for event-driven systems without requiring access to GCP.
- Full Eventarc API surface (47 RPCs including Publishing + Operations)
- CloudEvent routing with attribute filters and CEL condition evaluation
- HTTP delivery in CloudEvents binary content mode (
ce-*headers) - Triple protocol support: gRPC, REST (grpc-gateway), and CloudEvents
- Optional IAM enforcement via local GCP IAM emulator
- Drop-in compatibility with GCP SDKs (no code changes required)
Prerequisites: Go 1.24+
For Docker-based workflows (demo, sdk-demo): Docker with Compose plugin v2
(docker compose version). Docker Desktop includes this by default. On Docker
Engine, install the Compose plugin: https://docs.docker.com/compose/install/
go install github.com/blackwell-systems/gcp-eventarc-emulator/cmd/server-dual@latest
server-dualgRPC on :9085, REST on :8085.
Verify it works:
# Create a trigger
curl -X POST "http://localhost:8085/v1/projects/my-project/locations/us-central1/triggers?triggerId=my-trigger" \
-H "Content-Type: application/json" \
-d '{
"eventFilters": [
{"attribute": "type", "value": "google.cloud.pubsub.topic.v1.messagePublished"}
],
"destination": {
"httpEndpoint": {"uri": "http://localhost:3000/webhook"}
}
}'
# List triggers
curl "http://localhost:8085/v1/projects/my-project/locations/us-central1/triggers"
# List providers
curl "http://localhost:8085/v1/projects/my-project/locations/us-central1/providers"Tip: Pipe curl output through
| jq .for formatted JSON.
grpcurl -plaintext localhost:9085 list
# -> google.cloud.eventarc.v1.Eventarc
# -> google.cloud.eventarc.publishing.v1.Publisher
# -> google.longrunning.OperationsInstall grpcurl: https://github.com/fullstorydev/grpcurl
curl http://localhost:8085/healthz # {"status":"ok"}
curl http://localhost:8085/readyz # {"status":"ok"}Both return HTTP 200. Use these in Docker health checks, Kubernetes probes, or CI readiness scripts. The gRPC-only server (server) does not expose HTTP — probe it with grpcurl list localhost:9085.
┌─────────────────────────────┐
│ GCP SDK / gRPC Client │
└──────────────┬──────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌──────────▼──────────┐ ┌────▼─────┐ ┌──────────▼──────────┐
│ Eventarc Service │ │ LRO │ │ Publisher Service │
│ (40 RPCs: CRUD │ │ Store │ │ (PublishEvents, │
│ for 8 resources) │ │ │ │ PublishChannel- │
│ │ │ │ │ ConnectionEvents) │
└──────────┬──────────┘ └──────────┘ └──────────┬──────────┘
│ │
│ ┌───────────┐ │
│ │ Router │◄───────────┘
│ │ (match by │
│ │ filters │
│ │ + CEL) │
│ └─────┬─────┘
│ │
│ ┌────────▼────────┐
│ │ Dispatcher │
│ │ (HTTP POST, │
│ │ binary mode, │
│ │ ce-* headers) │
│ └────────┬────────┘
│ │
│ ┌────────▼────────┐
│ │ HTTP Endpoint │
│ │ (your service) │
└───────────┘ └───────────────┘
- Configure — Create triggers with event filters and HTTP destinations via the Eventarc gRPC/REST API
- Publish — Send CloudEvents via the Publisher gRPC service
- Route — The router matches events against trigger filters and optional CEL conditions
- Deliver — The dispatcher HTTP POSTs to destinations using CloudEvents binary content mode (
ce-*headers)
Implements the full Eventarc v1 API surface across triggers, channels, message buses, pipelines, and providers, along with the Publishing and Operations services (47 RPCs total).
The API surface is compatible with the official GCP Eventarc clients and follows the same request/response structure. See docs/api.md for the full list of supported operations.
Not sure which to pick? Use
server-dual. It does everything the others do.
| Variant | Protocols | Best For |
|---|---|---|
server-dual |
gRPC + REST | Most users — works with SDKs and curl |
server |
gRPC only | Go/Python/Java SDK users who want minimal overhead |
server-rest |
REST/HTTP only | Shell scripts, curl, non-Go languages without gRPC |
Docker (recommended):
docker build -t gcp-eventarc-emulator .
docker run -p 9085:9085 -p 8085:8085 gcp-eventarc-emulatorNote: The
docker composeexamples require Docker Compose plugin v2 (not the standalonedocker-composev1). Verify withdocker compose version.
Go install:
# gRPC only
go install github.com/blackwell-systems/gcp-eventarc-emulator/cmd/server@latest
# REST API only
go install github.com/blackwell-systems/gcp-eventarc-emulator/cmd/server-rest@latest
# Both protocols (recommended)
go install github.com/blackwell-systems/gcp-eventarc-emulator/cmd/server-dual@latestNote: Go accepts both
-flagand--flag. Examples use--for clarity.
gRPC server:
server --port 9085REST server:
server-rest
# gRPC (internal) on :9086, HTTP on :8085Dual protocol server:
server-dual
# gRPC on :9085, HTTP on :8085Point your existing GCP SDK code at the emulator. No code changes needed beyond the connection setup.
Go:
import (
eventarc "cloud.google.com/go/eventarc/apiv1"
"google.golang.org/api/option"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
conn, _ := grpc.NewClient("localhost:9085",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
client, _ := eventarc.NewClient(ctx, option.WithGRPCConn(conn))
defer client.Close()
// Use client normally — API is identical to real GCPPython:
from google.cloud import eventarc_v1
import grpc
channel = grpc.insecure_channel("localhost:9085")
client = eventarc_v1.EventarcClient(
transport=eventarc_v1.transports.EventarcGrpcTransport(channel=channel)
)# Create a trigger that routes Pub/Sub events to your local service
curl -X POST "http://localhost:8085/v1/projects/test/locations/us-central1/triggers?triggerId=pubsub-trigger" \
-H "Content-Type: application/json" \
-d '{
"eventFilters": [
{"attribute": "type", "value": "google.cloud.pubsub.topic.v1.messagePublished"}
],
"destination": {
"httpEndpoint": {"uri": "http://localhost:3000/events"}
}
}'
# Create a channel
curl -X POST "http://localhost:8085/v1/projects/test/locations/us-central1/channels?channelId=my-channel" \
-H "Content-Type: application/json" \
-d '{}'
# List all triggers
curl "http://localhost:8085/v1/projects/test/locations/us-central1/triggers"When a CloudEvent is published via the Publisher service:
- Attribute filters — Each trigger's
eventFiltersare matched against the event'stype,source, and extension attributes (exact match, all must pass) - CEL conditions — If the trigger has a
conditionfield, it's evaluated as a CEL expression against the event attributes - Delivery — Matching triggers' destinations receive the event via HTTP POST
Events are delivered using CloudEvents binary content mode (matching real GCP Eventarc behavior):
POST /webhook HTTP/1.1
Content-Type: application/json
Ce-Specversion: 1.0
Ce-Type: google.cloud.pubsub.topic.v1.messagePublished
Ce-Source: //pubsub.googleapis.com/projects/my-project/topics/my-topic
Ce-Id: abc-123
Ce-Subject: my-subject
{"subscription":"...","message":{"data":"base64..."}}
Event attributes go in Ce-* HTTP headers; the payload goes in the body. This matches how real Eventarc delivers to Cloud Run and HTTP endpoints.
Set the EVENTARC_EMULATOR_TOKEN environment variable to include an Authorization: Bearer header on all dispatched requests:
EVENTARC_EMULATOR_TOKEN=my-test-token server-dualIf unset, no authorization header is added (fine for local HTTP test servers).
Standalone — Run independently for Eventarc-only testing:
server-dualWith IAM Enforcement — Run with IAM checks:
IAM_MODE=strict IAM_EMULATOR_HOST=localhost:8080 server-dual
# Now requires valid permissions for all operationsOrchestrated Ecosystem — Use with GCP IAM Control Plane:
gcp-emulator start
# Eventarc + Secret Manager + KMS + IAM emulator
# Single policy file, unified authorizationOptional permission checks using the GCP IAM Emulator. All 39 Eventarc operations have IAM permission mappings — see docs/api.md for the full list.
| Scenario | off |
permissive |
strict |
|---|---|---|---|
| No IAM emulator | Allow | Allow | Deny |
| IAM unavailable | Allow | Allow | Deny |
| No principal | Allow | Deny | Deny |
| Permission denied | Allow | Deny | Deny |
| Variable | Default | Description |
|---|---|---|
EVENTARC_EMULATOR_HOST |
localhost:9085 |
gRPC host:port |
EVENTARC_HTTP_PORT |
8085 |
HTTP/REST port (server-rest, server-dual) |
EVENTARC_EMULATOR_TOKEN |
(unset) | Bearer token for dispatched HTTP requests |
GCP_MOCK_LOG_LEVEL |
info |
Log level: debug, info, warn, error |
IAM_MODE |
off |
IAM enforcement: off, permissive, strict |
IAM_EMULATOR_HOST |
localhost:8080 |
IAM emulator address |
Note:
IAM_MODEandGCP_MOCK_LOG_LEVELuse legacy prefixes for backward compatibility. A future release will standardize onEVENTARC_prefix.
The REST API is powered by grpc-gateway v2, which transcodes HTTP/JSON requests to gRPC using the official Eventarc proto HTTP annotations. This means:
- REST paths match the real GCP Eventarc REST API exactly
- Request/response JSON matches GCP's format
- Both the Eventarc service and Publisher service are exposed
Run the full loop — trigger creation, event publishing, and binary content mode delivery — using Docker:
# Start the emulator and a webhook receiver
docker compose up -d
# Run the demo script
./examples/demo.sh
# See the delivered CloudEvent (binary content mode, Ce-* headers)
docker compose logs webhook
# Tear down
docker compose downThe demo creates triggers, channels, message buses, pipelines, and enrollments, then publishes a CloudEvent and shows it arriving at the webhook receiver with Ce-* headers.
Run the full SDK workflow — list providers, create a trigger, publish a CloudEvent, and delete the trigger — using the official cloud.google.com/go/eventarc SDK:
# Terminal 1: start emulator
go run ./cmd/server-dual
# Terminal 2: start webhook receiver (separate module)
cd examples/webhook-receiver && go run main.go
# Terminal 3: run the SDK demo
cd examples/sdk-demo && EVENTARC_EMULATOR_HOST=localhost:9085 go run main.goThe sdk-demo automatically creates my-channel before publishing. If you prefer to pre-create it manually:
curl -X POST "http://localhost:8085/v1/projects/my-project/locations/us-central1/channels?channelId=my-channel" \
-H "Content-Type: application/json" -d '{}'# Run all tests
go test ./...
# With race detector
go test -race ./...
# Integration tests only
go test -v -run TestIntegration ./...- In-memory storage (no persistence)
- Immediate LRO resolution (no async operations)
- Optional IAM enforcement (off by default)
- No regional replication or constraints
- Static provider list (seeded at startup)
Designed for local development and testing — not production use.
This project is not affiliated with, endorsed by, or sponsored by Google LLC or Google Cloud Platform. "Google Cloud", "Eventarc", 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 and AI systems.
- GCP IAM Control Plane — CLI to orchestrate the Local IAM Control Plane
- GCP Secret Manager Emulator — IAM-enforced Secret Manager emulator
- GCP KMS Emulator — IAM-enforced KMS emulator
- GCP IAM Emulator — Policy engine for IAM enforcement
- gcp-emulator-auth — Enforcement proxy library
Apache License 2.0 — See LICENSE for details.