# Blog

Technical write-ups.

Source: https://cybars69.github.io/blog/


- [A GEO audit is a checklist, not a ranking model](https://cybars69.github.io/blog/geo-audit-checklist-not-ranking-model.md) (2026-09-24): What an audit of a young marketing site found, and why fixing every point on the checklist still wouldn't have moved it off page six.
- [A shop on a key-value store: index arrays, immutable Stripe prices and what you give up](https://cybars69.github.io/blog/a-shop-on-a-key-value-store.md) (2026-09-24): Modelling products and collections in Workers KV, syncing them to Stripe without breaking history, and the reliability limits of a store with no queries.
- [Demo mode as signed config, not a fork](https://cybars69.github.io/blog/demo-mode-as-signed-config.md) (2026-09-24): Ephemeral public demo stores that can't send email, take payments or be crawled, driven by one flag in the same signed document as everything else.
- [Direct-to-R2 uploads with presigned PUTs: presign, PUT, confirm](https://cybars69.github.io/blog/presigned-uploads-to-r2.md) (2026-09-24): Files go from the browser straight to object storage. The server issues a URL, then trusts nothing until the client confirms.
- [Dropping the agent framework](https://cybars69.github.io/blog/dropping-the-agent-framework.md) (2026-09-24): Rebuilding a multi-tenant WhatsApp chatbot with a cheap classifier, a plain router and raw API calls instead of an agent framework.
- [iCal is not a channel manager: what ARI restrictions are and why a booking engine needs them](https://cybars69.github.io/blog/ical-is-not-a-channel-manager.md) (2026-09-24): An audit of a stays marketplace against what a real online travel agency needs, and the availability, rates and inventory model behind it.
- [Modelling quizzes: question types, manual grading and the attempt state you didn't know you needed](https://cybars69.github.io/blog/modelling-quizzes-question-types-and-attempt-states.md) (2026-09-24): What I learned by reading the source of a mature open-source LMS plugin before designing a quiz schema.
- [Observability that lives outside the app stack](https://cybars69.github.io/blog/observability-outside-the-app-stack.md) (2026-09-24): Grafana, Loki, Promtail, Prometheus, cAdvisor and node_exporter as their own compose stack, joined to the app only by a shared network.
- [One state machine for work orders: deleting the workflow](https://cybars69.github.io/blog/one-state-machine-for-work-orders.md) (2026-09-24): A print shop's order system with a single three-state task machine, an atomic pickup, a delivery gate and a signed customer ledger.
- [OTP login, refresh-token families and three layers of access for multi-tenant portals](https://cybars69.github.io/blog/otp-auth-for-multi-tenant-portals.md) (2026-09-24): One identity system for patients, clinic staff, distributors and admins: challenge-based OTP in Redis, rotating refresh tokens, and access that is checked in three separate layers.
- [Plans that never block checkout](https://cybars69.github.io/blog/plans-that-never-block-checkout.md) (2026-09-24): Designing subscription tiers for a multi-tenant commerce platform so that billing state can never take a store's checkout offline.
- [Provisioning a WhatsApp number in six steps](https://cybars69.github.io/blog/provisioning-a-whatsapp-number-in-six-steps.md) (2026-09-24): Twilio subaccounts, a Chatwoot account, an agent bot, an inbox and a Meta embedded signup, wired into one onboarding flow.
- [Rebuilt this site with Astro](https://cybars69.github.io/blog/rebuilt-with-astro.md) (2026-09-24): Why the site moved from hand-written HTML to Astro, and how it deploys from a docs folder.
- [Signed entitlements over KV: Ed25519, a five-step fallback chain, and a document that can only narrow](https://cybars69.github.io/blog/signed-entitlements-over-kv.md) (2026-09-24): How a store Worker learns what it may do without ever learning what a plan is called, and why every failure path resolves to the shop still selling.
- [Streaming microphone audio from a browser, in three attempts](https://cybars69.github.io/blog/streaming-mic-audio-three-attempts.md) (2026-09-24): WebM chunks, WAV, decoded PCM and finally an AudioWorklet. What broke, why, and what the two failed write-ups got wrong.
- [The control-plane seam: idempotent usage snapshots, an event outbox and HMAC commands](https://cybars69.github.io/blog/control-plane-seam-usage-events-hmac.md) (2026-09-24): How a per-tenant Worker and a central control plane talk to each other safely over unreliable networks, with no queue infrastructure.
- [The repo is the system of record](https://cybars69.github.io/blog/repo-as-system-of-record.md) (2026-09-24): Setting up a small codebase so that both people and coding agents can work in it reliably, without a 500-line instruction file.
- [The webhook returns 200 first: a WhatsApp agent's message pipeline, tool loop and human handoff](https://cybars69.github.io/blog/the-webhook-returns-200-first.md) (2026-09-24): What happens between a customer's message and the reply: filtering, acknowledging early, classifying, routing, a tool loop, a silent fallback and a four-call escalation.
- [Two currencies, one invoice: keeping subscription billing and shopper commerce apart](https://cybars69.github.io/blog/two-currencies-one-invoice.md) (2026-09-24): A multi-tenant platform that bills merchants in one currency while their shoppers pay in another, and the bugs that appear when the two share a field.
- [Why my WebSocket kept disconnecting](https://cybars69.github.io/blog/websocket-pings-blocked-event-loop.md) (2026-09-24): Ping timeouts, restarting sessions and rapid reconnect loops. The fix wasn't a timeout setting; it was a blocking call.
