Home / Writing

Notes from building things

Write-ups of my side projects by Vu Minh Hoa: how they work, the trade-offs behind them, and what I would change.

These posts are drafted with AI assistance from the projects’ own source code and documentation.

01 / 9 posts

Backend, data & infrastructure

Stream processing, audit logs, real-time sync, search and the server the projects run on.

  1. One Keycloak for several apps: realms, audiences and quiet failures

    Self-hosted Keycloak for several side projects: realm per app, checking aud, deleted users recreated by live tokens, refresh races and a startup trap.

    · 10 min read

  2. Exactly-once doesn’t come for free: Kafka, Flink and ClickHouse

    Where an event pipeline loses or double-counts data: idempotency keys claimed too early, sink buffers outside checkpoints, and materialized views.

    · 9 min read

  3. A tamper-evident audit log in PostgreSQL with hash chains and HMAC

    Building an audit log whose history can’t be quietly rewritten, layer by layer, plus detecting truncation and clearing IPs without breaking the chain.

    · 7 min read

  4. Server-Sent Events in Spring Boot: Redis Pub/Sub and slow clients

    Real-time sync with SseEmitter: cookie auth for EventSource, fan-out across instances, a bounded write pool that drops hints, and when to consider WebFlux.

    · 8 min read

  5. Time in Apache Flink: watermarks, idle partitions, state TTL and replay

    Silently dropped late events, a quiet partition stalling every window, TTL on the wall clock, and processing-time timers that break deterministic replay.

    · 7 min read

  6. Vietnamese search in Elasticsearch: diacritics, n-grams and filter order

    Accent-insensitive Vietnamese search: asciifolding, why stopwords must come before it, n-grams for partial codes, search_after paging and rebuildable indexes.

    · 7 min read

  7. Tracking Vietnamese news trends with embeddings, HDBSCAN and an LLM

    How I group articles from ~95 Vietnamese news feeds into topics, score which ones are rising or fading, and have an LLM explain why.

    · 9 min read

  8. Modeling hospital procurement bidding: lessons from my graduation project

    A ~30-step public bidding process in software: one record from proposal to acceptance, JSON phases with defaults, and acceptance as one transaction.

    · 10 min read

  9. Self-hosting side projects: principles and silent failures

    Infrastructure in git, a small attack surface, canary deploys, and the bugs that never raised an error: pipefail, Actions concurrency, Docker bind mounts.

    · 8 min read

02 / 6 posts

Local-first apps

Apps that keep working offline: sync protocols, IndexedDB at scale and PWAs on iPhone.

  1. Local-first PWA sync: last-writer-wins done carefully

    Offline-first sync with IndexedDB: idempotent pushes, tombstones, seq order with advisory locks, server-corrected clocks, and data shaped to avoid conflicts.

    · 8 min read

  2. Four bugs that silently broke RxDB sync, and how they were fixed

    Rows stuck at version 0, a client-only column, a migration that never finished, and a resync loop: sync failures that never threw an error.

    · 10 min read

  3. A large tree in IndexedDB with RxDB: indexes, trigram search and lazy loading

    Why a nullable parent ID made a 61-node query take 19.6 s, and how a shadow index, diacritic-free trigrams and virtualization fixed it.

    · 8 min read

  4. An installed PWA on iPhone: safe updates, deploys and iOS details

    Updates that wait for the user, deploys that broke open tabs, a launch logo measured on the device, zooming fields, and Web Push for a local-first app.

    · 8 min read

  5. Parsing money the way Vietnamese people type it: 2,5tr, 500k and 1tr+500k

    Units like tr and tỷ, a dot that means two things, sums typed as in Excel, a caret that stays put, and the missing + key on phone keypads.

    · 8 min read

  6. Vietnamese lunar calendar in JavaScript: why 5 test dates weren’t enough

    Why a popular lunar library disagrees with the Vietnamese calendar on 3.48% of days (UTC+8 vs UTC+7), found by comparing 40,542 days instead of 5.

    · 8 min read

03 / 5 posts

Security & macOS

End-to-end encryption and a macOS monitor built on Endpoint Security.

  1. DarkSword on iPhone: the six-bug chain, P7, and which iOS is safe

    How the DarkSword iOS exploit chain works, what the P7 variant steals, which iOS versions are safe, and what to do if you can’t update yet.

    · 10 min read

  2. Building an end-to-end encrypted chat app with libsodium

    The key design behind an E2EE chat: per-chat keys sealed to each member, signed message chains, and the general limits of detecting a cheating server.

    · 8 min read

  3. Endpoint Security on macOS without the entitlement: eslogger and Swift

    Reading eslogger from a root Swift helper: Full Disk Access, a one-pass Aho–Corasick prefilter on raw JSON, rules scoped to the program, and self-noise.

    · 8 min read

  4. Making a Mac security monitor hard to silence

    Telling a reboot from kill -9, a Telegram bot that can delete its own alerts, processes that watch each other, an append-only log and a dead man’s switch.

    · 8 min read

  5. From a polling Bash script to an event-driven Swift daemon on macOS

    How a macOS security daemon went from ~4.5% to ~0.4% of a core: APIs instead of forks, FSEvents and IOKit, ctime prefilters, and a netstat that hides sockets.

    · 8 min read

04 / 2 posts

Web & open source

An SEO analysis library and the rules behind its scores.

  1. Forking a GPL library and publishing it on npm without losing upstream

    Forking Yoast’s yoastseo: GPL notices, a no-change merge that makes syncs three-way, a monthly filter-repo sync, and rules that keep conflicts rare.

    · 8 min read

  2. Auditing an SEO checker against Google’s docs, and silent scoring bugs

    Tracing every rule of an open-source SEO checker to Google’s guidance, what changed without a source, and score bugs that never threw an error.

    · 9 min read