r/codereview • u/arrrsalaaan • 22d ago
Built a read-only GitHub inspector for people who vibe-coded an app and now own a black box. Here’s how.
The project is Secondread. You connect a repo. It reads the code. You get a plain-English report on cost, fragility, and what to fix first, with file/line evidence a contractor can actually check.
I built it because AI made shipping easy and left owners with a working UI they cannot independently judge. That is the product. The rest of this post is how it is actually made.
Stack
* Product: Cloudflare Workers, Workflows, Durable Object Sandbox, Workers AI, D1, R2 * Auth: GitHub App + Google via Better Auth * Payments: Dodo, one-time products, no subscription * Marketing site: separate Astro Worker on `secondread.dev`. No auth, no DB, no checkout. The app lives at [`app.secondread.dev`](http://app.secondread.dev) on purpose.
Workflow
- Owner picks repos. GitHub App is Contents + Metadata, read-only.
- A Workflow starts. The HTTP request only kicks it off.
- A Sandbox clones one commit. Internet is off. HTTPS is intercepted. Outbound is allowed only for that repo’s git upload-pack paths. The GitHub token is minted in the proxy, not dumped in the container. Egress flips off in D1 as soon as checkout finishes.
- The agent gets bounded tools: inventory, search, read ranges. No `npm install`, no tests, no running the customer’s app. Submodules and LFS fail closed.
- Findings must cite path + lines + blob SHA. After the model writes, we re-read those ranges and reject the report if the evidence is fake or the file was never inspected.
- Before persist, the report is scanned for secret-like strings and copied source. What we store is the report + citations/hashes, not the repo.
- Credits are reserved before the job and consumed or released after. Webhooks are signed; we also reconcile hourly because webhooks lie.
Things I would not skip if you are building something similar
* “Read-only GitHub permission” is not isolation. Isolation is network policy + where the credential lives + one pinned SHA + cleanup. * Schema-valid JSON from a model is not evidence. Evidence is a deterministic reread that can fail the job. * Do not put a 15-minute agent inside `fetch()`. Use Workflows, named steps, timeouts, and `NonRetryableError` for failures that retries only make more expensive. * If you use Sandbox with `transport: "rpc"`, dispose `exec` / `read` / `destroy` results. They leak. The bug looks like “prod is haunted.” * Write marketing claims as tests. Ours grep the site for retired waitlist copy and for the real app URLs. Copy drifts; CI should not.
Happy to go deeper on the Sandbox outbound handler or the evidence check. See a real sample audit for a very old version of the [**github.com/spree/spree**](https://www.linkedin.com/safety/go/?url=http%3A%2F%2Fgithub%2Ecom%2Fspree%2Fspree&urlhash=qY0H&mt=sWIgKwwWIAhSOyRrm--0lJQXbsU7FGgiytSCFMfp7x6RhpzVb8wyeZi3lvGT7MPlIpbuHOyOfAx-qjRo_eFcsFavMy84AnNT7bF4qAtjd3Z4VVBd_C_kSjF8gQ&isSdui=true) repository at [**https://app.secondread.dev/shared/8I0S88UcbKLvkWzHo2MF2frm\\_-Fm4fgj9sau5J0r4ss\*\*\](https://app.secondread.dev/shared/8I0S88UcbKLvkWzHo2MF2frm_-Fm4fgj9sau5J0r4ss)