AgentBus Tech Stack
Status: Draft v0.1 | Date: 2026-10-10 | Owner: Founding team
Each choice lists the reason, the alternatives considered, and the condition under which the choice should be revisited.
1. Summary table
| Layer | Choice | Alternatives considered | Revisit when |
|---|---|---|---|
| Server language | Go 1.25+ | Rust, TypeScript | Never for MVP; Rust only for a hot path proven by profiling |
| Sidecar language | Go (same codebase) | Rust | Same |
| Broker | NATS JetStream 2.11+ | RabbitMQ, Kafka/Redpanda, MQTT brokers, Redis Streams | Per-tenant stream count exceeds tens of thousands |
| Control plane DB | PostgreSQL 19 | CockroachDB, MySQL | Multi-region writes become mandatory |
| Analytics and audit | ClickHouse | TimescaleDB, Postgres partitions, BigQuery | Never for MVP |
| Object storage | S3-compatible (Vultr, AWS S3, MinIO self-host) | GCS | Never |
| Edge / TLS | Caddy | Traefik, Envoy, nginx | Need L7 features Caddy lacks, then Envoy |
| Policy | Cedar (cedar-go) | OPA/Rego, Casbin, hand-rolled | Never |
| Identity | Clerk, through a generic OIDC verifier | Zitadel, Keycloak, Ory, WorkOS, Auth0 | Enterprise SAML demand or self-host, then Zitadel/Keycloak |
| Console | Next.js (App Router), TypeScript, Tailwind | SvelteKit, Remix | Never |
| API style | REST + JSON, OpenAPI 3.1, WebSocket for streams | gRPC external, GraphQL | Never external; gRPC internal is allowed |
| Schemas | JSON Schema 2020-12, CloudEvents 1.0 | Protobuf, Avro | Binary efficiency becomes a measured problem |
| Signing | Ed25519, RFC 8785 canonical JSON | HMAC, ECDSA | Never |
| Tracing and metrics | OpenTelemetry, Prometheus, Grafana, Tempo, Loki | Datadog, Honeycomb | Managed SaaS if ops burden dominates |
| Orchestration | Kubernetes, Helm, Terraform | Nomad, plain VMs | Never for hosted; compose for self-host |
| CI/CD | GitHub Actions | GitLab CI | Follows where the repo lives |
| Billing (post-MVP) | Stripe Billing + Stripe Connect | Paddle, Lago | Never |
| Local store in sidecar | SQLite (modernc, pure Go) | BoltDB, files | Never |
2. Languages
2.1 Go for gateway, sidecar, ingest workers, ADK core
- One language across server and client; the sidecar library is the Go ADK.
- First-class NATS client maintained by the NATS team.
- Static, cross-compiled single binaries for Linux, macOS, Windows on amd64 and arm64.
- Strong standard library TLS, crypto (Ed25519), and HTTP/2.
- Fast compile and iteration speed for a small team.
Rust was considered for the sidecar. It would give smaller binaries and no GC, but neither is a real constraint for a sidecar that mostly waits on sockets, and the NATS and Cedar ecosystems are more mature in Go. Keep the door open for Rust only on a profiled hot path.
2.2 TypeScript for console, support console, TypeScript SDK, Claude Code plugin glue
2.3 Python for the Python SDK only
3. Broker: NATS JetStream
Why NATS over the alternatives:
| Need | NATS JetStream | RabbitMQ | Kafka / Redpanda | MQTT broker |
|---|---|---|---|---|
| Per-agent durable inbox | Filtered consumer per agent, cheap | Queue per agent, heavier | Partition per agent, impractical | Topic per agent, weak persistence story |
| Request-reply | Native | Via reply-to queues | Not native | Not native |
| Multi-tenancy | Accounts and subject prefixes | vhosts | ACLs, topics | ACLs |
| TLS, mTLS | Native | Native | Native | Native |
| Single binary ops | Yes | Erlang runtime | JVM or C++ cluster | Varies |
| KV and object store built in | Yes | No | No | No |
| Per-message TTL | 2.11+ | Yes | No | No |
| Dedupe by message id | Yes | No (plugin) | Idempotent producer | No |
| Speaks MQTT if ever needed | Yes | Plugin | No | Yes |
Configuration baseline: 3 replicas per stream, file storage, max message size 1 MiB plus envelope overhead, explicit ack, pull consumers. Enterprise tenants may get a dedicated account. See 05-DELIVERY-SEMANTICS.md and 10-INFRA-AND-OPERATIONS.md.
4. Databases
4.1 PostgreSQL 19 for the control plane
- Row-level security for tenant isolation.
- Partitioned
messagesmetadata by month. pgcryptonot used for payloads; payload encryption happens in the gateway with tenant keys.- Migrations with golang-migrate, checked in CI against a fresh database and against the previous release's schema.
- Connection pooling via PgBouncer in transaction mode.
- Managed provider with point-in-time recovery. Confirm the provider ships 19 before committing the environment; design uses nothing newer than 16 features so a downgrade is not a blocker.
4.2 ClickHouse for events, usage, and audit
- MergeTree tables ordered by
(tenant_id, event_time, message_id), partitioned by month. - Storage policy with a hot volume and a cold S3 volume.
TTL ... TO VOLUME 'cold'after the hot window,TTL ... DELETEusing a per-rowretention_dayscolumn materialised from the tenant plan. - Row policies on
tenant_idfor any direct query path. - Ingest through the
sys.auditNATS stream and a batching worker, never directly from the request path, so analytics outages never block publishes. - ClickHouse Cloud for hosted if ops time is scarce; single node bundled in self-host compose.
4.3 Object storage
S3-compatible API only, so hosted (Vultr Object Storage or AWS S3) and self-host (MinIO) share code. Server-side encryption plus tenant-key encryption of payload bytes. Lifecycle rules match the retention matrix in 07-DATA-MODEL.md.
4.4 SQLite in the sidecar
Pure Go driver, no cgo, so the single binary stays static. Tables: inbox, outbox, receipts, adapter state. WAL mode. The transport ack is sent only after the SQLite commit.
5. Edge and networking
5.1 Caddy
- Automatic ACME certificates and renewals.
- On-demand TLS for Enterprise custom domains: the tenant adds a CNAME, the console verifies it,
and Caddy issues a certificate on the first request, gated by an
askendpoint on the gateway. - HTTP/2 and WebSocket proxying.
- HSTS, TLS 1.3 only.
Envoy is the fallback if advanced L7 policies or mTLS termination at scale are needed. mTLS in the Enterprise tier can start by terminating at Caddy with a per-tenant client CA.
5.2 Domains
| Host | Purpose |
|---|---|
| agentbus.exchange | Marketing |
| console.agentbus.exchange | Console |
| console.agentbus.exchange/docs | Docs, error pages, llms.txt |
| support.agentbus.exchange | Support console (separate app, separate auth policy) |
| komsary.agentbus.exchange | Gateway, API, WebSocket, schema registry |
| agentbus.exchange | Marketplace (post-MVP) |
The gateway lives on a different host from the console and sets no cookies, so session cookies never reach the API.
6. Identity and authorisation
6.1 Clerk via a generic OIDC verifier
- Console uses Clerk components for sign-in, passkeys, social login, and organisation invites.
- The gateway verifies JWTs with a generic OIDC JWKS verifier configured with issuer, audience, and JWKS URL. Clerk is one issuer config; Keycloak or Zitadel is another.
- AgentBus mints its own
usr_ids. The IdP subject is stored as(idp, subject). - Memberships and roles live in Postgres. Clerk Organisations are mirrored in by webhook for UI convenience and are never the source of truth.
- Self-hosted edition ships with the generic OIDC path and a Keycloak example compose file.
6.2 Cedar
cedar-goembedded in the gateway, policies loaded from Postgres and cached with a version.- Schema defines entity types
Tenant,Workspace,User,Agent,Token,Grantand actionssend,receive,read_audit,manage. - Every publish evaluates
sendwith the sender agent as principal, the recipient agent as resource, and the envelopetypeandcapabilityin context. POST /policy/simulateruns the same evaluation and returns the deciding policy id.
7. Protocol and schema tooling
- CloudEvents 1.0 JSON format with the CloudEvents Go SDK for parsing.
- JSON Schema 2020-12 for payloads, validated in the gateway with a compiled schema cache.
- Schemas are source files in the repo, published to
komsary.agentbus.exchange/schemas/. - OpenAPI 3.1 is the source for the TypeScript and Python SDKs via code generation.
- RFC 8785 canonical JSON for signing.
8. Observability
- OpenTelemetry SDK in gateway, sidecar, and ingest. The envelope's
traceparentis the root. - Prometheus metrics scraped from every service, Grafana dashboards checked into the repo.
- Tempo for traces, Loki for logs, both behind Grafana.
- Alertmanager rules in 10-INFRA-AND-OPERATIONS.md.
9. Build, release, supply chain
- Go modules with vendoring disabled,
govulncheckin CI. - goreleaser builds the sidecar for all targets, generates SBOMs (Syft), signs with cosign (Sigstore keyless), publishes checksums, Homebrew tap, and a curl install script that verifies checksums and signatures.
- Container images are distroless, signed, scanned (Trivy).
- Release channels for the sidecar:
stableandedge.
10. Testing
| Level | Tooling |
|---|---|
| Unit | Go testing, testify |
| Schema contract | Generated tests from JSON Schemas, run against every message type example |
| Integration | testcontainers for NATS, Postgres, ClickHouse, MinIO |
| Conformance kit | Black-box suite any adapter or SDK must pass; published as a binary |
| Chaos | Kill gateway after publish, kill sidecar after transport ack, NATS leader change |
| Load | k6 against staging, targets from the SLOs |
| Security | gosec, semgrep, Trivy, dependency review, pentest before Business tier launch |
11. Developer environment
docker compose upbrings up NATS, Postgres, ClickHouse, MinIO, Caddy, gateway, ingest, console, and a Keycloak instance for generic OIDC testing.make devruns the gateway with live reload;make sidecarbuilds the local binary.- Seed script creates a tenant, two workspaces, three agents, and sample traffic.
12. Explicit non-choices
- No GraphQL. The API must be
curl-able by an AI debugging a problem. - No Kafka. Partition-per-agent is the wrong shape and the ops cost is unjustified.
- No home-grown auth. Password storage and MFA are an IdP's job.
- No custom envelope. CloudEvents already exists.
- No Redis. NATS KV covers presence, revocation, and counters.