This guide covers deployment topology, configuration delivery, secret management, networking, health checks, observability, and scaling. The concepts apply wherever you run GoBridge; the concrete artifact differs by how you build it. For cloud-specific guidance, see What’s Next.
The AWS deployment profile is not a platform-neutral image. The project publishes no image; on AWS the CDK constructs build one into your own account at deploy time (and the repository root
Dockerfilebuilds the same thing locally — see Container and Orchestrator Deployment). That image runsdeployment/awsand is bound to AWS. It requires SSM to resolve secrets —admin_api_key_paramis mandatory (deployment/aws/lib/model/bootstrap.go), the SSM resolver runs at startup (deployment/aws/lib/bootstrap/secrets.go), and it builds a DynamoDB client unconditionally (deployment/aws/lib/bootstrap/app.go). On Kubernetes and other non-AWS platforms run the maintained Kubernetes profile, which packages the reference binary (MQTT transport, memory/SQLite stores,file://credentials, API keys from a Secret) with explicitGO_BUILD_TAGS=gobridge_mqtt,gobridge_nativeand is tested end to end. Select other reference-binary transports with build tags; no fork is needed for supported families. The GoBridge core and library are portable; the AWS profile binary is AWS-bound.
The reference cmd/gobridge binary is a blank root without build tags:
no transport, store or telemetry exporter is linked. File configuration,
file:// credentials and the admin/monitor HTTP API remain available.
A config naming a transport or store that was not compiled in fails with an
unknown-kind error. To build the Kubernetes profile’s MQTT and memory/SQLite
set locally, run from the repository root:
make build-gobridge GOBRIDGE_TAGS=gobridge_mqtt,gobridge_native
./cmd/gobridge/gobridge.out -version
Direct Go builds use
go -C cmd/gobridge build -tags gobridge_mqtt,gobridge_native -o gobridge.out ..
Use gobridge_aws for SQS and DynamoDB, gobridge_azure for Service Bus,
gobridge_amqp091 or gobridge_amqp10 for AMQP, gobridge_http for the HTTP
transport, and gobridge_otel for OTLP metrics and tracing. Combine tags with
commas or use gobridge_all. The HTTP API itself does not require a family tag.
No family registers processors (tenant, filter, transform), selects a different
config source, or adds a credential backend. Those still require a custom
composition root and explicit wiring. Supported transport/store families do
not: their decoder, factory and seed calls live in plugins_<family>.go.
See PLUGIN.md for the complete
family table and extension contract.
The reference binary takes these flags:
| Flag | Default | Description |
|---|---|---|
-version |
false |
Print version, commit SHA and sorted compiled families, then exit. Unstamped metadata is dev; a blank root reports families=[]. |
-config |
bridge.yaml |
Path to the configuration file |
-log-level |
info |
Log level; an unrecognised value is rejected |
-credentials-dir |
credentials |
Base directory backing file:// credential URIs |
-start-empty |
true |
Start with an empty configuration when -config does not exist. Set false to refuse to boot a bridge that would carry no routes – a mistyped path or an unmounted volume then fails the process instead of quietly transporting nothing. |
-seed-managed-subscriptions |
– | Seed the managed-subscription baseline of a persistent/exclusive MQTT session and exit (repeatable). session-id attests a new broker identity with no subscriptions; session-id=filter,filter records the exact filters the existing broker session holds. Idempotent – see durable sessions. |
Starting empty is a real, supported state, but a limited one: the empty
configuration defines no http block, and this composition root binds its HTTP
listeners once at startup, so a process that started empty serves no admin
API and no health probes. Create the configuration file to converge the
routes – the watcher watches the directory, so file creation is picked up –
and restart the process to bring up the HTTP listeners.
GoBridge supports two deployment modes set via bridge.deployment_mode:
standalone – A single logical bridge instance. One or more replicas
share the same bridge identity but do not coordinate with each other beyond
what the backing stores provide.clustered – Multiple instances with lease-based coordination, shared
outbox draining, and session exclusivity. Requires distributed store
backends (DynamoDB or equivalent).Within these modes, the topology field in the bootstrap config controls how replicas discover and share configuration:
| Criteria | single |
filesystem_replicated |
|---|---|---|
| Replicas | 1 | N (shared config file) |
| Config coordination | N/A | File-based poll watcher |
shared_outbox routes |
Supported | Not supported (use DynamoDB profile) |
| Session lease coordination | Supported | Not supported (use DynamoDB profile) |
| Best for | Dev, low-throughput | Scale-out without DynamoDB |
Use single when you run exactly one replica or during development. Use
filesystem_replicated when you want horizontal scale-out with a shared
filesystem (e.g., EFS, NFS, GlusterFS) and do not need durable outbox or
lease coordination. For full high-availability with outbox and lease support,
use the clustered deployment mode with DynamoDB-backed stores instead.
bridge:
id: my-bridge
deployment_mode: standalone # or "clustered"
shutdown_timeout: 45s # process shutdown budget
drain_timeout: 30s # runtime drain ceiling, keep below shutdown_timeout
# Outbox drain batch ceiling.
per_record_drain_timeout: 3s
max_drain_timeout: 20s
GoBridge separates bootstrap configuration (deployment-level settings) from bridge configuration (routes, sessions, transports). The bootstrap config tells the runtime where to find the bridge config and how to resolve secrets.
Mounted file – Write a bootstrap JSON file to the container filesystem
and set GOBRIDGE_AWS_BOOTSTRAP_FILE to its path. This is the
recommended approach for container orchestrators that support config
volumes (ECS task definitions, Kubernetes ConfigMaps).
Inline environment variable – Set GOBRIDGE_AWS_BOOTSTRAP_JSON
to the full JSON content. Useful for small configs in environments where
file mounts are awkward.
Remote config store – Use the DynamoDB config loader or a custom
ports.Loader implementation. The bridge config is fetched from a
remote store at startup and watched for changes.
The bootstrap loader reads JSON (from GOBRIDGE_AWS_BOOTSTRAP_JSON
or the file named by GOBRIDGE_AWS_BOOTSTRAP_FILE) — it is not YAML:
{
"bridge_id": "my-bridge",
"config_file_path": "/var/lib/gobridge/bridge.yaml",
"admin_api_key_param": "/gobridge/admin-key",
"poll_interval": "5s",
"node_role": "control",
"topology": "single"
}
| Field | Required | Default | Description |
|---|---|---|---|
bridge_id |
Yes | – | Unique bridge identifier |
config_file_path |
Yes | – | Path to the bridge YAML/JSON config file |
admin_api_key_param |
Yes | – | SSM parameter path for the admin API key |
monitor_api_key_param |
No | – | SSM parameter path for the monitor API key |
poll_interval |
No | 1s |
Config file poll interval |
node_role |
No | control |
control or worker. Also selects the admin config single-writer posture — see Admin Config Transactions and the Single-Writer Posture |
topology |
No | single |
single or filesystem_replicated |
admin_addr |
No | :8080 |
Admin server listen address |
monitor_addr |
No | :8081 |
Monitor server listen address |
transport_http_addr |
No | :8082 |
Transport HTTP server listen address |
The bridge config file watcher supports two modes configured via the
config_watch section in the bridge config:
| Mode | Mechanism | Best for |
|---|---|---|
notify |
Hybrid: filesystem events (fsnotify) on the containing directory, debounced, plus a periodic hash-resync backstop (30s default) | Local disks and Kubernetes ConfigMap volume mounts, fast change detection |
poll |
Periodic SHA-256 content comparison | NFS/EFS, network mounts, subPath mounts |
notify mode is safe for Kubernetes ConfigMap volume mounts: the watcher sees
the atomic ..data symlink swap, and the hash-resync backstop catches anything
fsnotify misses (see Kubernetes ConfigMap Config).
Keep poll mode for network filesystems (EFS, NFS) and subPath mounts, which
do not deliver reliable inotify events.
config_watch:
mode: poll
poll_interval: 30s
Pre-deploy checklist item. Any process that writes the watched bridge config file — a deploy script, a templating tool (Helm, Jsonnet,
envsubst), or a CI job — MUST write atomically: render to a temporary file in the same directory, thenrenameit over the target. Never truncate-and-rewrite the file in place.
The watcher can read a truncated in-place write mid-flight. A partial-but-valid
document — one that parses with only bridge.id and no routes — swaps live and
stops forwarding traffic while /health and /ready stay green. Validation
checks bridge.id first and the graph validator permits an empty route graph
(config/validate.go, validate.ValidateBlueprintGraph), so the runtime
loads the empty config as valid and logs no error. rename is atomic, so the
watcher only ever sees the complete old or complete new file.
GoBridge’s own writer already renames; this requirement is only for external writers. See External config writers must write atomically for the failure mode and the fix.
The admin API supports config transactions (/api/v1/admin/config/transactions)
that durably rewrite the bridge config on commit. The file-based profile’s
config store is a parser.FileStore over the shared EFS volume, which is
non-CAS (it has no atomic compare-and-swap SaveIfVersion). On a non-CAS
store, two admin instances that both read version N could each pass the
read-time version guard and clobber each other’s acknowledged commit (a silent
lost update). To prevent that, the admin server fails closed: a durable
commit is refused with HTTP 500 unless the process asserts it is the sole
durable writer of the store (httpapi.Config.ConfigSingleWriter = true).
The bootstrap App derives that assertion from node_role:
node_role |
Single-writer asserted? | Config-txn commit |
|---|---|---|
control (and the default) |
Yes | Permitted — this node owns the RW config store and is the only admin writer |
worker |
No | Refused (fail closed) — a worker mounts EFS read-only and is not a durable writer |
This is correct for both reference topologies: GoBridgeSingle is a single
task, and GoBridgeCluster forces the control service to DesiredCount=1
(workers mount EFS read-only), so exactly one node is ever the config writer.
When you need a CAS config store instead. If you build a genuine
multi-writer deployment — more than one node accepting admin config
transactions against the same shared backend concurrently — asserting
single-writer would be unsafe. Such a deployment MUST wire a
ports.ConditionalConfigStore (compare-and-swap) config store instead, which
serializes concurrent commits safely regardless of the single-writer flag. The
bundled parser.FileStore is not CAS today, so multi-writer config-txn
commits remain refused on the file-based profile — keep to the single control
writer, or supply a CAS store.
GoBridge resolves secrets at startup through the credential URI system. The bootstrap config references SSM Parameter Store paths; the runtime fetches the actual values before building the bridge.
flowchart LR
BC[Bootstrap Config] -->|admin_api_key_param| SSM[SSM Parameter Store]
SSM -->|GetParameter| App[App Runtime]
BC -->|http_receiver_api_key_params| SSM
App -->|X-API-Key header| HTTP[HTTP API]
admin_api_key_param points to an SSM SecureString
read at startup to authenticate admin API requests.monitor_api_key_param gives the monitor
API a separate key; otherwise the admin key covers both.http_receiver_api_key_params and
http_sender_api_key_params map receiver/sender IDs to SSM paths, resolved
into the transport options before the runtime builds.credentials_uri in session/receiver/sender
options resolves from SSM (pms://) or disk (file://). The file:// store
tolerates read-only/immutable mounts, so a Kubernetes Secret mounted
read-only does not crash-loop it – see the
Kubernetes secret-mount cookbook.
Rotation cadence and live-connection behavior:
Credentials & HTTP API and
Credential Rotation.{
"bridge_id": "production",
"config_file_path": "/var/lib/gobridge/bridge.yaml",
"admin_api_key_param": "/gobridge/prod/admin-key",
"monitor_api_key_param": "/gobridge/prod/monitor-key",
"http_receiver_api_key_params": {
"http-in": "/gobridge/prod/receiver/http-in-key"
},
"poll_interval": "5s",
"topology": "single"
}
GoBridge runs three independent HTTP servers on separate ports, so network policies can keep management traffic internal while exposing transport traffic.
flowchart TD
subgraph GoBridge Container
A[":8080\nAdmin API"]
M[":8081\nMonitor API"]
T[":8082\nTransport HTTP"]
end
LB[Load Balancer] --> A
LB --> M
LB --> T
Client[External Clients] --> LB
style A fill:#36f,stroke:#333
style M fill:#3a6,stroke:#333
style T fill:#f96,stroke:#333
| Port | Server | Auth | Purpose | Expose externally? |
|---|---|---|---|---|
:8080 |
Admin | X-API-Key (required) |
Config CRUD, health, runtime status | No (internal only) |
:8081 |
Monitor | X-API-Key (optional) |
Metrics, Prometheus scrape, health probes | Internal or monitoring VPC |
:8082 |
Transport HTTP | Per-receiver api_key |
Message ingress/egress | Depends on use case |
Admin API – Restrict to internal networks only. This server exposes config management, bridge start/stop, and DLQ operations. Never expose it to the public internet.
Monitor API – Allow access from your monitoring infrastructure.
Health probes (/api/v1/monitor/health, /api/v1/monitor/live,
/api/v1/monitor/ready) are unauthenticated so load balancers and
orchestrators can use them directly.
Transport HTTP – Expose only when you use HTTP receivers or SSE senders. Place behind a load balancer with TLS termination. Each receiver/sender has its own API key for authentication.
Probes, the shutdown sequence and its budgets are in Health Checks and Graceful Shutdown. Image pinning, orchestrator integration and building your own image are in Container and Orchestrator Deployment.
GoBridge provides structured logging, metrics, and distributed tracing through
pluggable adapters. The runtime instruments message delivery automatically –
build cmd/gobridge with gobridge_otel for OTLP metrics and tracing, or wire
exporters explicitly in a custom composition root.
Use slog with the JSON handler for machine-parseable output. The
observability.CorrelationHandler wrapper automatically injects contextual
fields into every log record:
| Field | Source |
|---|---|
correlation_id |
x-bridge.correlation-id header (auto-generated if missing) |
trace_id |
Active span’s W3C trace-id when the tracer exposes ports.SpanIdentity (OTel); upstream traceparent fallback |
span_id |
Active span’s W3C span-id (same capability); upstream traceparent fallback |
The cross-hop log join key x-bridge.correlation-id resets per hop unless the
downstream receiver’s route sets trust_bridge_headers: true (ingress strips
and re-generates it otherwise).
jsonHandler := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
Level: slog.LevelInfo,
})
logger := slog.New(observability.NewCorrelationHandler(jsonHandler))
We recommend info level for production and debug for staging, set via bridge.log_level.
Two built-in adapters are available:
| Adapter | Package | Backend |
|---|---|---|
| CloudWatch | adapters/aws/metrics/cloudwatch |
AWS CloudWatch |
| OTLP | adapters/otel/metrics |
Any OTLP-compatible collector |
The shipped AWS file-based image accepts only
nooporcloudwatchfor itsmetrics_exporter— those are the sole values its bootstrap wires (deployment/aws/lib/bootstrap/metrics.go), and any other value is rejected at startup. For OTLP, buildcmd/gobridgewithgobridge_oteland setOTEL_EXPORTER_OTLP_ENDPOINTor the signal-specific metrics/traces endpoint variables. It is not reachable from the stock AWS image; the Kubernetes Dockerfile accepts the tag throughGO_BUILD_TAGS.
The runtime emits metrics automatically when a MetricsExporter is
registered. Key metrics include DeliveryE2ELatency, MessagesReceived,
MessagesSent, RouteErrors, DLQEntries, and OutboxDepth. See
Scenario 18: Observability
for the full metrics table and Go bootstrap code.
The runtime creates a bridge.handleDelivery span around each message delivery
with attributes for route_id, envelope_id, and (when an ingress
traceparent is present) trace_id; W3C traceparent headers propagate
through the bridge. Use a sampling ratio of 0.1 (10%) in production to control
costs.
| Alert | Condition | Severity |
|---|---|---|
| Bridge unhealthy | Health check failing > 1 min | Critical |
| High error rate | RouteErrors / MessagesReceived > 5% |
High |
| DLQ growing | DLQEntries sum > 100 |
High |
| Config reload failure | Reload rejected | Medium |
| Circuit breaker open | State = open > 5 min | Medium |
Configure these alerts in your monitoring system (CloudWatch Alarms, Grafana,
PagerDuty) using the metrics emitted by the bridge runtime. For consumers not
using the CDK constructs, the programmatic path is
cloudwatch.EnsureAlarms(ctx, client, cloudwatch.DefaultAlarms(ns, snsTopic))
alongside an exporter constructed with
WithRollupMetrics(DefaultRollupMetrics()...) and the same namespace –
see Monitoring. Which alarms the shipped CDK
bundle provisions for each deployment shape, which ones DefaultAlarms()
provisions instead, and which ones you must author yourself are listed in
CloudWatch alarms — together with the rollup
metrics every built-in alarm depends on to match a series at all.
Sizing, concurrency, and horizontal-scale guidance is on its own page: Scaling a GoBridge deployment.
| Guide | Description |
|---|---|
| AWS Deployment Guide | ECS Fargate, EFS, SSM, CloudWatch, CDK constructs |
| GCP Deployment Guide | Cloud Run, GCS, Secret Manager (planned) |
| Bare-Metal Guide | Systemd, NGINX, manual TLS (planned) |
For bridge configuration, see:
| Document | Description |
|---|---|
| Configuration Overview | Lifecycle, sources, layered config |
| Configuration Reference | Every config field documented |
| Transport Configuration | MQTT, SQS, Azure SB, HTTP options |
| Processors & Stores | Filter, transform, circuit breaker, store backends |
| Credentials & HTTP API | Credential URIs and admin API endpoints |
| Scenarios | Progressive walkthroughs from simple to clustered |
| Runbooks | Symptom-first incident runbooks and upgrade/rollback procedures |