release

CodeInstances Adds a Self-Hosted Store, Push and Pull by ID, and Bug Reports That Reproduce

CodeInstances v0.5.0 is about getting a run from where it failed to where someone can fix it. A test fails in CI at 3 a.m. The run is kept, but it’s sitting on a build machine that’s gone in an hour. This release adds a small store of your own to keep runs in, push and pull to move them by id, and report, which writes the bug report you paste into the issue.

The id was already there. Every instance carries the SHA-256 of its contents, which verify checks, so it can’t drift from what’s in the file. The store uses that id as the address. You push a file and get its id back. Anyone with access pulls it by that id, or by its first 12 characters, and gets exactly that run, checked on arrival.

A failing run, from CI to a laptop. A CI job runs record – go test ./… and when it fails, runs codeinstances push run.instance and codeinstances report. The store, run with codeinstances serve, keeps files named by their ids. A laptop pulls by id with codeinstances pull 3f2a9c1e77b0, then replays offline. Below, the bug report pasted into the issue: a heading with the error, the command, the fingerprint, the calls that failed, and the pull and replay commands.

The Store

codeinstances serve -dir instances runs a small HTTP server that keeps instances in a folder, one file per id. It only takes a file whose id checks out, and the same file pushed twice is kept once. It lists what it holds, groups the failures by fingerprint the way group does, and has a plain page that shows both. The API is four JSON endpoints, documented in the README.

It’s a network service, so it’s careful. Every request needs a token, and when you don’t give one, serve makes a random one and prints it. Uploads have to be sent as JSON, which a web page can’t do to another site without asking first. A store on your own machine answers only to loopback names, so a page can’t reach it by pointing a name of its own at 127.0.0.1. Each download is checked against its id again before it’s sent. On the other side, push and pull follow no redirects, so the token only goes where you sent it, and pull refuses a file that isn’t intact or isn’t the one it asked for.

The Bug Report

codeinstances report failing.instance writes Markdown: how the run ended, the command, the fingerprint, the last lines of output, the calls that failed, and the commands that bring it back, pull then replay. Paste it into the issue and the person fixing it is two commands away from the exact run that failed, offline. Text from the run sits in code blocks, so a stray @team in a log can’t ping anyone. File names and commands are quoted for the shell, so pasting them runs nothing extra.

In CI

The README links a GitHub Actions example. It records the tests, and when they fail, it writes the report into the job summary and pushes the run to your store. record exits with the test’s own exit code, so the job fails exactly as it did before. It’s an example, and this project’s own CI doesn’t run it.

What It Doesn’t Do

The store is one server and one folder, with one token and no accounts. It keeps an index in memory, which suits thousands of runs, not millions. Nothing is scrubbed before a push beyond the secret headers that recording already redacts, because changing bodies or query strings would stop the run from replaying. So keep the store as private as your CI logs.

Tested

An independent review before release went at the store as an attacker would, and it found real problems. A store started without a token could be written to by any web page, and read through DNS rebinding. The report’s shell quoting would have run $(...) from a crafted file name if pasted. A large upload could take many times its size in memory. A second review of the fixes found that a crafted file could still slip terminal control codes or a fake heading into the report through fields like the signal name, so the file format’s checks are stricter now too. All of that is fixed, and each fix has a test. 93 Go tests pass, and the race detector finds nothing. The self-checking demo has 24 checks now: it starts a store with a token, pushes three failing runs, pulls one back by a 12-character id prefix and compares the bytes, and checks the report. Pushing the three took 18 ms, and pulling one back took 5 ms. The smoke check on the release binaries does the same round trip with a real codeinstances serve. The result files are in test/results.

Next on the plan is forking a run at a step and carrying on from there. Download v0.5.0, and the reference has the details.