<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>mahui.me</title><description>Ma Hui&apos;s personal site: writing and indie work.</description><link>https://mahui.me/</link><item><title>A TLS Certificate Inside a QR Code — No CA Will Vouch for a Mac on Your Home Network</title><link>https://mahui.me/en/blog/lynsi-certificate-in-qr/</link><guid isPermaLink="true">https://mahui.me/en/blog/lynsi-certificate-in-qr/</guid><description>Lynsi&apos;s phone and Mac talk over TLS 1.3, but the certificate chain simply doesn&apos;t exist in this setting. The fix: put the certificate itself into the pairing QR code, and have the phone trust exactly that one from then on. Three decisions — P-256 over RSA, generating the certificate in-process instead of shelling out to openssl (and what happened in the sandbox when we didn&apos;t), and why the QR code is safe to photograph.</description><pubDate>Sun, 23 Aug 2026 00:00:00 GMT</pubDate></item><item><title>TLS Alone Doesn&apos;t Know Who&apos;s Calling — Lynsi&apos;s Handshake Gate, and the Test Script That Lied to Me Twice</title><link>https://mahui.me/en/blog/lynsi-handshake-gate/</link><guid isPermaLink="true">https://mahui.me/en/blog/lynsi-handshake-gate/</guid><description>TLS gives you a private channel; it doesn&apos;t tell you who is on the other end of it. Lynsi&apos;s Mac receiver used to process every frame the moment TCP came up — an unpaired peer could write its clipboard. This is the design of the gate that fixed it: unauthenticated connections may send exactly one kind of frame, get five seconds, and receive nothing back — plus the two bugs in my own verification script, each of which made the test results the exact opposite of the truth.</description><pubDate>Sun, 23 Aug 2026 00:00:00 GMT</pubDate></item><item><title>256 Characters, No Appeals — After a Restricted HarmonyOS Permission Got Rejected</title><link>https://mahui.me/en/blog/lynsi-harmony-acl-rejection/</link><guid isPermaLink="true">https://mahui.me/en/blog/lynsi-harmony-acl-rejection/</guid><description>Lynsi applied for two restricted HarmonyOS permissions. READ_PASTEBOARD passed; READ_IMAGEVIDEO came back rejected: &apos;scenario does not meet the permission&apos;s usage requirements.&apos; On reflection, the rejection was correct — the feature is backup, but every line of copy said &apos;browse.&apos; This is the full story of the reapplication: spending 256 characters in the order the reviewer judges, answering the PhotoViewPicker suggestion head-on, and the rule above all others — every promise in the application must actually be true in the code.</description><pubDate>Sun, 23 Aug 2026 00:00:00 GMT</pubDate></item><item><title>Pricing Against a Free Official App — Where Lynsi&apos;s Paywalls Go</title><link>https://mahui.me/en/blog/lynsi-pricing-against-free/</link><guid isPermaLink="true">https://mahui.me/en/blog/lynsi-pricing-against-free/</guid><description>Lynsi&apos;s direct competitor is Huawei&apos;s own SuperHub: free, and first-party. This is how the paid boundary was drawn — everything the official app does for free stays free, and walls stand only on capabilities it doesn&apos;t have. Why mirroring and video streaming share one 10-minute daily pool, why the allowance is per-day rather than per-session, how gallery saving got reclassified from free to Pro, and three schemes that were rejected — each of which nearly shipped.</description><pubDate>Sun, 23 Aug 2026 00:00:00 GMT</pubDate></item><item><title>A Segmented Control You Can&apos;t Click on Mac — Catalyst Doesn&apos;t Deliver Taps Into List Section Headers</title><link>https://mahui.me/en/blog/flick-catalyst-section-header-hit-testing/</link><guid isPermaLink="true">https://mahui.me/en/blog/flick-catalyst-section-header-hit-testing/</guid><description>Flick&apos;s range picker had a sort control that worked fine on iPhone and was completely dead on Mac. It rendered correctly, compiled without warnings, and shared one code path across both platforms. The only difference was that clicks never reached it. The cause: it lived in a List Section header.</description><pubDate>Thu, 06 Aug 2026 00:00:00 GMT</pubDate></item><item><title>Implementing a Protocol You&apos;re Not Allowed to Read — Re-deriving Mosh from Packet Captures</title><link>https://mahui.me/en/blog/shellby-mosh-clean-room/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-mosh-clean-room/</guid><description>Official mosh is GPLv3; Shellby is a closed-source commercial app, so reading or copying it was off the table. I re-derived the SSP protocol from the paper plus my own packet captures: every field number came from measurement, guessing one wrong crashes mosh-server outright, and the same protocol had to be byte-identical across Swift and Dart.</description><pubDate>Thu, 06 Aug 2026 00:00:00 GMT</pubDate></item><item><title>Mosh Was Painfully Slow, and I Almost Blamed the Tradeoff I&apos;d Just Made</title><link>https://mahui.me/en/blog/shellby-mosh-rtt-misdiagnosis/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-mosh-rtt-misdiagnosis/</guid><description>Shellby&apos;s from-scratch mosh-compatible transport shipped and was unusably laggy. I had just made a deliberate throughput-sacrificing tradeoff — stop-and-wait state sync — so it was the obvious suspect. But one metric didn&apos;t fit, and it exonerated the architecture. The real culprit was a mis-filled timestamp that made the server throttle itself to one packet per 100ms.</description><pubDate>Thu, 06 Aug 2026 00:00:00 GMT</pubDate></item><item><title>How Long Did You Actually Type? — Making Practice Time Deterministic, Tamper-Resistant, and Reproducible</title><link>https://mahui.me/en/blog/keydo-deterministic-timing/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-deterministic-timing/</guid><description>A KeyDo postmortem: practice time was Date.now()&apos;s end - start, so AFK, tab-switching, and wall-clock rollback all counted. Changed to accumulate &apos;accepted gaps between keystrokes,&apos; dropping any gap over 60 seconds whole, layered with IME guards, a monotonic clock, and idempotent settlement so the number is both accurate and unfarmable.</description><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Playable Offline, No Lost Scores Online — an Idempotent Outbox and One Worker Boundary Pipeline</title><link>https://mahui.me/en/blog/keydo-offline-sync/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-offline-sync/</guid><description>A KeyDo postmortem: score reporting was fire-and-forget, lost when offline or on failure; the public API had no size limit, origin check, or rate limiting. This time: PWA offline, a dual-side-idempotent score-sync outbox, and a Worker request-boundary pipeline where &apos;the order is the layers of protection.&apos;</description><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Sync the Source of Truth, Rebuild the Rest on Each Stack — Cross-Stack AI Conversation Sync</title><link>https://mahui.me/en/blog/shellby-derived-sync-transcript/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-derived-sync-transcript/</guid><description>A Shellby postmortem: syncing AI conversation history between Apple and Flutter stacks. A conversation has both raw messages and a step transcript, but only the raw messages should sync — the steps are derived, rebuilt on each stack from the messages. Plus a &apos;HarmonyOS receives nothing&apos; version-field pit.</description><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate></item><item><title>A Command Snippet Library: Changing &apos;Tap to Execute&apos; to &apos;View Before Executing&apos;</title><link>https://mahui.me/en/blog/shellby-snippet-library/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-snippet-library/</guid><description>A Shellby postmortem: a command tool bar at the top of the terminal, tap and it sends. After shipping, that interaction was judged unsafe and refactored into a &apos;view before executing&apos; snippet library. Plus the pits of building this across six platforms — mobile terminals have no menu-injection hook, macOS&apos;s self-drawn context menu gets grayed out by the system, and you can&apos;t blindly inject into a full-screen program.</description><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate></item><item><title>No Server-Side Merge, No Account, and the Save Still Converges Across Devices — G-Counters plus CAS</title><link>https://mahui.me/en/blog/keydo-cas-cloud-save/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-cas-cloud-save/</guid><description>A KeyDo postmortem: anonymous, no login, multiple devices practicing offline — how do you keep the save eventually consistent without lost updates? The answer is splitting responsibility: the Worker does only versioned atomic access (CAS), and the frontend does a deterministic merge that is commutative, associative, and idempotent.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate></item><item><title>A Leaderboard That Shows Your City Without Knowing Where You Are — Anonymous Region Labels</title><link>https://mahui.me/en/blog/keydo-city-rankings/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-city-rankings/</guid><description>A KeyDo postmortem: adding city-level region labels to an anonymous leaderboard, on the condition of collecting no precise location, no third-party IP geolocation, and never storing a byte of IP in the business DB. Read only Cloudflare&apos;s edge-injected country/province/city fields, degrade level by level, gate the format with a DB CHECK constraint, plus let only a complete run enter the ranking and keep ranks stable via three-segment COUNT.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate></item><item><title>If You Can&apos;t Parse the Command Name, Don&apos;t Allow It — Command Parsing Is the AI Agent&apos;s Security Boundary</title><link>https://mahui.me/en/blog/shellby-command-parsing-security/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-command-parsing-security/</guid><description>A Shellby postmortem: the AI Agent grades commands read-only / mutating / destructive before executing, and the verdict rests entirely on parsing the real command name from the line. And sudo options, su elevation, env-assignment prefixes, a pipe inside quotes — each can fool a naive parser. Why the parser itself is the security boundary, and why the right default when it can&apos;t parse is &apos;deny&apos;.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate></item><item><title>One Sync Protocol, Two Tech Stacks — And a Contract zlib Forced Me to Change</title><link>https://mahui.me/en/blog/shellby-cross-stack-sync/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-cross-stack-sync/</guid><description>A Shellby postmortem: a six-platform SSH client needs cross-ecosystem sync (Apple is SwiftData, Flutter is drift), over the user&apos;s own third-party storage, no server, end-to-end encrypted. The hard part is making &apos;the same data consistent after sync on both stacks&apos; — and mid-implementation, one zlib detail overturned the &apos;byte-identical ciphertext&apos; contract.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate></item><item><title>A Pairing Code You Can Freely Photograph — Because the Passphrase Isn&apos;t in It</title><link>https://mahui.me/en/blog/shellby-qr-pairing/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-qr-pairing/</guid><description>A Shellby postmortem: a new device joining cross-stack sync has to type a pile of sensitive config. Encode it into a passphrase-encrypted pairing code, render it as a QR, scan once. The key design is that the passphrase isn&apos;t in the code — it&apos;s only the decryption key — so the code is useless when photographed; plus HarmonyOS white-screen QR and desktop pick-image decode.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Making Alibaba OSS Actually Behave Like S3 — &apos;S3-Compatible&apos; Is Not a Contract</title><link>https://mahui.me/en/blog/shellby-s3-compatible-oss/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-s3-compatible-oss/</guid><description>A Shellby postmortem: sync supports bring-your-own S3 storage, and one user filling in Alibaba OSS made sync fail. Tracing a 403 with an empty body led to four pits — addressing, conditional writes, connection resets — each a facet of &apos;S3-compatible ≠ S3&apos;.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Deleted and Back Again — An Object That Writes Itself Into the Database</title><link>https://mahui.me/en/blog/shellby-delete-resurrection/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-delete-resurrection/</guid><description>A Shellby postmortem: adding per-conversation deletion to AI chat history, and it comes right back after deletion. The culprit is the session&apos;s own auto-persistence callback — abort triggers one last state change that upserts the deleted record back, then CloudKit resurrects it across every device. The fix is one line, but its position is everything.</description><pubDate>Mon, 13 Jul 2026 00:00:00 GMT</pubDate></item><item><title>&apos;Try Restart&apos; Is Not &apos;Undo&apos; — Shell-Free Restart and a Crash-Recovery Ledger</title><link>https://mahui.me/en/blog/pier-safe-restart-recovery/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-safe-restart-recovery/</guid><description>A Pier 3.0 postmortem: after stopping a dev server, restarting it looks simple and is deeply trapped. Why the button is &apos;Try restart&apos; and not &apos;Undo,&apos; why restart must never go through a shell, and a recovery design that tolerates both its own crash and a corrupt ledger.</description><pubDate>Sun, 12 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Prefer a Miss Over a Wrong Kill — A Fail-Closed Cleanup Grading Engine</title><link>https://mahui.me/en/blog/pier-fail-closed-classification/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-fail-closed-classification/</guid><description>A Pier 3.0 postmortem: one-click cleanup only includes &apos;green&apos; items, and for a process to go green it has to clear a whole row of hard guards at once. On designing deterministic rules, why green defaults to &apos;not qualified,&apos; and a list of &apos;safety invariants&apos; that pin these rules as release gates.</description><pubDate>Sat, 11 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Stopping a Process That Keeps Changing — TOCTOU and Continuous Checkpoints in a Cleanup Action</title><link>https://mahui.me/en/blog/pier-safe-stop-toctou/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-safe-stop-toctou/</guid><description>A Pier 3.0 postmortem: the green verdict uses a snapshot from the moment of diagnosis, but between the user clicking cleanup and the signal firing, the process can grow new connections and new children. On shrinking that check-to-use window to the minimum with continuous checkpoints, a 2-second evidence deadline, and re-verifying the kernel start time right before the signal.</description><pubDate>Sat, 11 Jul 2026 00:00:00 GMT</pubDate></item><item><title>From Resource Monitor to Cleanup Assistant — When Safety Is the Product Itself</title><link>https://mahui.me/en/blog/pier-cleanup-safety-first/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-cleanup-safety-first/</guid><description>A Pier 3.0 postmortem: repositioning from &apos;developer resource monitor&apos; to &apos;local cleanup assistant for after AI coding.&apos; The basic unit shifts from port/process to project/session, and the core question becomes &apos;what did my AI tools leave running, and what can I safely stop.&apos; And why, for a cleanup tool aimed at non-expert developers, safety isn&apos;t a selling point — it&apos;s the product itself.</description><pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate></item><item><title>What Actually Belongs in a Bilingual Site&apos;s &lt;head&gt; — canonical, hreflang, and Structured Data</title><link>https://mahui.me/en/blog/astro-bilingual-seo-head/</link><guid isPermaLink="true">https://mahui.me/en/blog/astro-bilingual-seo-head/</guid><description>Adding a full SEO head to a bilingual Astro site. The interesting parts: hreflang group correctness (self-reference + reciprocal + x-default — pointing only at the other language is wrong), not emitting hreflang for posts with no translation, and which JSON-LD an article page versus the home page should emit.</description><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate></item><item><title>No OG-Image Service — Making Share Cards With Headless Chrome</title><link>https://mahui.me/en/blog/og-image-headless-chrome/</link><guid isPermaLink="true">https://mahui.me/en/blog/og-image-headless-chrome/</guid><description>You need a 1200×630 social share image. No design tool, no third-party OG service, no dynamic generation — write an HTML file with the site&apos;s own CSS variables and screenshot it with headless Chrome. And why a static default first, deferring per-article generation until it&apos;s actually needed.</description><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate></item><item><title>One Key, Five Macs — Keeping Activation Seats From Quietly Leaking Away</title><link>https://mahui.me/en/blog/pier-device-seats/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-device-seats/</guid><description>A Pier postmortem: a perpetual license caps activation at 5 devices, but if &apos;deactivate&apos; only clears local state, the server-side seat stays occupied forever, and after a few machine changes users hit the wall and email you to free them by hand. The fix: deactivation must release the seat server-side, and on failure would rather not release than quietly leak.</description><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate></item><item><title>SEO for a One-Person Constellation of Sites — Same-Origin Sitemaps, Domain Properties, and a 308 Trap</title><link>https://mahui.me/en/blog/subdomain-seo-architecture/</link><guid isPermaLink="true">https://mahui.me/en/blog/subdomain-seo-architecture/</guid><description>A main site plus six product subdomains, and the sitemap only covers the main site. Why one sitemap can&apos;t cover the constellation, whether to use a Domain property or verify each origin, and a Cloudflare Pages .html trap that turns every sitemap entry into a redirect.</description><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Notarized, Approved, and Dead on Double-Click</title><link>https://mahui.me/en/blog/pier-notarization-blind-spot/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-notarization-blind-spot/</guid><description>A Pier 2.0.0 postmortem: one restricted entitlement, added to remove a keychain prompt, sailed through notarization — and got the app SIGKILLed on every user&apos;s Mac. Rolled back in 2.0.1 the same night.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Pier 2.0 Goes Paid — and You Pick the Price</title><link>https://mahui.me/en/blog/pier-pay-what-you-want/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-pay-what-you-want/</guid><description>The free promo is over. Pier is now pay what you want: from $0.99, suggested $6.99, one-time purchase. On where to draw the paywall line, why PWYW, and what existing users get.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Two Kinds of Garbled Terminal — vim Paste Wedging and Remote Chinese Turning to Question Marks</title><link>https://mahui.me/en/blog/shellby-terminal-encoding/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-terminal-encoding/</guid><description>A Shellby postmortem: one bug garbles every keystroke after pasting into vim, rooted in concurrent writes to the SSH channel losing their order; the other turns Chinese into escape gibberish on remote macOS, rooted in the sshd session having no LANG. Both go wrong on the path bytes travel to the remote — but one is order, the other is environment.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate></item><item><title>KeyDo: A CRT Arcade-Style Typing Dojo</title><link>https://mahui.me/en/blog/keydo-launch/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-launch/</guid><description>A retro arcade-style web typing game: four training modes, martial-arts belt ranks, a global leaderboard, purely frontend and playable offline. How it&apos;s built, and the trade-offs behind a pile of technical choices.</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Three Subscriptions, One Endpoint: Open-Sourcing LLM-Bridge</title><link>https://mahui.me/en/blog/llm-bridge-launch/</link><guid isPermaLink="true">https://mahui.me/en/blog/llm-bridge-launch/</guid><description>Putting Claude Code, Codex, and Antigravity behind one OpenAI-compatible API. What it does — and what it deliberately doesn&apos;t.</description><pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Make the AIs Hold a Meeting: Open-Sourcing Open Council</title><link>https://mahui.me/en/blog/open-council-launch/</link><guid isPermaLink="true">https://mahui.me/en/blog/open-council-launch/</guid><description>A local multi-model debate orchestrator: several AI models debate one question, peer-review each other, and reach a measured consensus for an answer more reliable than any single model. How it works, and where it fits.</description><pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Why Trust a Client-Computed Score? A Leaderboard&apos;s Server-Side Floor</title><link>https://mahui.me/en/blog/keydo-leaderboard-anti-cheat/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-leaderboard-anti-cheat/</guid><description>A KeyDo postmortem: WPM is computed in the browser, so how does a global leaderboard stop someone from just POSTing a 9999? Cap validation, one row per person per mode, best-score logic in SQL.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Found but Unreachable: One Message, Two Identities</title><link>https://mahui.me/en/blog/vibetrail-search-anchor/</link><guid isPermaLink="true">https://mahui.me/en/blog/vibetrail-search-anchor/</guid><description>Search anchors on physical lines, the timeline renders logical messages--and the fold operation dropped the mapping between the two.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Opening a Door in Someone Else&apos;s Terminal: Four Downgrades of VibeTrail&apos;s Resume</title><link>https://mahui.me/en/blog/vibetrail-resume-degrade/</link><guid isPermaLink="true">https://mahui.me/en/blog/vibetrail-resume-degrade/</guid><description>AppleScript crashes, TCC silent denials, a dead-click investigation — the permission reefs of macOS automation, and the path that was in plain sight all along.</description><pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Building This Site: How It&apos;s Put Together</title><link>https://mahui.me/en/blog/hello-world/</link><guid isPermaLink="true">https://mahui.me/en/blog/hello-world/</guid><description>Tech choices, directory structure, and deployment, all in one post.</description><pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Running an Imperative Game Loop Inside React: Word Rain</title><link>https://mahui.me/en/blog/keydo-falling-words-loop/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-falling-words-loop/</guid><description>A KeyDo postmortem: words fall from the sky, requestAnimationFrame advances the physics each frame. Calling setState every frame in React grinds to a halt — state goes in a ref, rendering is triggered by an empty tick.</description><pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Letting an AI Type Commands on Your Server: Shellby&apos;s Three-Tier Gate and Five Guardrails</title><link>https://mahui.me/en/blog/shellby-ai-agent-safety/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-ai-agent-safety/</guid><description>A Shellby postmortem: the hard part of an AI agent that runs commands isn&apos;t getting it to work — it&apos;s keeping it from wrecking things.</description><pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Keeping an AI Agent&apos;s Context From Ballooning With Step Count — Cache Breakpoints, History Folding, and Streaming</title><link>https://mahui.me/en/blog/shellby-agent-context/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-agent-context/</guid><description>A Shellby postmortem: an agent that autonomously runs commands resends its whole accumulated history every step, so tokens and time-to-first-token climb linearly with step count. Three fixes — Anthropic prompt caching breakpoints, monotonically-frozen history folding, and SSE streaming — orthogonal and stackable.</description><pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Credentials Can&apos;t Touch the Cloud: Shellby&apos;s Dual-Track Sync</title><link>https://mahui.me/en/blog/shellby-dual-track-sync/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-dual-track-sync/</guid><description>A Shellby postmortem: syncing an SSH client across devices, the hardest constraint is that passwords and private keys must never reach the cloud in plaintext.</description><pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Linking ripgrep into Your App: Index-Free Full-Text Search in VibeTrail</title><link>https://mahui.me/en/blog/vibetrail-ripgrep/</link><guid isPermaLink="true">https://mahui.me/en/blog/vibetrail-ripgrep/</guid><description>No database, no index--just compile the grep engine straight into a Rust app.</description><pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Typing Sound Effects Without a Single Audio File</title><link>https://mahui.me/en/blog/keydo-web-audio-sfx/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-web-audio-sfx/</guid><description>A KeyDo postmortem: hit, miss, kill, level-up, game-over — a full set of 8-bit sound effects, zero audio assets, all synthesized live by Web Audio oscillators.</description><pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate></item><item><title>No Agent, One Command for All Server Metrics: Shellby&apos;s Monitoring</title><link>https://mahui.me/en/blog/shellby-server-monitoring/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-server-monitoring/</guid><description>A Shellby postmortem: no collector agent, pulling CPU/memory/disk/load over plain SSH, packing five metric classes into one command.</description><pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate></item><item><title>There&apos;s Only One Kind of Tunnel: Shellby&apos;s Port Forwarding</title><link>https://mahui.me/en/blog/shellby-port-forwarding/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-port-forwarding/</guid><description>A Shellby postmortem: local forward, remote forward, dynamic SOCKS, multi-hop jump — four forms, one unified tunnel model, plus a NIO trap.</description><pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Truth in a Ref, Snapshot for React: A Per-Character Typing Engine</title><link>https://mahui.me/en/blog/keydo-typing-engine/</link><guid isPermaLink="true">https://mahui.me/en/blog/keydo-typing-engine/</guid><description>A KeyDo postmortem: typing is a high-frequency keyboard event, and shoving it straight into React state gets you burned by StrictMode and async setState. Keep the truth in a ref, commit a snapshot to render.</description><pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate></item><item><title>A Dead Connection That Looks Alive: Shellby&apos;s Liveness Token</title><link>https://mahui.me/en/blog/shellby-liveness-token/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-liveness-token/</guid><description>A Shellby postmortem: when an SSH connection silently hangs, &apos;the command returned&apos; says nothing about whether the connection is still alive.</description><pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate></item><item><title>What 4 Million Files Taught Me: A Diskly Scanner Performance Postmortem</title><link>https://mahui.me/en/blog/diskly-four-million-files/</link><guid isPermaLink="true">https://mahui.me/en/blog/diskly-four-million-files/</guid><description>75s to 2.2s, 6.46TB of phantom usage, one change saving 150MB, more threads running slower — every intuition has to survive the numbers.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate></item><item><title>One SwiftUI Codebase, Three Different Hands: Shellby&apos;s Multi-Session Adaptation</title><link>https://mahui.me/en/blog/shellby-cross-platform/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-cross-platform/</guid><description>A Shellby postmortem: command broadcast, split view, ⌘W — one codebase serving the Mac&apos;s keyboard, the iPad&apos;s multitasking, the iPhone&apos;s one-handed use.</description><pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Making macOS Stop Asking for Keychain Access on Every Connect: A Keychain Choice</title><link>https://mahui.me/en/blog/shellby-keychain-popups/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-keychain-popups/</guid><description>A Shellby postmortem: the same password-storing code popped a permission dialog on every connect on macOS — switching keychains fixed it.</description><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate></item><item><title>One reasoning_effort, Translated for Three Backends</title><link>https://mahui.me/en/blog/llm-bridge-reasoning-effort/</link><guid isPermaLink="true">https://mahui.me/en/blog/llm-bridge-reasoning-effort/</guid><description>An LLM-Bridge postmortem: OpenAI gives one unified reasoning_effort field, but the three backends accept depth in completely different ways — an SDK option, a config override, encoded in the model name.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Skipping the App Store: Signing, Notarization, and Self-Updating for macOS Apps</title><link>https://mahui.me/en/blog/mac-app-distribution/</link><guid isPermaLink="true">https://mahui.me/en/blog/mac-app-distribution/</guid><description>How Pier and Diskly ship independently: Developer ID, notarytool, Sparkle, and a Homebrew tap.</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Cutting OAuth, Keeping Two Protocols: The Trade-offs of an Access Convergence</title><link>https://mahui.me/en/blog/open-council-api-convergence/</link><guid isPermaLink="true">https://mahui.me/en/blog/open-council-api-convergence/</guid><description>An Open Council postmortem: from a wide access matrix of CLI / OAuth / subscription / middleware down to just anthropic + openai standard APIs. Why subtract, and how to subtract honestly.</description><pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate></item><item><title>The Life and Death of a Feature: Four Rewrites of Pier&apos;s Per-Process Network Monitor</title><link>https://mahui.me/en/blog/pier-network-monitor-postmortem/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-network-monitor-postmortem/</guid><description>A Pier postmortem: block buffering, the CRLF grapheme, a spin-looping nettop, a private framework that crashes on sight — and how to do the math on deleting a feature.</description><pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Group Cards Flickering During a Scan — Three Layers of Nondeterminism and One Epoch Bin</title><link>https://mahui.me/en/blog/flick-group-stability/</link><guid isPermaLink="true">https://mahui.me/en/blog/flick-group-stability/</guid><description>A Flick postmortem: analysis streams in results while the Groups screen has cards appearing, vanishing, and reappearing in a loop. The culprit was three stacked layers of nondeterminism plus a window anchor that drifts over time. The fix: make the same set of photos compute a bit-identical grouping on every rebuild.</description><pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate></item><item><title>A License System With Zero Backend</title><link>https://mahui.me/en/blog/pier-no-backend-license/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-no-backend-license/</guid><description>A Pier postmortem: one-time purchase, 5 devices, refund revocation — the whole licensing system is under 200 lines of client-side Swift with no server and no embedded secrets. The interesting parts: where state lives, and who to believe when validation fails.</description><pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Two SSH Stacks for One Login: Shellby&apos;s Backend Choice</title><link>https://mahui.me/en/blog/shellby-two-ssh-backends/</link><guid isPermaLink="true">https://mahui.me/en/blog/shellby-two-ssh-backends/</guid><description>A Shellby postmortem: iOS has no system SSH library. Pure Swift or C — I walked both roads.</description><pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Three App Freezes, One Culprit: Don&apos;t Block Swift&apos;s Cooperative Thread Pool</title><link>https://mahui.me/en/blog/flick-cooperative-pool/</link><guid isPermaLink="true">https://mahui.me/en/blog/flick-cooperative-pool/</guid><description>A Flick postmortem: 4 &quot;compute start&quot;, 0 &quot;compute done&quot; — the contract of cooperative scheduling, and three ways to break it.</description><pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate></item><item><title>A Red Dot in the Menu Bar — Passive Monitoring for LaunchAgents</title><link>https://mahui.me/en/blog/pier-launchagent-badge/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-launchagent-badge/</guid><description>A Pier postmortem: LaunchAgents fail silently, always. Treat plist files as the source of truth, backfill state from launchctl, light a red dot on a nonzero exit code, refresh every 5 minutes — monitoring earns its keep precisely when you&apos;re not looking.</description><pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Three Subprocess Invariants: Don&apos;t Turn a CLI Into a Zombie</title><link>https://mahui.me/en/blog/llm-bridge-subprocess-invariants/</link><guid isPermaLink="true">https://mahui.me/en/blog/llm-bridge-subprocess-invariants/</guid><description>An LLM-Bridge postmortem: a gateway&apos;s job is really &apos;safely shelling out someone else&apos;s CLI.&apos; stdin, stderr, finally-kill — three invariants, and dropping one leaks processes or deadlocks.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate></item><item><title>lsof Says node. It Doesn&apos;t Say Which Project.</title><link>https://mahui.me/en/blog/pier-port-fingerprint/</link><guid isPermaLink="true">https://mahui.me/en/blog/pier-port-fingerprint/</guid><description>A Pier postmortem: the ports list was all node, python, com.docker.backend. Fingerprinting 40 dev services from full argv, pulling working directories via proc_pidinfo, and resolving container names through docker ps — three signals that answer what a port actually is.</description><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Not a Single Photo Uploaded: Flick&apos;s On-Device Similar Photo Detection</title><link>https://mahui.me/en/blog/on-device-photo-similarity/</link><guid isPermaLink="true">https://mahui.me/en/blog/on-device-photo-similarity/</guid><description>Analyzing the photo library entirely on device with Vision feature prints, Laplacian variance, and a SQLite cache.</description><pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Database Constraints as Tripwires: Three Bugs Caught Red-Handed</title><link>https://mahui.me/en/blog/yuwei-constraints-as-tripwires/</link><guid isPermaLink="true">https://mahui.me/en/blog/yuwei-constraints-as-tripwires/</guid><description>A Yuwei postmortem: 23514, 23P01, and a batch of ghost seats--constraints aren&apos;t just defenses, they&apos;re the most honest alarms.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate></item><item><title>The Debate Is Too Long to Feed In: A Compression Before Synthesis</title><link>https://mahui.me/en/blog/open-council-presynthesis-compression/</link><guid isPermaLink="true">https://mahui.me/en/blog/open-council-presynthesis-compression/</guid><description>An Open Council postmortem: multi-model × multi-round debate produces text that blows past the chairman&apos;s context. Preserve the core, truncate the periphery, never touch code blocks — plus an honest &apos;ship determinism first, leave LLM summarization for v2&apos; trade-off.</description><pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate></item><item><title>Three Layers of Defense Against Overselling: Concurrency Control in a Seat Allocation System</title><link>https://mahui.me/en/blog/prevent-overselling/</link><guid isPermaLink="true">https://mahui.me/en/blog/prevent-overselling/</guid><description>Yuwei&apos;s inventory safety design: row locks, optimistic locking, and database constraints -- all three are essential.</description><pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate></item><item><title>One OpenAI Endpoint, Three Very Different Processes Behind It</title><link>https://mahui.me/en/blog/llm-bridge-provider-adapters/</link><guid isPermaLink="true">https://mahui.me/en/blog/llm-bridge-provider-adapters/</guid><description>An LLM-Bridge postmortem: outward it&apos;s one tidy OpenAI format, inward it&apos;s a three-headed beast — SDK message stream, line-delimited JSON, plain text. Normalization all happens in the adapter layer.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate></item><item><title>Every Chinese Keyword Was Dead Code: Routing, Roles, and a Regex Trap</title><link>https://mahui.me/en/blog/open-council-routing-roles/</link><guid isPermaLink="true">https://mahui.me/en/blog/open-council-routing-roles/</guid><description>An Open Council postmortem: how to decide how many rounds a question needs, who to send in, and what role each model plays. Starting from a classic regex trap where `\b` killed every Chinese routing keyword.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>Extracting Tokens Is a Dead End: A Personal AI Gateway&apos;s Compliance Line</title><link>https://mahui.me/en/blog/llm-bridge-no-token-extraction/</link><guid isPermaLink="true">https://mahui.me/en/blog/llm-bridge-no-token-extraction/</guid><description>An LLM-Bridge postmortem: pulling OAuth tokens out of the CLI to hit the backend directly is the shortest path — and the first to die. Why it all fell back to official harnesses.</description><pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate></item><item><title>How a Consensus Score of 0.82 Is Computed: Quantifying Multi-Model Peer Review</title><link>https://mahui.me/en/blog/open-council-consensus/</link><guid isPermaLink="true">https://mahui.me/en/blog/open-council-consensus/</guid><description>An Open Council postmortem: several models score each other, and the scores become a 0-1 consensus. Half from score dispersion, half from ranking agreement — plus a bug that nearly made the debate never stop.</description><pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate></item></channel></rss>