Notes from building real systems.
Notes on production and operations systems, full-stack development, AI applications and web development, written from real project work in Cyberjaya rather than from tutorials.
Installing Drivers From Official Sources Only: Allow-Lists, Catalogue Hashes and Authenticode
How the BBTech Laptop Inspection Tool finds, downloads, installs and verifies a driver for a refurbished laptop without ever running an unsigned or unofficial package, and why its status vocabulary has no word for a success it did not confirm.
4 min read
Google Sign-In Without a Client Library: Server-Side PKCE and Grants Read on Every Request
RPOMS added Google sign-in through its Supabase project's provider with no client library in the browser bundle. Google proves who someone is and grants nothing; a Super Admin grants access per service and capability, and revocation takes effect on the next request.
3 min read
Sorting on the Column the Index Serves: 160 ms to Under 1 ms at 250,000 Routers
The RPOMS Registry page sorted on a text copy of the timestamp it selected, so the index on the real column was never used. Measured on an isolated 250,000-row copy, the fix is one ORDER BY and a read deadline that keeps the page honest when a query is slow anyway.
3 min read
One Live Record per Router per Session: Partial Unique Indexes Instead of Per-Scan Locks
A barcode scanner can fire the same serial twice in a few milliseconds, and two workstations can scan the same router at once. RPOMS's Refining workstation catches both with one partial unique index, and decides whether to print from what the save recorded.
3 min read
Refusing Runtime DDL on Production: Schema Stamps, a Gate, and Schema-Only Migrations
RPOMS used to build its own tables on a cold start. On a shared production database that is a race and a risk, so the application now refuses to alter production schema, and a release ships as ordered, schema-only migration files with rollback blocks.
4 min read
Sending Labels From One Web App to Another Tab: BroadcastChannel, COOP and window.opener
How RPOMS sends a router label to the Print Engine tab that holds the Bluetooth printer: why a hidden iframe could never work across sites, why one header had to be removed from one path, and what a lost answer must say.
5 min read
Offline Receipt OCR on Android: On-Device Google ML Kit Text Recognition with Jetpack Compose
How SpenDrop's native Android engine parses payment receipts and merchant summaries offline on device using Google ML Kit, Jetpack Compose, and Room SQLite without third-party cloud APIs.
3 min read
Extracting Hardware Serial Numbers Over LAN: UPnP, CSTECGI, and SNMP Across Telco Router Vendors
How the BBTech WiFi Router Diagnostic Tool extracts authentic factory serial numbers directly over an Ethernet connection without an external barcode scanner, handling Kaon, Huawei, and TP-Link routers.
2 min read
Excluding a Damaged Display Zone via the Windows AppBar API (SHAppBarMessage)
How to salvage a laptop or monitor with a broken, flickering line of pixels by registering a hidden Windows Application Desktop Toolbar to shrink the system work area at runtime.
2 min read
Why Hardware Identity Matters: How Generated Stock Prefixes Concealed Duplicate Refurbishments in ITMS
An architectural audit of enterprise refurbishment systems: why generating sequential stock prefixes like 'HP1' creates phantom inventory, and how binding to immutable SMBIOS hardware serials provides a tamper-proof audit trail.
3 min read
Sub-Millisecond Saves: Designing a Local-First Outbox and Sync Engine in SwiftData
How SpenDrop achieved an on-device save latency of 0.240 ms in SwiftData while synchronising in the background with a remote Supabase PostgreSQL backend using the persistent outbox pattern.
3 min read
Connection-Pool Exhaustion Under Serverless Scale: Investigating Supavisor and Vercel in Production
A postmortem of production incident INC-2026-10-11-01: why Vercel serverless functions saturated Supavisor transaction pool connections during a warehouse shift change while CPU was under 5%, and how we recovered with zero data loss.
4 min read
The Wrong Supabase Key Doesn't Fail. It Returns 200 and Zero Rows.
A publishable Supabase key in a server slot meant for a secret one: every read succeeded with no rows, the dashboard showed confident zeros, and the health check said all was well.
3 min read
Writing a Page an AI Answer Engine Can Quote Correctly: GEO on a Locked Design
Implementing a GEO requirement on a locked design: test the questions visitors ask, make answers self-contained, attribute claims, and escalate contradictions.
6 min read
The Service Worker Was Active and the Cache Was Empty: A Workbox Duplicate-Entry Bug
An offline PWA that registered its service worker, cached nothing, and failed silently. The cause was one file listed twice in the precache manifest by vite-plugin-pwa's includeAssets.
3 min read
The Plan Said RAG. The Papers Said Send the Whole Thing.
ResearchForge was planned as a retrieval pipeline. Measured papers fit whole in the context window, so it sends the full text and only chunks a genuinely oversized document.
4 min read
Production Broke and Nothing Had Changed: A Paused Free-Tier Database
Admin sign-in returned 503 and nothing was being saved, with no deploy and no config change. One credential-free request that tells a database outage from wrong credentials.
3 min read
What Moving Self-Hosted Inference to a Server Will and Won't Fix
A migration plan, not a result: why moving RPOMS AI's model from a laptop to a dedicated server would improve availability but not accuracy, and the gates it must pass first.
4 min read
Versioning an Operational Spreadsheet Like Software
A stock tracker's Change History sheet as a release log: eight iterations before go-live, a 1.x line after, user-requested versions, and what an in-file changelog cannot give you.
5 min read
Vercel or a Self-Hosted Ubuntu Box? How I Chose Hosting App by App
Eight deployed apps, three hosting shapes: Vercel for pages and serverless APIs, a self-hosted Ubuntu box for a stateful API, and a hybrid for a model Vercel can't run.
6 min read
Why My VBA Maps Columns by Header Name, Not Column Number
One workbook held both approaches: event macros with hard-coded column numbers that drifted as columns were added, and an update macro that finds columns by header text. The difference.
5 min read
Two Ways I Use Supabase Row Level Security: Deny-All Behind a Server, or Per-Owner Policies
Across five projects, RLS does one of two jobs: close the Data API while a trusted server does the work, or let Postgres enforce who owns each row. When to pick which.
5 min read
Theme-Specific Logos Without a Flash: Let CSS Pick, Not useTheme()
A logo with an opaque white background became a white block on the dark theme. Swapping it in CSS on data-theme, not with useTheme(), fixed it on the first frame.
3 min read
Testing Without Paid APIs or Hardware, and the Places Fakes Lied
Fake LLM providers, a simulated label printer and an in-process Postgres keep my test suites free and offline. What each proved, and three places a fake fell short.
5 min read
Proving a PWA Boots Offline: A Headless Chrome Test Over the DevTools Protocol
An automated test that installs a PWA under production headers, cuts the network at the protocol level, reloads, and checks the app still works, with no test framework beyond a WebSocket.
5 min read
Testing Real PostgreSQL SQL in Vitest With PGlite and the Real Driver
No Postgres server, no Docker, and production off limits. PGlite behind a loopback socket let the real postgres.js driver run real SQL in Vitest and in CI.
4 min read
The Test That Calls Every Next.js API Route With No Session
Five API reads in RPOMS typechecked and linted cleanly and were open to anyone. They were found by probing the built app, and now one test globs every route.ts and expects a refusal.
4 min read
Testing an LLM App Without Calling a Paid API
538 tests for an LLM application, none of which call a paid model. FastAPI dependency overrides, a fake provider, generated PDFs, opt-in live tests, and where fakes stop telling the truth.
4 min read
SwiftData Migrations That Can't Lose Data: VersionedSchema, Pre-Upgrade Copies and a Safe Mode
SwiftData's inferred migration silently dropped a table. How SpenDrop moved to VersionedSchemas, pre-upgrade copies and a safe mode that never deletes.
6 min read
Why Supabase Google Sign-In Landed on localhost With a Token in the URL
Google sign-in on a custom domain returned users to localhost:3000 with an access token in the address bar. Two separate causes, the Site URL fallback and the implicit flow, and the fixes.
4 min read
Google Sign-In on iOS Through Supabase, With PKCE and No Google SDK
Google sign-in on iOS through Supabase with ASWebAuthenticationSession and PKCE, no Google SDK or client secret, plus an identity bug RLS would reject.
5 min read
Backing Up a Database on a Plan With No Backups, and a One-Way Staging Refresh
The procedure and scripts I designed for a production Supabase database on the Free plan: a read-only JSON export, a restore that only targets staging, and an anonymising refresh.
4 min read
Structured Data for a Local Recycling Business: Organization, RecyclingCenter and an FAQPage That Matches the Page
Turning a client's schema requirements into JSON-LD for a Malaysian e-waste service: Organization plus RecyclingCenter, and an FAQPage that mirrors the visible FAQ.
5 min read
Storing Uploaded Photos in Postgres bytea, Served Only to Admins
Why RPOMS AI keeps inspection photos in a PostgreSQL bytea column instead of a bucket, how they are served only through an admin-guarded route, and when this stops being a good idea.
3 min read
Stopping Vercel Preview Deployments From Touching the Production Database
Every preview of RPOMS carried the production connection string. A guard in code now refuses the pairing, answers 503 with the reason, and can be checked from outside.
5 min read
A Stock Request Portal With a State Machine and an Event Log
The lifecycle behind the RPOMS Ecommerce portal: a transition table, terminal states, a void that reverses, version-checked edits and an event per step.
5 min read
Out-of-Order Responses on Filter Changes: Only the Newest Request May Write
Filters that apply on change put several fetches in flight, and the slow broad one often lands last. A ticket per request in a React ref keeps the screen answering the current question.
3 min read
Splitting RM 10 Three Ways: Integer Sen and the Largest-Remainder Method
Floating-point money drifts and naive splits lose a sen. Integer sen at one boundary and the largest-remainder method, so shares always add up.
4 min read
Sorting Apple Vision Text Into Lines Without Crashing Swift's Sort
VNRecognizeTextRequest returns boxes, not lines. Sorting them top to bottom and left to right with a comparator Swift's sort can trust.
3 min read
Sharing One Supabase Project Between Two Apps Safely
A second app inside an existing Supabase project: prefixed tables, RLS on with no policies, revoked grants, server-only access and an admin check for a shared user list.
4 min read
Shadow-Testing a Replacement Model With Next.js after(), Without the User Waiting
How RPOMS AI is built to run a candidate model on real requests after the response is sent, record whether it agreed, and never show it. The lifecycle around it, and its limits.
4 min read
Detecting a Device Model From Its Serial Number With Prefix Rules
How RPOMS and its print engine tell a router's model from its serial: prefix and length rules stored as data, longest prefix first, unknowns flagged.
5 min read
Telling the Sender Bank From the Recipient on a Malaysian Transfer Receipt
A CIMB to Maybank transfer receipt names both banks. Reading the receipt's structure to attribute the payment to the bank that actually sent it.
5 min read
Self-Hosting a Node and PostgreSQL Service With nginx and systemd on Ubuntu
The deployment behind print.rukon.dev: one origin for a PWA and API, a hardened systemd unit, atomic releases, nginx header gotchas and a backup design.
7 min read
Baseline Security Headers in Next.js, and Why the CSP Waited
Adding five response headers to a Next.js site that served none, why the Content-Security-Policy was deliberately deferred, and a nonce-free CSP on another app.
4 min read
Security Headers for a Web Bluetooth PWA, Pinned by a Test
The CSP, Permissions-Policy and COOP for a PWA that drives a Bluetooth printer, why each source is there, a separate policy for the API, and a test that keeps nginx and the app in step.
5 min read
When the Code Ships Before the Migration: Degrading One Schema Level at a Time
Selecting a column that a pending migration adds made PostgREST fail every library read. The fix degrades one migration tier at a time, so deploy order stops mattering.
4 min read
Schema Setup on Cold Start: One Statement, One Winner, and a Building Sentinel
Serverless instances that create tables on first use race each other and hold exclusive locks. How RPOMS claims the schema build with one SQL statement, and the bug in the first version.
4 min read
Running Qwen2.5-VL 7B on an Intel Iris Xe With Ollama and Vulkan: 119 s to 41 s
Setting up a 7B vision model on a Windows laptop with no discrete GPU: enabling the Iris Xe under Ollama, where the time actually goes, and what the GPU profile showed.
5 min read
Adding Arabic (RTL) to a Next.js Dashboard Without Translating the Whole App
Publishing the RPOMS dashboard in Arabic: a typed dictionary that fails the build, dir on one wrapper not <html>, an LTR chart, and Latin digits.
4 min read
Defaults Are Claims: Removing Placeholder Numbers That Looked Like Measurements
A 75% tank, a 10 km/L economy, a hardcoded user id: defaults in DriveKeep that rendered as real data, what replaced them, and the same rule in SpenDrop.
5 min read
Working Through a 44-Finding Code Audit, Six of Them P0, Without Losing Data
How I worked through a forensic audit of a live operations system: the four root causes behind 44 findings, a second pass that caught my own regressions, and what I chose not to change.
5 min read
Rate Limiting a Public Endpoint on Serverless, Keyed by an HMAC of the Address
Two layers for an anonymous upload endpoint on Vercel: an honest in-memory burst limiter, and a durable hourly count in Postgres keyed by an HMAC so the address is never stored.
4 min read
A Qualification Gate: Computing What a Model Is Allowed to Decide From Its Own Results
Instead of trusting a model or not, RPOMS AI computes per verdict and per criterion what the model has earned the right to say, from a reference run. The policy, the code and the trade-off.
4 min read
Promise.race Timeouts Don't Cancel Queries, and That Emptied My Connection Pool
A read deadline built on Promise.race walks away from a query that keeps running and keeps its pooled connection. How that fed on itself on Vercel, and the bounds that fixed it.
4 min read
A Production Write Lock That Fails Safe: Only Named Modules May Write
How RPOMS keeps finished modules from writing in production until sign-off: a middleware lock that is on by default in production and an allowlist matched by path segment.
4 min read
postgres.js Dropped the Microseconds From My timestamptz Parameter
A revision check refused every save because a bound timestamptz went through a JavaScript Date and lost its microseconds. One extra ::text cast fixed it.
3 min read
postgres.js Stored My JSON Arrays as Strings in a jsonb Column
Writing JSON.stringify(x)::jsonb through postgres.js encodes the value twice, so a jsonb column holds a string, not an array. The symptom, the sql.json() fix, and handling rows already stored.
3 min read
Bulk Inserts Past PostgreSQL's 65,535 Bind-Parameter Limit: unnest and Chunks
A multi-row VALUES insert with six parameters per row failed outright above 10,922 rows. Replacing it with unnest over array parameters, chunked, and why the import is not one transaction.
3 min read
Porting a Static Page to Next.js Without Visual Drift
Rebuilding a finished page in Next.js 16 and Tailwind 4 so it looks identical: the font fallback, image optimiser and class-conflict traps, and how drift was checked.
5 min read
Planning a University Project Like a Real One: Milestones, Risks and a Decision Log
The first commit of ResearchForge had no code: a plan with milestones, a risk register, a definition of done and numbered decisions. What that structure caught, and where the plan was wrong.
5 min read
Apple Pay Is Not a Bank: Payment Channels, Funding Accounts and Reconciling Double Records
One purchase can arrive as an Apple Pay tap and again as a bank record. Separating how you paid from where money came from, and merging the duplicate.
6 min read
One Pydantic Schema, Two LLM Vendors: Native Parsing vs JSON Mode
The same Pydantic models drive structured output from Anthropic's native parsing and Groq's JSON mode. Forbidding extra fields, catching truncation, and validating instead of repairing.
3 min read
Preview and Commit From the Same Plan: Stock Transfers You Can Reverse Exactly
In RPOMS one pure function plans a stock transfer: the preview shows it, the commit re-runs it, and a void inverts the frozen executed plan exactly.
5 min read
One SQL Function for a Whole Analytics Dashboard
Every figure on a small analytics dashboard, totals, a gap-free time series and ranked breakdowns, returned as one JSON document by one PostgreSQL function, in the owner's time zone.
4 min read
Deletes That Don't Come Back: Tombstones and Last-Write-Wins in Offline Sync
In offline sync, a deleted record comes back when another copy is pushed. DriveKeep's soft deletes, tombstone list and timestamp merge, and its clock problem.
4 min read
Reading an Odometer Photo: Preprocessing Variants, Tesseract.js and a Plausibility Check
DriveKeep's web app reads four sharp-preprocessed variants of an odometer photo with Tesseract.js and ranks every candidate against the previous reading.
4 min read
Two Windows Bugs in a Node Launcher: "C:\Program" and a PATH That Hadn't Updated
A one-command launcher for Ollama, a gateway and cloudflared broke on Windows twice: shell: true split the node path at its space, and a fresh install was not on the open terminal's PATH.
3 min read
Next.js Open Graph Metadata Replaces, It Doesn't Merge
A child route that sets its own openGraph object silently drops the layout's og:image and og:locale. Why Next.js does that, and the helper this site uses to stop it.
3 min read
An Error Boundary That Retried Into the Outage: Backoff With Jitter
A Next.js error.tsx that retries on its own is right for a wall display, and wrong if every open screen retries in lockstep. Longer waits, jitter and a slow interval that never stops.
3 min read
Refreshing a Supabase Session in Next.js 16's proxy.ts, and Not Authorising There
Next.js 16 renamed Middleware to Proxy. Using proxy.ts only to refresh a Supabase session cookie for admin routes, while every page and API route checks authorisation itself.
3 min read
Why My App No Longer Creates Indexes When It Deploys
A plain CREATE INDEX in a cold-start schema build locks a busy table as a side effect of shipping code. Moving indexes to CONCURRENTLY migrations, and what the app still does.
4 min read
Spending, Cash Flow and Who Owes Whom: Modelling Shared Bills in a Personal Finance App
When a bill is shared, 'how much did I spend?' has three honest answers. SpenDrop's rules for spending, cash flow, refunds and balances.
5 min read
Modelling a Multi-Stage Refurbishment Pipeline in Excel
Following one laptop through an Excel stock tracker: a completeness gate at receiving, an automatic hand-off list, status derived from department ticks, e-commerce counts, and EDATE warranties.
5 min read
Measured or Estimated: Calculating Fuel Economy Only From Full Tanks
Dividing a top-up into the distance driven gives a meaningless economy figure. How DriveKeep measures km/L only from full tanks and labels estimates.
5 min read
What Survives When an Excel Tracker Becomes a Web App
Mapping an Excel stock tracker's sheets, formulas and macros onto the tables of the web system that replaced it: what carried over unchanged, and what had to become explicit.
6 min read
Screens That Look Like Payments but Aren't: Parsing Malaysian Receipts Without Fake Expenses
Balance, credit-limit and reward screens all show RM amounts. How SpenDrop rejects them, and the Malaysian receipt formats two of my parsers read.
6 min read
One Checked Value Instead of Two Booleans: Making a Bad Config Unrepresentable
Two on/off flags allow a state with no correct behaviour: both off. Storing the enabled set as one CHECK-constrained value and validating the resulting state instead.
3 min read
Logout Still Worked for 30 Seconds: Token Caches on Serverless
A token verification cache meant a signed-out session kept working for up to 30 seconds. Why evicting on sign-out does not fix it on serverless, and why the answer was a small measured bound.
3 min read
Logging Apple Pay Taps Automatically With an App Intent and a Wallet Automation
An App Intent lets the Shortcuts Wallet automation hand each Apple Pay purchase to SpenDrop. The intent, its duplicate guard, and what iOS lets apps see.
3 min read
A Written Conflict Policy for a Local-First Workstation App
How an offline-first PWA writes locally first, syncs on its own timer with backoff, and resolves conflicts with a rule per data type: last writer wins, server wins, append-only, never synced.
5 min read
The Retries You Didn't Write: LLM Rate Limits and SDK Hidden Retries
A client library's default retries turned one rate-limited analysis into twelve requests against a free tier of twenty. Removing requests, telling limits apart, and returning 429 with Retry-After.
5 min read
A Cross-Paper Literature Review Built From Stored Analyses, Not PDFs
Building one LLM literature review across several saved papers by reading their stored analyses instead of re-sending every PDF. The trade-off, the size guards, and why partial results are refused.
4 min read
Keeping Your Email and Phone Off a Public Link Page Until Someone Asks
A QR link page that never ships private contact details in its HTML or JavaScript. Server-only values, a short request form, only the asked-for methods returned, and a 24-hour signed re-reveal.
3 min read
Building an iOS Share Extension That Reads a Screenshot and Saves to the App's Database
How SpenDrop's Share Extension loads an image from any app, stays inside the memory limit, runs Vision OCR and saves to the app's shared SwiftData store.
5 min read
Where Does a 4-Second Label Go? Instrumenting a Scan-to-Print Pipeline
Stage-by-stage tracing of a Web Bluetooth label print, an audit of every sleep in the code, and the rule of not claiming a speed-up until the hardware has measured it.
6 min read
Testing an iOS App With an In-App Test Runner, Launch Flags and XCUITest
274 logic checks run inside the app from a launch flag, a final check proves no test opened the real database, and six XCUITest flows drive the UI.
5 min read
Implemented, Available, Not Connected: Writing Honest Status Into Project Docs
"It exists in the repository" and "it runs in production" are different claims. The three-state status model I use across my projects, with real rows from each.
5 min read
Reference Data Nobody Can Edit: Postgres Triggers, Hash Pinning and Append-Only History
Protecting the examples an AI system is measured against: a trigger that refuses UPDATE and DELETE, SHA-256 hashes checked by tests, append-only history, and what these do not stop.
4 min read
From a Constant Per-Role Cookie to HMAC-Signed, Expiring Sessions
RPOMS's session cookie was the same bytes for every admin, forever. Replacing it with a signed payload carrying issue time and session id, plus a login throttle that needs no Redis.
4 min read
Hardening a Public Photo Upload: Magic Bytes, Header Dimensions and Stripping EXIF Twice
An anonymous endpoint that decodes images is easy to abuse. How RPOMS AI re-encodes photos in the browser, trusts none of it on the server, and never stores the uploaded bytes.
4 min read
Google Sign-In for Shared Workstations: PKCE, Pending Users and Revocable Device Tokens
Google OIDC with PKCE on a framework-free Node server, users keyed by sub, accounts that start pending, and workstations provisioned with hashed, revocable device tokens that keep working offline.
6 min read
Generating an Xcode Project File From a Script, With Stable IDs
Both of my iOS apps write project.pbxproj from a script. How the object IDs are made, why hashing names beats a counter, and what it costs.
3 min read
Monthly Request Numbers That Never Skip: Counters, Voids and Retries in PostgreSQL
How RPOMS issues monthly request numbers with no gaps: a locked counter row, number and row in one transaction, voids given back, retries made safe.
5 min read
Freezing a Label Design in CI by Hashing Its Rendered Raster
A Vitest test that hashes each label template's JSON and its exact 1-bit print raster, so any change that would move a single printed dot fails the build and the deploy.
5 min read
When a Per-Minute Token Limit Is Smaller Than One Request
A free-tier limit of 7,000 input tokens per minute, and research papers needing 7,920 to 20,362 tokens per call. Why waiting cannot fix that, and how a working fallback hid it.
3 min read
"Unclear" Is Not One Answer: Five Decision States for an AI Inspection
An AI check that cannot decide can fail for four very different reasons. How RPOMS AI records which one, maps it to what the public sees, and makes a missing mapping a type error.
4 min read
Getting Usable Text Out of Academic PDFs: Signatures, Encryption, Scans and Ligatures
The PDF ingestion path behind ResearchForge: checking the real file signature, opening print-restricted files, rejecting scans with no text layer, and cleaning text without removing content.
4 min read
A Postgres-Backed Express 5 API With No Database Server: PGlite, JWT and Security Tests
DriveKeep's backend runs PostgreSQL in-process with PGlite, signs HS256 tokens and has 17 security checks. What it does well and what must change first.
5 min read
Exposing a Local Ollama to a Vercel App: A 90-Line Token Gateway and a Cloudflare Quick Tunnel
Ollama has no authentication, so its port must never face the internet. The small Node gateway RPOMS AI puts in front of it, the tunnel, and how each hop is diagnosed.
4 min read
Evaluating an LLM App When Your Own System Hides the Result
Evaluating a deployed LLM app with no labelled data, and the three things that produced plausible but wrong measurements: a content-keyed cache, a CDN and a fallback that worked too well.
5 min read
Evaluating a Vision Model When You Only Have Thirteen Examples
Thirteen labelled photos are not a test set, but they can still be an honest evaluation. Separating what is known from what is assumed, removing leaked annotations, and controls built on purpose.
5 min read
Enforcing Architecture Boundaries in a Next.js App With One Test File
A Vitest file that reads the source tree and fails when a client component imports server code, an admin route skips its auth guard, or a minifier hazard creeps back in.
4 min read
Writing a User Guide Into the Spreadsheet Itself
A stock tracker's in-workbook guide: a colour legend for formula, drop-down and free-text columns, a sheet-by-sheet purpose list, and how the guide drifted from the formulas it described.
5 min read
Encoding Vague Acceptance Criteria Without Inventing Thresholds
Criteria that said long, multiple and small with no numbers: how a pure rule engine applies vague words as categories, orders checks for safety and answers UNCLEAR.
5 min read
Configuration Health You Compute, Not Store: One Model Record Across Three Modules
How RPOMS merged three definitions of a router model into one record, and flags broken configuration with a pure function instead of an issue table.
5 min read
Dependent Drop-Downs With INDIRECT and Named Ranges, and What They Cost to Maintain
A Brand → Model drop-down built with INDIRECT and one named range per brand works well on day one. The two update procedures, orphaned #REF! names and fixed ranges are what it costs later.
6 min read
Designing a Delivery Module: Scan Boxes, Never Type Serials, Freeze What Left
How the RPOMS delivery module works: scanned boxes instead of typed serials, a load that must match its quantity, and a snapshot of what actually left.
5 min read
Proving a Release Deleted Nothing: Row-Count Gates and a Whole-Database Preservation Test
How RPOMS checks that a release or migration lost no business data: a row-count gate that fails on any decrease, read-only scripts, and a test that compares every row of every table.
5 min read
CSV Formula Injection in Exports: The One-Apostrophe Fix
A name typed as =HYPERLINK(...) runs when someone opens a CSV export in a spreadsheet. The small cell guard RPOMS uses on both client and server exports, and what it leaves alone.
2 min read
Letting Another Web App Print Through an Open Tab: BroadcastChannel, Web Locks and COOP
How a tiny relay page lets another web application print on a Bluetooth printer held by an already-open tab, with one tab answering via a Web Lock and the printer's real result reported back.
6 min read
When Two Days Fill One Box: Counting Shared Consumables Without Double-Counting
A packaging box filled across two days must be charged once. How RPOMS derives each day's big-box usage from a running total, and how the rule evolved.
5 min read
First-Party Analytics Without Cookies: A Daily-Rotating Salted IP Hash
Page views and link clicks recorded by the site itself, with no third-party tracker and no cookies. A salted IP hash that changes daily, beacons that survive navigation, and what is still stored.
4 min read
Maintaining a Web System I Didn't Build: Small, Scoped Fixes
Three small fixes to a Next.js inventory app another developer built: a lazily loaded Excel library, a duplicate fetch on first load, and a reset-password redirect, kept reviewable.
5 min read
Continuous Label Printing Without Duplicates: Page Counters and "Uncertain" Jobs
Why one printer job per label made the paper feed and rewind, how multi-page jobs fixed it, and how a print queue decides between a safe retry and a label that may already exist.
6 min read
Contact Form Spam Protection Without a CAPTCHA, and Its Honest Limits
A honeypot, a minimum fill time, a same-site check, strict validation and a database-backed rate limit on a Next.js contact form. What each layer stops, and which ones a script can walk past.
4 min read
Yesterday Is Not Enough: Choosing Comparison Baselines for an Operations Dashboard
The five baselines the RPOMS dashboard compares today against, how each is computed, why one rising metric is red, and why missing data shows N/A.
4 min read
Chrome Throttled My Bluetooth Printer: Moving Timers Into a Web Worker
Labels slowed from about 4 seconds to 13 or more whenever the print tab was in the background. The cause was Chrome's timer throttling; the fix was a tiny Web Worker that owns the waits.
4 min read
Choosing an Embedding Model Under pgvector's 2,000-Dimension Index Limit
A decision record: how ResearchForge picked an embedding model and dimension around pgvector's index limit, cost and reversibility. The choice was made and wired up; no embedding was ever produced.
3 min read
Why Every Real Photo Timed Out: Capping Vision Input at 896 px to Fit a 60-Second Function
Full-size phone photos never got an answer from RPOMS AI's vision model. Encoding cost grew faster than pixel count; capping at the evaluated scale took 114 s down to 38 s.
4 min read
Canonicals That Pointed at a Redirect: www vs Apex, Sitemaps and Permanent Redirects in Next.js
Every canonical, og:url and sitemap entry on this site named a URL that 308-redirected. The fix, plus the sitemap, feed and redirect decisions made around it in Next.js.
4 min read
Calling Supabase's PostgREST Directly From FastAPI: The Details a Mock Won't Tell You
A Python backend talking to Supabase's PostgREST over plain httpx. Embedded ordering, counts in headers, deletes that return 200, reserved characters, and what mock tests can and cannot prove.
4 min read
Caching LLM Analyses by Content Hash Without Recording Who Uploaded What
A shared cache for expensive LLM analyses, keyed by a hash of the extracted text. How the key is chosen, what is never cached, and why the cache table has no user column.
5 min read
Blank Is Not Zero: Small Input Decisions That Decide Whether a Report Is Right
Three bugs from RPOMS's daily report: a target of 0 read as unset, a running output collapsed into one number, and one worker split by capitalisation.
5 min read
The 21-Second Benchmark That Was Wrong: Ollama Reuses an Encoded Image
A vision-model latency figure that was half the real cost, because the benchmark sent the same image twice. How it was caught, corrected, and what a fair benchmark needs.
3 min read
A Performance Gate That Stopped Crying Wolf: Medians and a Noise Floor
A baseline-versus-current benchmark flagged regressions on identical code. Medians of three runs and a rule that ignores tiny metrics made it a gate worth trusting.
4 min read
Building a Web Screen Around a Barcode Scanner: Enter Keys, Excel Pastes and a Beep That Froze the Page
How RPOMS's scan screens handle a barcode scanner: Enter-terminated scans, pasted Excel columns, one batched lookup, and a beep that no longer blocks.
5 min read
An Append-Only Audit Log That Keeps What Was Deleted
RPOMS's audit log records who acted, from the signed session, and a snapshot of what each destructive action removed, which turns irreversible deletes into recoverable ones.
4 min read
Structured Receipt Extraction With Apple's Foundation Models and @Generable: A Code Walkthrough
How DriveKeep's iOS code asks Apple's on-device Foundation Models for typed receipt fields and fuses them with Vision OCR. A walkthrough of unverified code.
5 min read
An Append-Only Cloud Backup for a Local-First iOS App
SpenDrop's optional cloud backup never overwrites: per-user Storage folders, no UPDATE policies, a content hash, and a restore that copies local data first.
5 min read
The SwiftData Store I Configured Was Never the One Being Used
Renaming an iOS app opened an empty database. The App Group store URL I had configured had never worked; SwiftData's fallback had been doing the job.
4 min read
Keeping Analytics for 90 Days: Roll Up, Then Delete, With pg_cron
Detailed analytics events kept for a set period, then summarised into daily totals and deleted by a scheduled PostgreSQL function. The retention setting, the job, and the time-zone trade-offs.
4 min read
Adding Label Printing to an Existing Next.js App Without Touching the Printer
The calling side of a print integration: validating SKUs the way the printer app will, passing them in a URL fragment, detecting blocked pop-ups, and refusing to print unsaved changes.
5 min read
When the Supabase Session Pooler Ran Out: An EMAXCONNSESSION Postmortem
Admin pages on a Next.js app on Vercel showed a generic error for about six minutes. The cause was Supabase's session-mode pooler hitting its 15-client limit, not the code.
6 min read
From Millimetres to Dots: Rasterising a 50×30 mm Label for a 576-Dot Printhead
How a label template in millimetres becomes a 1-bit raster for a 300 dpi thermal printhead: geometry, thresholding, whole-dot barcodes and why some designs cost more Bluetooth packets.
6 min read
A Photo Quality Gate That Runs Before the Vision Model: Blur, Exposure and Glare From Raw Pixels
How RPOMS AI rejects blurred, dark, over-exposed and glare-filled photos with plain pixel arithmetic before any model is called, and how the thresholds were calibrated.
6 min read
Optimistic Concurrency for a Postgres Upsert, Using the Row's Own updated_at
Two admins saving the same day meant the second silently replaced the first. One guarded INSERT ... ON CONFLICT statement and a 409 fixed it, and two bugs followed.
5 min read
One Person, Referenced Everywhere: A JSON-LD @graph for a Personal Site
How this site declares its Person and WebSite nodes once and points every article, project page and the homepage at them by @id, and what that forced me to leave out.
5 min read
The NIIMBOT B1 Pro Bluetooth Protocol, Packet by Packet
The frame format, checksum, connect packet, identification reads and the full V4 print sequence I use to drive a NIIMBOT B1 Pro over Web Bluetooth, with what is verified and what is not.
9 min read
Logging Every Status Change in Excel With VBA to Measure Time per Department
Formula-driven status changes don't fire Worksheet_Change. How a stock tracker logged them with Worksheet_Calculate, timed each stage, and read the log back with SUMIFS and LOOKUP(2,1/…).
6 min read
Which RM Is the Payment? Classifying Every Amount on a Receipt Screenshot
A payment screenshot shows fees, balances and advert prices as well as the payment. How SpenDrop labels every RM value, using keywords and screen position.
6 min read
Generating Barcode and Group SKUs in Excel From Spec Columns
How an Excel stock tracker built a per-brand serial that rolls past 99, a barcode SKU and a variant group key from spec columns with LET and TEXTJOIN, and where formula IDs break.
6 min read
Filling a Word Template in Node.js When Word Splits Your Placeholders
How RPOMS fills the office's own Delivery Order .docx with JSZip: placeholders split across Word runs, a two-pass replace, and an Excel tracker beside it.
6 min read
Running FastAPI and Next.js as Two Services Behind One Vercel Origin
One Vercel project, a Next.js frontend and a FastAPI backend on the same origin. The routing, the CORS outage it fixed, the image optimizer that vanished, and the Python packaging details.
5 min read
Stock You Never Store: Deriving Inventory From a Ledger and the Day's Report
Why RPOMS never stores a stock balance: remaining is worked out at read time from a ledger and the daily report, so edits need no reversal and a figure can be traced.
5 min read
Building Software in Cyberjaya: The Development Environment Behind the Work
What a production operations system, a client website, an AI research assistant and a vision inspector actually get built on, from a laptop in Cyberjaya: the machine, the stack, where things are hosted, and the local details that shaped the code.
6 min read
A Vision Model That Only Observes: Building RPOMS AI Without Letting the Model Decide
A router surface inspector where the vision model reports what it sees and a deterministic rule engine decides. Why the split, what a 7B open model actually scored, the blank-image control that changed the prompt, and a safety gate that turns most answers into "check manually".
7 min read
Printing Labels From the Browser Over Web Bluetooth: The RPOMS Print Engine
A scan-to-label workflow that drives a NIIMBOT B1 Pro directly from Chrome, with no vendor app and no backend. What the protocol work looked like, how 116 tests run without a printer, and what the first real print taught.
6 min read
The Business Day Is Not the Server Day: Time Zones in a Malaysian Operations System
A deployed server keeps UTC. A warehouse floor in Malaysia keeps UTC+8. Until eight in the morning, the two disagree about what day it is, which is long enough for a morning delivery to file itself under yesterday.
4 min read
Staged CSV Import: Validating Operational Data Before It Is Written
The office already keeps its records in a workbook. Importing that sheet into a production system means matching its headers loosely, reading its dates the way it wrote them, surfacing duplicates before anything is saved, and never letting the client tell the server what a file contains.
5 min read
One Storage Interface, Three Backends: How RPOMS Picks Its Database
PostgreSQL on Vercel, MySQL on cPanel, or a folder of JSON files on a laptop, chosen by which environment variables exist. What that bought, what it cost, and the production lessons about pooled connections and reads that never came back.
6 min read
Gating a CV Download Behind a Server-Signed Grant in Next.js
A public portfolio, a CV with a phone number in it, and a public repository. How this site releases three contact fields and one PDF only after a form, without a database and without trusting the browser.
4 min read
A Live GitHub Contribution Heatmap in Next.js, Without a Token in the Browser
How this site's homepage renders real GitHub contribution data: a server-fetched calendar cached for an hour, a documented API with an undocumented fallback, and one small client island for the tooltip.
5 min read
Every Policy Was Correct, and Every Policy Was Inert
A data-isolation defect that passed the entire test suite, shipped, and behaved perfectly. What it took to find it, and why the fix was to stop asking the application to be careful.
4 min read
When Not to Fall Back to Another AI Provider
Automatic failover between model vendors is easy to build and easy to build wrong. Most errors should never trigger it.
3 min read
Making an LLM Admit the Paper Doesn't Say That
The dangerous failure of an AI summarising tool is not a blank answer. It is a plausible one about a section that was never in the source. Enforcing groundedness in four layers instead of asking for it once.
4 min read
Cutting a Page's Image Payload From 12 MB to Under 3 MB
The performance and accessibility pass on a production homepage: what was actually slow, what fixed it, and why most of it was not clever.
4 min read
Verifying a Static Site You Built by Hand
Hand-written HTML has no type checker and no test runner. On a production build I wrote the guarantees instead, as a post-build script that fails the build.
4 min read
Rebuilding a Design-Tool Prototype as a Production Website
An approved design arrived as a self-extracting bundle running React from a CDN. Shipping it meant removing almost everything and keeping the part that was actually the specification.
5 min read
Reporting From Operational Data
What makes operational reporting actually useful for decisions, instead of just being a record of what happened.
1 min read
Why Internal Software Needs Good UX
Internal tools get less design attention than public products, even though the people using them have no choice but to use them.
1 min read
Replacing Repetitive Manual Work With Software
How to tell whether a repetitive manual task is actually worth automating, and what to check before building anything.
1 min read
What an IT Systems Role Actually Involves
A grounded look at what IT systems work covers day to day, beyond the narrow idea of fixing computers.
1 min read
Designing for Desktop and Mobile Before Development
Planning responsive behavior as part of the design phase, instead of leaving it as a problem for development to solve alone.
1 min read
Translating Figma Components Into Reusable Code
How design components in Figma map to reusable UI components in code, and where that mapping gets harder than it looks.
1 min read
From Figma Design to Production Website
What actually happens between a finished Figma design and a working, deployed website.
1 min read
Designing a Website in Figma Before Development
Why doing the design work in Figma first, rather than designing in the browser, changes how the build goes.
1 min read
Why System Maintenance Matters After Deployment
Shipping an operational system is the start of the work, not the end. What ongoing maintenance actually involves.
1 min read
Why Software Developers Should Understand the Physical Process
Building software for a real operation goes better when the developer understands the physical process it supports, not just the data model.
1 min read
From Manual Workflow to Digital Workflow
What actually changes when a manual, paper-or-spreadsheet process becomes software, and what shouldn't change.
2 min read
How Admin Panels Help Manage Operational Data
What a well-designed admin panel actually needs to do for a production or operations system, beyond CRUD screens.
1 min read
Common Problems in Manual Production Tracking With Spreadsheets
The specific ways spreadsheet-based production tracking breaks down in practice, the tell that says it is time to move, and what a replacement has to remove.
3 min read
What Is a Production Management System?
A plain explanation of what production management software actually does, and why it's a different problem from a generic business app.
1 min read
Also available as an RSS feed.