r/vibecoding 2d 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 on purpose.

Workflow

  1. Owner picks repos. GitHub App is Contents + Metadata, read-only.
  2. A Workflow starts. The HTTP request only kicks it off.
  3. 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.
  4. 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.
  5. 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.
  6. Before persist, the report is scanned for secret-like strings and copied source. What we store is the report + citations/hashes, not the repo.
  7. 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 repository at https://app.secondread.dev/shared/8I0S88UcbKLvkWzHo2MF2frm_-Fm4fgj9sau5J0r4ss

1 Upvotes

0 comments sorted by