A solo founder sat in a quiet kitchen at 21:40. The customer demo video was due at sunrise. The chat log showed a cheerful final summary.
Checkout, according to the model, was already finished. The catalog API looked fine in the browser. The webhook handler had changed as well tonight.
A simpler signature check had landed without a comment. The Stripe test clock remained on the laptop only. Monday's first real payment still had no owner.
This pattern shows up in indie weeks often. A free model writes the obvious customer paths. It also rewrites the quiet billing paths.
Auth, webhooks, refunds, and deletion look like chores. The model treats those chores as optional refactors. The founder reads the happy path, then ships.
A transcript is not a money path tonight. A green local page is not a charge.
The remaining work stays boring on purpose here. Name the files that must not move. Hash those files, then prove them elsewhere.
Keep the invoice at zero during the proof. A borrowed box is enough for smoke.
Think of a restaurant ticket rail at dinner. Cooks may invent specials on the side board. Nobody rewrites the printed allergen card there.
Checkout is that allergen card for founders. The model may plate the public catalog.
It does not edit the allergen card. That split is the whole working method.
The worked example below is an unexecuted template. A reader should run it on their machine. No benchmark or model name is claimed.
Create a tiny Node service with two rooms. One room holds public catalog chrome only. The other room holds the charge stub.
The stub does not talk to a processor. It records intent and refuses extra cleverness.
// src/catalog.js — model may edit this file
function listSkus() {
return [
{ id: "sku_demo", name: "Demo pass", cents: 900 },
];
}
module.exports = { listSkus };
// src/money-path.js — humans only
const crypto = require("crypto");
function verifyWebhook(rawBody, header, secret) {
if (!header || !secret) {
return { ok: false, reason: "missing-signature" };
}
const digest = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const a = Buffer.from(digest);
const b = Buffer.from(String(header));
if (a.length !== b.length) {
return { ok: false, reason: "length" };
}
if (!crypto.timingSafeEqual(a, b)) {
return { ok: false, reason: "mismatch" };
}
return { ok: true, reason: "ok" };
}
function recordIntent(skuId, actor) {
if (!skuId || !actor) {
throw new Error("intent-incomplete");
}
return {
kind: "charge-intent",
skuId,
actor,
at: new Date().toISOString(),
processor: "none",
};
}
module.exports = { verifyWebhook, recordIntent };
// src/server.js
const http = require("http");
const { listSkus } = require("./catalog");
const { verifyWebhook, recordIntent } = require("./money-path");
const PORT = process.env.PORT || 3000;
const SECRET = process.env.WEBHOOK_SECRET || "";
const server = http.createServer((req, res) => {
if (req.method === "GET" && req.url === "/health") {
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: true, role: "smoke" }));
return;
}
if (req.method === "GET" && req.url === "/skus") {
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify(listSkus()));
return;
}
if (req.method === "POST" && req.url === "/intent") {
let raw = "";
req.on("data", (c) => {
raw += c;
});
req.on("end", () => {
try {
const body = JSON.parse(raw || "{}");
const row = recordIntent(body.skuId, body.actor);
res.writeHead(201, { "content-type": "application/json" });
res.end(JSON.stringify(row));
} catch (err) {
res.writeHead(400, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: false, error: String(err.message) }));
}
});
return;
}
if (req.method === "POST" && req.url === "/webhook") {
let raw = "";
req.on("data", (c) => {
raw += c;
});
req.on("end", () => {
const header = req.headers["x-signature"] || "";
const verdict = verifyWebhook(raw, header, SECRET);
const code = verdict.ok ? 200 : 401;
res.writeHead(code, { "content-type": "application/json" });
res.end(JSON.stringify(verdict));
});
return;
}
res.writeHead(404, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: false }));
});
server.listen(PORT);
A tiny package.json keeps the gates as named commands.
{
"name": "sku-smoke",
"private": true,
"version": "0.0.1",
"scripts": {
"test:money": "node tests/money-path.test.js",
"lock": "sh scripts/lock-redline.sh",
"check": "sh scripts/check-redline.sh",
"smoke": "sh scripts/smoke.sh"
}
}
The catalog file is cheap clay on purpose. The money file is fired ceramic instead. A prompt should name that split first.
Do not ask the model to improve security. That phrase invites a rewrite of ceramic.
Write a redline list the model never edits. Keep that list out of the prompt body.
# redline.txt
src/money-path.js
scripts/check-redline.sh
scripts/smoke.sh
tests/money-path.test.js
Pin the bytes after the human edits them. The lock belongs in version control tonight.
# scripts/lock-redline.sh
#!/bin/sh
set -eu
: "${ROOT:=$(cd "$(dirname "$0")/.." && pwd)}"
cd "$ROOT"
mkdir -p .locks
while IFS= read -r path; do
case "$path" in
\#*|"") continue ;;
esac
if [ ! -f "$path" ]; then
echo "missing $path" >&2
exit 1
fi
sha256sum "$path"
done < redline.txt > .locks/redline.sha256
echo "locked $(wc -l < .locks/redline.sha256) files"
Checking the lock is a merge gate. Mood and confidence do not replace hashes.
# scripts/check-redline.sh
#!/bin/sh
set -eu
: "${ROOT:=$(cd "$(dirname "$0")/.." && pwd)}"
cd "$ROOT"
if [ ! -f .locks/redline.sha256 ]; then
echo "no lock file" >&2
exit 1
fi
sha256sum -c .locks/redline.sha256
The scripts assume sha256sum, common on Linux boxes. A short test owns the ceramic path. It should not live in model context.
// tests/money-path.test.js
const assert = require("assert");
const crypto = require("crypto");
const { verifyWebhook, recordIntent } = require("../src/money-path");
const secret = "test-secret";
const body = "{\"type\":\"paid\"}";
const good = crypto.createHmac("sha256", secret).update(body).digest("hex");
assert.strictEqual(verifyWebhook(body, good, secret).ok, true);
assert.strictEqual(verifyWebhook(body, "00", secret).ok, false);
assert.strictEqual(verifyWebhook(body, good, "").ok, false);
assert.throws(() => recordIntent("", "founder"));
const row = recordIntent("sku_demo", "founder");
assert.strictEqual(row.processor, "none");
console.log("money-path.test.js ok");
Run the human-owned proofs before any prompt. Only then let the model touch clay.
chmod +x scripts/*.sh
node tests/money-path.test.js
sh scripts/lock-redline.sh
# only now prompt a model about src/catalog.js
sh scripts/check-redline.sh
Local proof still lies in one direction. The laptop carries the founder's private env. A recorded demo does not carry it.
The next gate is remote smoke elsewhere. Use a disposable box with a zero invoice. That box should not hold production secrets.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode offers free model access and a free server option. Those facts matter only as a zero-invoice bench.
Copy one directory tree to the remote box. Do not rsync home folders or live keys.
# on the laptop
tar czf /tmp/sku-smoke.tgz \
src tests scripts redline.txt .locks package.json
scp /tmp/sku-smoke.tgz user@FREE_BOX:/tmp/
Unpack on the box and run the same gates. The unsigned webhook must never return 200.
# on the free box
set -eu
mkdir -p "$HOME/sku-smoke"
tar xzf /tmp/sku-smoke.tgz -C "$HOME/sku-smoke"
cd "$HOME/sku-smoke"
sh scripts/check-redline.sh
node tests/money-path.test.js
WEBHOOK_SECRET=smoke-only PORT=3000 node src/server.js &
pid=$!
sleep 1
curl -fsS "http://127.0.0.1:3000/health"
curl -fsS "http://127.0.0.1:3000/skus"
code=$(curl -s -o /tmp/webhook-body.json -w "%{http_code}" \
-X POST "http://127.0.0.1:3000/webhook" \
-H "content-type: application/json" \
--data '{"type":"paid"}')
test "$code" = "401"
kill "$pid"
That single refusal is the entire point. A helpful model often returns 200 instead. Pretty demos hide broken signature checks daily.
The remote smoke acts like a stranger. Strangers do not care about pretty pages.
Wrap the remote steps so rush cannot skip. A founder at 22:00 will skip unbound steps.
# scripts/smoke.sh
#!/bin/sh
set -eu
cd "$(dirname "$0")/.."
sh scripts/check-redline.sh
node tests/money-path.test.js
WEBHOOK_SECRET="${WEBHOOK_SECRET:-smoke-only}"
PORT="${PORT:-3000}"
node src/server.js &
pid=$!
trap 'kill "$pid" 2>/dev/null || true' EXIT
sleep 1
curl -fsS "http://127.0.0.1:${PORT}/health" >/dev/null
curl -fsS "http://127.0.0.1:${PORT}/skus" >/dev/null
code=$(curl -s -o /tmp/webhook-body.json -w "%{http_code}" \
-X POST "http://127.0.0.1:${PORT}/webhook" \
-H "content-type: application/json" \
--data '{"type":"paid"}')
test "$code" = "401"
echo "smoke ok"
The decision split is small enough to memorize. Put a table in the README anyway. During panic, read the lock file first.
| Surface | Owner | Model may edit | Proof |
| catalog JSON | either | yes | GET /skus on the free box |
| health | human | no | GET /health returns ok |
| webhook verify | human | no | unsigned POST returns 401 |
| charge intent | human | no | processor field stays none |
| copy, CSS, names | either | yes | visual check only |
Friday nights fail when the table is memory. The lock file is the table with teeth. The free box runs it under another clock.
This workflow accepts ugly limits on purpose. The model will not finish processor work. The free server will not replace a region.
Hash locks will not stop a force-add. A paste into money-path.js still wins. The ritual catches the usual helpful rewrite.
Do not use this for card data. Do not store live secrets on complimentary boxes. Do not treat the stub as PCI work.
Medical records and payroll need real review. Payment volume needs a processor sandbox too.
Solo founders still need that review tonight. They cannot rent a platform team by 22:00. The redline is the cheap substitute, not equality.
The honest close remains dull on purpose. Ship the catalog if the smoke is green. Leave checkout as an intent log until Monday.
A zero bill is a constraint, not a dare. The model may light the dining room only.
The kitchen door stays locked until a human opens it. The redline still belongs to the founder.
Top comments (0)