GREEN · RED · BLUE PACKAGE SKILL · VULTR · AUTOMQ

AutoMQ Package Skill

A reproducible workflow for a three-node AutoMQ cluster on Vultr — the Kafka 3.9 wire protocol over brokers whose storage tier is object storage rather than replicated disks. Shipped in Clojure, TypeScript and Python, rendering the same bytes from one colors.yml.

Replication factor 1 is the design, not a mistake. AutoMQ acknowledges a produce once the record is in Cloudflare R2, so replicas would add cost and write amplification without adding durability — upstream ships RF=1 for the same reason. The three nodes buy a controller quorum, partition failover, and throughput. What RF=1 does not buy is availability, which is why acceptance kills a broker that leads a specific partition and measures how long that partition stays unwritable.

Quick start

npx skills add getcolors/automq
cp .agents/skills/package-automq-green/green ./green
chmod +x green
./green build
./green create --dry-run

Build renders reproducible output and dry-run walks the workflow without provider side effects. Real creation and deletion require explicit authorization.

Substitute package-automq-red/red or package-automq-blue/blue for the same verbs on Bun or uv. The three colours render byte-identical artifacts, and scripts/parity.sh is what proves it on every change.

Architecture

Compute

Three Vultr instances in one VPC, and a firewall that opens 22 and 9092 — nothing else. The controller quorum and inter-broker replication never touch the public interface.

Storage tier

Two R2 buckets, adopted and never created. AutoMQ writes hash-prefixed keys at the bucket root and supports no path prefix, so each bucket belongs to one cluster outright — proven empty before it is claimed.

Identity

Four principals, no anonymous access anywhere. The controller listener uses PLAIN from a static JAAS entry; inter-broker and public listeners use SCRAM bootstrapped into the metadata log at genesis.

Acceptance

Six gates on the hosts and seven from the workstation, including a targeted failover: the partition is chosen because node 2 leads it, so the test cannot pass without exercising the broker it killed.

Connecting

The bootstrap name carries one A record per node; each broker advertises its own name, and one Let’s Encrypt certificate covers all of them.

kcat -b <automq-host>:9092 \
  -X security.protocol=SASL_SSL \
  -X sasl.mechanism=SCRAM-SHA-512 \
  -X sasl.username=automq \
  -X sasl.password=<from automq-credential> -L

The password is generated on the server and printed by automq-credential, a root-only command deliberately separate from automq-status — the command an operator runs a hundred times must not put a credential into scrollback.

Authentication is not authorization

Port 9092 faces the internet, so SCRAM alone is not the boundary. The cluster runs StandardAuthorizer with allow.everyone.if.no.acl.found=false, and the public client principal holds prefixed Describe/Read/Write on topics and Describe/Read on groups — no Create, no Alter, no ClusterAction. Acceptance asserts the denials, not only the grants.

One certificate issuer

Node 0 alone runs ACME and is the only host that receives a Cloudflare token, publishing the result to the ops bucket for the others to pull. Three nodes issuing independently would race on the shared _acme-challenge record for the bootstrap name — each deleting the others’ proof — and would put a zone-editing credential on every publicly reachable broker. Restarts are ordered by a conditional-create lease in object storage, because a node checking its own quorum health cannot order actors it cannot see.

Exposure

PortExposurePurpose
22vultr-ssh-sourcesKey-only SSH: convergence and recovery
9092vultr-kafka-sourcesThe public Kafka endpoint: SASL_SSL, SCRAM-SHA-512, ACL-gated
9093VPCKRaft controller quorum
9094VPCInter-broker traffic
delete never empties the buckets. They hold the cluster’s data, and a teardown that silently erased them would make an accidental delete unrecoverable. Purging is a separate, deliberate action.

Source

github.com/getcolors/automq · reference deployment automq-vultr · getcolors.ai