Hardening pass
Reading the money path first
A small commerce platform was days from taking real payments. I reviewed the trust boundaries before the features and found an order that could mark itself paid.
- Payment, webhook and administrative boundaries reviewed before anything else
- Money path firstPayment, webhook and administrative boundaries reviewed before anything else
- What was knowingly not fixed, with reasons and cost
- Deferred listWhat was knowingly not fixed, with reasons and cost
- Mis-committed asset removed from repository history
- 164 MBMis-committed asset removed from repository history
The client
A small direct-to-consumer commerce platform in the EU — payments, an administrative area, file uploads and a messaging bot — built quickly by a small team and approaching launch without a security review.
The engagement
A short review-and-harden pass ordered by trust boundary, closing with a written deferral list.
The problem
A commerce platform with payments, an administrative area, file uploads and a messaging bot was approaching launch without anyone having looked at it as an attacker would. The failure mode here is not a dramatic breach — it is a quiet one: orders marked as paid that were not, a webhook anyone can call, an administrative page that is hidden rather than protected.
What I did
I read the money path before anything else, because that is where a small platform loses real value rather than reputation. The order-completion route trusted its caller instead of verifying the payment provider's event; the incoming webhook had no shared secret; the administrative area relied on the interface not linking to it; and rate limiting was held in process memory on a runtime that discards process memory. Those were fixed first, followed by idempotency on payment events so a retried notification cannot double-apply, then the ordinary hygiene. Everything I chose not to fix was written down with the reason and the cost of doing it later — an undocumented deferral is indistinguishable from an oversight when someone reviews the system in six months.
What was built
A hardening pass ordered by trust boundary rather than by feature: provider-verified payment completion, a shared secret on the incoming webhook, a real server-side guard on the administrative area, durable rate limiting, idempotent payment events, then the ordinary hygiene of indexes, caching, error reporting and continuous integration.
On the table at the end
- Fixes across the payment, webhook and administrative boundaries
- Documented deferral list with reasons and effort estimates
What it changed
Closed a payment-verification hole before the platform took live money, made repeated payment notifications safe, and left an explicit record of what was deliberately deferred and what it would cost to close later.
How it ran
- 01
Follow the money
Trace the path from checkout to an order being marked as paid, and check what the system trusts at each hop.
- 02
Close the trust boundaries
Verify the provider's event rather than the caller's word, add a shared secret to the webhook, put a real server-side guard on the admin area.
- 03
Make retries safe
Idempotency on payment notifications and durable rate limiting, since a serverless runtime forgets what it held in memory.
- 04
Ordinary hygiene
Indexes, caching, error reporting wired into the build, continuous integration, and a large mis-committed asset removed from history.
- 05
Write down what you did not do
Row-level security, content security policy and migration baselining documented as deliberate deferrals with reasons and effort.
Other work
All case studies →- Investment research
Seventeen architecture decisions before the first sprint
An AI research platform for analysts, where the hard parts were not the models. I wrote the decisions down — including prompt-injection defence and per-subscription isolation of the vector store — before anyone started building.
- Capital markets
Giving a bilingual site's content back to its editors
Every string on a bilingual market operator's site lived in code, so a comma was a developer ticket. I moved the copy into a headless CMS without letting the market data follow it.
- Consumer IoT
Shipping a wearable app before the wearable existed
A child-safety product whose hardware was not ready. We built the MVP against a mocked device contract instead of waiting, and shipped without the usual collapse when real constraints arrived.
Something similar on your plate?
Thirty minutes, no deck. I will tell you whether it is worth doing at all.