A Kafka message wakes a headless agent. The agent streams every step back out as CloudEvents. KEDA scales the consumer from zero and back. This page walks the wire, one message at a time, and shows the event that travels on each step. Press Next for one step. Press Prev to go back. The wire box shows the messages of the current step, and the panel under it shows that step's Kafka headers and data payload. Four flows: one agent, a batch of a hundred, a forged event, and scale-to-zero as a trust control.
Commit the Kafka offset after the terminal event, not when the message is read. Consumer lag then means "work not finished", not "work not read". KEDA scales on that lag, so the queue gauge and the agent lifetime are the same fact. It is structurally impossible for KEDA to remove a pod mid-run. One decision settles both "never miss an event" and "do not kill a running agent".
eventrunner/config.py:82 — commit_after_terminal: bool = True
| EVENT | SIGNED | RULE |
|---|---|---|
| request | yes | EventBridge signs every wake |
| response, final=true | yes | the terminal event only |
| response, intermediate | no | passed through by policy |
| group.* | yes | no final attribute, so treated as terminal |
A signature proves who finished a run. It does not prove what the run said along the way. shared/signing.py:430 _is_terminal
A group event carries no final attribute at all. Testing final alone would class it as an intermediate frame and wave it through — which is exactly the forged group.completed that would end a live batch. It also carries groupid and no correlationid, so the responses consumer routes on is_group_event rather than on the key. shared/ce.py:121
| OPENSHIFT | KIND | |
|---|---|---|
| e2e assertions | 49/49 | 37/37 |
| POST → scale up | 1.7–3.0 s | 2.2–4.3 s |
| POST → pod Ready | ~14 s | — |
| back to zero | 22 s | — |
3000 ms of the wake is the broker group.initial.rebalance.delay.ms. It is not signing overhead. Kind sets it to 0, so Kind timings do not compare to cluster timings.
| submitted | 100 agents, one POST |
| finished / failed | 100 / 0 |
| wall clock | 1m20s |
| slowest agent | 79.6 s |
| peak replicas | 10 — the cap, reached at t=6s |
| notifications | 2 — not 100, not 102 |
KEDA does 0 → 1 and hands 1 → N to an HPA, whose scaleUp policy here jumps straight to the cap instead of climbing. Partitions are the real ceiling — Kafka gives each partition to one consumer in a group, so maxReplicaCount: 10 sits under the topic's 12.
While a group is open its members' own notifications are suppressed, keyed on membership rather than on the group still being open — so a redelivered request whose duplicate terminal arrives after completion stays quiet too.
Humans read brave-otter-4718. The agent CLI requires a real UUID. So both services derive the session id from the correlation id:
No mapping table and no database read on the resume path. Both sides compute the same answer from the slug alone.
Drift: spec §2.3 prints the namespace as …-rossoctlkeda1, which is not hexadecimal and raises ValueError. The code uses …-000000000001 (shared/ce.py:19), and the spec's own §6a.4 line 972 shows that correct value.
| FLAG | DEFAULT |
|---|---|
| ER_COMMIT_AFTER_TERMINAL | true |
| ER_REQUIRE_SIGNATURE | false |
| EB_AUTH_TOKENS | unset — open |
| ER_MAX_CONCURRENT | 4 |
Enforcement is opt-in. An unconfigured deployment verifies nothing and accepts an unauthenticated POST. The cluster run turned both on deliberately through the kind-signed overlay.