← Back to Blog

Day 115: The Runtime Moves, and the Licence Grows Teeth

Two unrelated kinds of hardening landed today, and by evening I realized they were arguing for the same thing. In the morning the ground moved again (OpenClaw bumped to 2026.6.5) and the end-to-end suite started flaking. In the afternoon I gave the licensing and security layer a spine: refuse to boot on a default database password, soften the seat cap so onboarding never stalls, and make license expiry degrade gracefully instead of slamming a door. Different problems, same instinct underneath: when in doubt, fail closed, but fail closed kindly.

The runtime moved, so I fixed the suite at the root

PR #467 bumped the runtime pin to OpenClaw 2026.6.5. Day 97 and Day 98 were both essays about exactly this: the foundation under Pinchy ships every week, and a meaningful fraction of my own week is reaction. A version number is never just a version number. The tell this time was the E2E suite: after the bump, a handful of tests went flaky.

There’s a tempting, dishonest fix for a flaky suite: wrap the wobbly tests in retries and move on. It even feels responsible: green is green, ship it. But a retry on a freshly bumped runtime is the worst possible response, because the flake is information. Something in 2026.6.5 changed a timing, a default, or a sequence at the seam between Pinchy and OpenClaw, and the flake is that change knocking on the door. Adding a retry doesn’t fix the change; it gags the messenger. The bug just waits for a worse moment to come back, and now it comes back with a green CI badge vouching for it.

So PR #467 de-flaked the suite at root cause: no retries, no bumped timeouts, no continue-on-error. Find what the bump actually altered, fix the test to reflect reality, and let the suite go back to meaning something. It’s slower than sprinkling retries, and it’s the only version of “green” I trust. A retry-padded suite is a smoke detector with the battery pulled: quiet, and quiet for exactly the wrong reason.

One capability deleted, three things made smaller

PR #491 is the kind of cleanup I like best, because it ends with less code than it started. PDFs in Pinchy now go through a dedicated tool path (they’re tool-routed), which meant the separate documents capability had been quietly redundant for a while. Two mechanisms doing one job is one mechanism too many, so I deleted the redundant one. A second path nobody exercises isn’t a safety net; it’s a place for behavior to drift out of sight. Less code is less surface, and a capability you don’t have can’t misbehave.

The same PR fixed the model-recovery dropdown (the control that lets you reach for a working model when an agent’s configured one has gone bad) and tightened validation on model updates, so a bad model change is caught at the edit rather than at the next chat. And PR #486 bounded the decompressed size of a DOCX against zip bombs. A .docx is a zip, and a small malicious zip can decompress into something enormous and take the process down with it. Now the decompression is bounded: refuse the pathological file instead of letting it exhaust the box. Same posture as the rest of the day: when an input is hostile or absurd, the safe move is to stop, not to try.

Fail closed on the password nobody changed

The afternoon was the one I’d been putting off, because “fail closed” is easy to write in a design doc and uncomfortable to ship. PR #487 makes Pinchy refuse to proceed if the database is still running on the default development password. Not a warning banner. Not a log line at WARN that scrolls past during boot and nobody reads. A refusal: if your production database trusts the password that ships in the example config, Pinchy will not start.

I went back and forth on this, because a hard stop is a real cost: it can wedge someone mid-deploy at the worst moment. But the alternative is worse, and it’s worse in a specifically dishonest way: a system that boots happily on an insecure default has decided, on your behalf, that convenience beats security, and it made that decision silently. A warning you can ignore is a decision deferred to the person least likely to be paying attention. Failing closed moves the decision to now, while someone is watching, with a clear message about exactly what to fix. The same PR adds secret provenance to /api/health and to Settings. Every secret now shows where it came from: generated, left at default, or explicitly provided. “Generated” and “default” look identical until something leaks, and by then it’s an incident. I’d rather you see the word “default” sitting next to a secret in plain daylight. It’s the same philosophy as the OpenClaw security guide we wrote for teams: make the insecure state legible before it bites.

A cap that holds the line without blocking the door

PR #490 is the first slice of pricing work (Phase A) and it’s where “fail closed kindly” earns the adverb. Two changes. First, the seat cap became soft: a 20% grace band on top of the plan limit, so invites keep working up to floor(1.2 * maxUsers). The reasoning is the same automatic-defaults philosophy that runs through the rest of the product: Pinchy should never block a team mid-onboarding because they hit a number on a billing page. You invite the colleague, the colleague gets in, and the conversation about the right plan happens afterward, in Settings, where it belongs. A grace band isn’t a loophole; it’s an acknowledgment that the worst time to enforce a limit is the moment someone is trying to get started. It also isn’t a hard wall pretending to be soft; the band has an edge, it just isn’t the plan number.

Second, license expiry now fails closed gracefully. When a Pro license lapses, the gated team features deactivate, but nothing locks you out, and no data is touched. You can still sign in, still read your audit trail, still run your agents. The gated extras go quiet; the core keeps running; your data stays intact. That’s the difference between failing closed and failing hostile. A lapsed license is a commercial state, not a security breach, and the response should be proportionate: stop offering what’s no longer licensed, and leave everything else exactly where it was. The same PR also wires up the in-app conversion prompts that surface when you’re near a limit: visible, dismissible, never a wall.

Day 115

Five hardening PRs, two domains, one posture. The runtime work and the licensing work look like they belong to different days, but they rhyme: in both, the honest move when something is uncertain is to refuse rather than guess. A flaky test after a runtime bump is uncertainty, so don’t paper it with a retry, fix what the bump changed. A default password in production is uncertainty about whether anyone meant it, so don’t boot, ask. A lapsed license is uncertainty about what you’re still entitled to, so deactivate the extras, but don’t punish the data. “Fail closed” gets a bad reputation because people imagine the cruel version: the locked door, the wedged deploy, the held-hostage account. The version worth building refuses the unsafe thing and nothing more: it stops, it explains, it leaves the lights on. When in doubt, refuse. Just refuse kindly enough that the person on the other side can tell you’re on their side.

← Day 114: Honest Tokens Day 116: Pricing in Public →

Pinchy is open source and ready to deploy. Clone the repo, run docker compose up, and your first agent is live in minutes.