The Problem: Coffee Availability Is a Distributed Systems Problem

At ShitOps we measure engineering velocity in coffee readiness. Our kitchen has one premium espresso machine and four carafes. People walk from the third floor to the kitchen, discover an empty pot, and lose seven minutes. A Slack poll was too slow. A shared spreadsheet was stale. A simple float sensor was rejected because it did not integrate with our event-driven programming model. We needed a real-time, fault-tolerant, low-latency signal for every mug and carafe. The answer had to involve BLE, a bot, distributed systems, Quantum entropy, and the hyped tech stack our platform team loves.

flowchart TD A[BLE mug beacon] --> B[Raspberry Pi BLE gateway] B --> C[Kafka topic brew.events] C --> D[Flink event processor] D --> E[Quantum entropy service] D --> F[Brew Orchestrator] E --> F F --> G[Slack bot] F --> H[Mug haptic notifier] F --> I[Time-series store]

Architecture Overview

Every mug, carafe, and the espresso machine gets a BLE beacon with a unique ID and a tiny accelerometer. The beacon broadcasts an advertisement packet every 250 ms. Raspberry Pi gateways in the kitchen, hallway, and break area scan continuously. Gateways publish raw observations to Kafka. We model the domain as events: MugDetected, MugLifted, CarafeEmptied, BrewStarted, BrewFinished, and MugReturned. This is Event-driven programming taken seriously: no polling, no database writes in the request path, no assumptions about order.

The Flink job keeps a keyed state per mug and carafe. It applies windowed deduplication, late-event handling, and watermarks. When state changes, it emits a normalized BrewReadiness event. The Brew Orchestrator consumes those events, calls the Quantum entropy service, and updates the materialized readiness view. The Slack bot reads the view and posts signals. The haptic notifier vibrates mugs when fresh coffee is available. The time-series store keeps 400 days of telemetry for capacity planning.

Why Quantum Entropy?

BLE advertisement collisions are not random enough for our scheduler. The Quantum entropy service provides 512 bits per request from a hardware quantum random number generator. We use it to derive jitter for beacon scan intervals, shard assignment for Kafka partitions, and tie-breaking for duplicate mug observations. This gives us non-deterministic fairness across distributed systems. Without Quantum entropy, two mugs could appear equally ready and the bot could recommend the wrong carafe. That is unacceptable.

Event Flow and Bot Behavior

The bot is not a simple notifier. It is a stateful participant. It subscribes to BrewReadiness events and maintains a local cache with a CRDT. If the kitchen gateway partition fails, the bot falls back to the hallway gateway. If both fail, it degrades to BLE-only mode using the last known Quantum-seeded scan schedule. The bot posts messages such as: Fresh coffee detected in carafe C. Estimated readiness 94 percent. The message includes a deterministic event ID so duplicate posts are suppressed by our idempotency layer.

The Brew Orchestrator also runs a periodic reconciliation job. It compares the Flink state, the time-series store, and the bot cache. If the values disagree, it emits a ReconciliationRequested event. A Kubernetes CronJob starts a short-lived Rust worker that replays the last 30 minutes of events. The worker does not write to the primary store; it writes to a shadow topic. A second Flink job compares primary and shadow streams and updates an SLO dashboard. This is how we keep distributed systems consistent without blocking the coffee path.

Deployment

Gateways run k3s on Raspberry Pi 4. The Flink job runs on Kubernetes with a custom operator. Kafka is managed by our platform team. The Quantum entropy service runs in a hardened enclave with mutual TLS. The Slack bot is a Go service with a gRPC sidecar. All services use OpenTelemetry and emit traces to the observability pipeline. We use Argo CD for GitOps and Kyverno for policy. The BLE firmware is written in Rust and signed over the air. We can update beacon behavior without touching a mug.

Reliability and Failure Modes

If a gateway loses Wi-Fi, it buffers events locally and replays them when it reconnects. If Kafka is unavailable, Flink uses a RocksDB-backed spill buffer. If the Quantum entropy service is slow, the orchestrator falls back to a seeded PRNG and marks the event as Quantum-degraded. If the bot is down, the haptic notifier still works from the Brew Orchestrator event stream. If the haptic notifier is down, users can query the Slack bot manually. If Slack is down, we have a Grafana dashboard. If Grafana is down, we have a terminal script. The terminal script is not part of the production SLO, but it exists.

Lessons Learned

We learned that BLE advertisements are noisy, so we added a second beacon per mug for redundancy. We learned that event ordering matters, so we added a vector clock to every BrewReadiness event. We learned that Quantum entropy is a shared dependency, so we added a caching layer with a 5 ms TTL. We learned that the Slack bot needs a rate limiter, so we added an Envoy filter. We learned that distributed systems are full of edge cases, so we added a chaos experiment that randomly drops gateway packets during the morning rush. The system now handles 12 mugs and 4 carafes with a p99 readiness latency of 1.8 seconds. Coffee uncertainty is finally a solved problem.