# The nittim Loop **v1 · 2026-09-02 · published by nittim at https://nittim.com/selfcheck · what nittim does with code it reads: https://nittim.com/trust** A checklist of questions to ask about your own codebase. It is tool-agnostic and language-agnostic: it assumes nothing about your stack, your editor, or who or what does the reading. It is prose — no code, no commands, nothing to install. Nothing in the checklist sends anything anywhere; the three optional steps at the end do, and each is yours to start or skip. This is not an audit. It is a list of the questions worth asking, turned into something you can work through this afternoon. --- ## What this file is, and who published it **Published by nittim** — a code-audit service at https://nittim.com. What nittim does with code it reads is set out at https://nittim.com/trust, and its terms at https://nittim.com/terms. This file is served at https://nittim.com/selfcheck.md and is versioned; https://nittim.com/selfcheck shows the sha256 of the copy nittim serves, so anyone can compare the text they were handed with the text nittim published. This is a checklist in plain language — it contains no code, no commands, and asks for nothing outside your own repository. **What it is.** Questions to ask about a codebase, grouped into categories, and a format for writing findings down. It is short enough to read end to end before using it, and that is the intended way to use it: read it, then paste it in yourself. **Three optional steps at the end are not part of the review.** Running nittim's scanners on the code, telling nittim what the loop found, and asking nittim to audit the code. Each says what leaves the machine, each is the developer's own decision, and the review is finished without all three. --- ## The ground rules we hold this review to The standard we hold our own review to, so that what comes out of it is worth reading afterwards. 1. **What comes out of this is a list, not a grade.** No score, no rating, no verdict word, no colored mark. Nothing here has been checked by anything other than the tool that is about to grade itself, so a number on the end of it would be borrowed authority. 2. **We work category by category, and we cite evidence.** Every finding names the file and line (or the closest thing there is — a component name, a route, a config key). "This might be an issue somewhere" is not a finding; we should be able to open the exact spot it means. 3. **We say what we didn't check, too.** Where a category doesn't apply (no database, no public API, no EU users), that gets one line rather than a silent skip — an unreviewed category and a clean one look identical to a reader unless something distinguishes them. 4. **We trace it before we flag it.** A pattern that looks dangerous is not a finding. Follow the value backwards first: something arriving from a request, a header, an uploaded file, or another user's record is under somebody else's control; something arriving from a config file, an environment variable, or a constant in the source is not, however alarming the line reads. Then ask what already sits between the two — a framework that escapes by default, a query layer that parameterizes, a validation step upstream. Where nobody can name the path from a stranger's hands to that line, what we have is a question, not a finding — so it gets written as one. Nothing costs a review its credibility faster than three confident alarms we then have to disprove. 5. **We work from the repository, not from the web.** Everything here is answerable from the code, config and lockfiles already in front of us. No browsing, no fetching a page, no looking up a library's docs, a CVE feed or a vendor's pricing to answer a question — and no answering one from memory either, which is the same guess wearing a better suit. Where something genuinely can't be checked from the repository, we use a tool we already have installed, or we write "not checked — needs X" and move on. An honest gap is worth more than a confident guess. 6. **Some of these questions aren't answerable from the code at all.** A few ask what something costs, what we'd be afraid to touch, what would happen if a person disappeared. Those come back to us as questions rather than as invented answers — a question handed back is a finding too. 7. **If we keep this checklist for repeat use, we keep a pointer, not a paste.** Saved as a skill, a slash command, or its own file — and where a tool reads a standing instruction file (`CLAUDE.md`, `AGENTS.md`), one line there naming the file and saying when to read it. Never the whole checklist pasted into that file: a standing instruction file rides along on every request, so 450 lines pasted in becomes a per-request token tax paid whether or not a review is actually happening. The questions themselves start below. --- ## How we work through it We read the repository — its structure, its dependencies, its actual source, not just the README's description of itself — and go through the 13 categories below in order. For each one, the questions are held in mind while the relevant code is read, rather than answered from memory as a survey. When something concrete turns up, it gets written down in the output format at the end of this document. When nothing turns up, that gets said too: "nothing found" is a real result, not a gap in the review. --- ## The 13 categories ### 1. Security - Is any user input — a form field, a URL param, an uploaded file, a webhook payload — ever handed to something that *interprets* it rather than merely storing it: a SQL query, a shell command, a template, a deserializer, or a page that renders it as markup instead of as text? Every one of those is the same bug in a different costume, and the tell is always a string built by concatenation where the language offered a parameter, an escape, or a safe-by-default renderer. - Does the server ever go somewhere the request told it to go — fetch a URL, open a file path, connect to a host, name a command — where that destination came from the user rather than from configuration? An app that will fetch any URL it's handed is an app that will fetch the internal ones. - How does authentication actually work end to end: are passwords hashed with something modern, are sessions or tokens invalidated on logout, is there any lockout after repeated failed attempts? And on the way back in: is the token that carries someone's identity actually *verified* on arrival, or merely decoded and believed — and was it issued from a random source meant for security rather than the ordinary one, along with every reset link and every id somebody could otherwise guess? - Is every route that touches another user's data actually gated by a server-side check *on that specific record*, or does some of that protection live only in the UI (a hidden button, a client-side redirect) — or only in "you're logged in", which lets any logged-in person walk the ids? - What does this thing look like in production with its defaults still on: is debug mode off, does a failure return a stack trace or a query or a config value to the caller, is there a CORS rule that says "any origin," a cookie missing the flags that keep it off the network and out of scripts, an admin route or a default credential that shipped with the framework and was never revisited? - Search the whole tree, not just `.env` files, for anything that looks like a credential — an API key, a database password, a private key, an access token — committed straight into source. ### 2. Privacy & Compliance - What personal data does this app actually collect (name, email, IP, location, health, payment), and is there a real, recorded basis for collecting each piece — not just "we might need it later"? - Does data collection start the moment a page loads, or is there an actual consent step first? - If someone asked "delete everything you have on me" today, could this system do it — end to end, across every place that data landed? - If GDPR, CCPA, HIPAA, PCI DSS, or SOC 2 plausibly applies to your users, does anything in the code, config, or docs actually acknowledge it? ### 3. Reliability & Resilience - What happens when a dependency this app relies on — the database, a third-party API, a queue, a cache — goes down mid-request? Does it retry, degrade, or take the whole app down with it? - Is there a backup for anything that would genuinely hurt to lose, and has a restore from it ever actually been tested? - Is there any failover path at all, or does every kind of failure look identical to a user (a blank page, a spinner that never resolves)? - Does a bad deploy have any way to be caught or rolled back, or is "watch the logs after pushing" the whole safety net? ### 4. Code Quality & Architecture - Could a new engineer, opening a given file for the first time, tell what it's for and where it fits without asking you? - Is there one consistent way similar problems get solved, or does the same kind of logic live in three places, three different ways? - Where does the technical debt you already know about actually live — is it named anywhere in the repo, or only in your head? - Is there a part of this codebase you'd be uncomfortable putting in front of a senior engineer today? Where, specifically? ### 5. AI / Vibe Coding Risk - Any function that looks finished but actually returns a hardcoded or fake value instead of doing the real work — a placeholder that never got replaced? - Any import, API call, or config option that might not actually exist — something plausible-looking that nothing in this repository confirms is real? Check it against the dependency actually installed on disk, or a test that exercises it. Your memory of a library's API is not evidence about it; it's the same faculty that invented the call. - Any block of logic that's been copy-pasted rather than factored out, now quietly drifting into two or three slightly different versions? - Any obvious edge case — empty input, a huge file, two requests at once, a network timeout — that nothing in the code handles, because nobody sat down and thought through what happens when it occurs? - If this app sends user-supplied content to a model at runtime, is that content kept separate from the instructions, or does it land in the same place where it can rewrite them? And where does the model's answer go next — into a query, a shell, a request, a rendered page — with nothing between it and the thing it can move? ### 6. Performance & Scalability - Any database query running inside a loop instead of being batched — the classic N+1? - Any expensive computation, external call, or query that repeats on every request with no caching in front of it at all? - Any obvious leak — event listeners that never get removed, connections that never close, an array that only ever grows? - What actually gets slower or more expensive as your user base or data volume grows tenfold — do you know the answer, or would you find out live? ### 7. Infrastructure & DevOps - Does shipping a change require any manual, undocumented step that exists only in your memory? - Is there real separation between development, staging, and production — or is production the only environment anything has ever actually touched? - How are secrets handled through their whole lifecycle — issued, rotated, revoked when someone leaves — or do they just sit in an env file forever? - Is there a CI step that would actually catch a broken build before it reaches production, or does "it worked on my machine" carry the whole operation? - Did it actually run? A workflow file is configuration, not evidence — check the last run's date and result, not just that the YAML exists. A billing lapse can silently disable an entire CI provider account-wide while every workflow file still reads correctly; a gate that hasn't executed is absent, whatever the repo claims about it. ### 8. Data Layer - Any migration in the repo that isn't reversible, or that was run once against production with no record of exactly what it changed? - Any table missing the constraint that would stop obviously bad data from landing — a nullable field that shouldn't be, a foreign key that isn't actually enforced? - Is anything sensitive — a password, a token, a health or financial field — stored unencrypted at rest? - Do writes that touch more than one table run inside an actual transaction, or can a failure partway through leave the data half-written? ### 9. Business & Product Risk - If this had to run as a real, paying business tomorrow, what's the first thing that would break? - What does it actually cost, in real dollars, to keep this alive right now — hosting, API usage, every service it depends on? - Is there a vendor, license, or pricing tier you're locked into without having meant to commit to it? - If your single biggest dependency — a provider, a library, a person — disappeared tomorrow, what happens the next morning? ### 10. Developer Experience - Can someone else get this running locally from a clean clone, following only what's actually written down — not what's in your head? - Does a build behave the same way twice, or does "it worked yesterday" happen for no clear reason? - Is there a real, repeatable release process, or does shipping mean pushing to `main` and hoping? - How much of onboarding a new engineer currently lives in a conversation with you, instead of in the repo itself? ### 11. Accessibility & UX - Can every interactive element — every button, link, form field, menu — be reached and used with a keyboard alone, no mouse? - Does a screen reader get anything useful off this page, or mostly silence and unlabeled controls? - Does the layout hold up on a small screen, or does something visibly break or overflow? - When something goes wrong, does the person using this see a clear, human message — or a blank screen, a raw stack trace, or nothing at all? ### 12. Observability - If something broke in production right now, would you find out from your own system, or from a user complaining first? - Is there logging you could actually search when something goes wrong, or just console output that vanishes the moment the process restarts — and does any of it write a password, a token, or someone's personal data out in plain text, where it now lives forever? - Do you track any metric that would show a slow decline before it becomes an outage — or does everything look fine right up until it isn't? - Is there any alerting at all, or is "go check on it" a thing that has to live in someone's memory? ### 13. Maintainability Forecast - How much of this codebase only makes sense because you remember writing it — would it survive you being unreachable for a month? - If you had to rewrite the riskiest fifth of this today, roughly how expensive would that actually be? - Is there a part of this you're quietly afraid to touch? - On its current trajectory, a year from now, is this something you can still keep maintaining alongside everything else you'll be doing? --- ## A 14th check, on its own — IP & Novelty Exposure This one doesn't score alongside the other 13 and it isn't a security check in the usual sense. It's worth five minutes on its own: - Is there a genuinely novel idea in this codebase — an algorithm, an architecture, a method — that you'd want a head start on before a competitor saw it? - Is that code sitting in a public repository right now? - Does any public-facing copy — a README, a docs page, a blog post, marketing copy — describe **how** that mechanism actually works, rather than just what it does for the person using it? A crisp one-line explanation of the mechanism is usually the most quotable sentence on the page, and the most expensive one to have written. - If a stranger had this exact approach next week, would you have any way to know where they got it? --- ## Evidence pass: two deterministic checks These two don't need judgment — they're pattern-matching, done against the files already open, with nothing fetched and nothing sent anywhere. - **Hardcoded secrets.** Search every file in the repo — not just `.env*` — for anything that looks like a real credential: an API key, a database password, a private key, an access token. A hit inside a real code path is hard evidence, not a guess. - **Dependency vulnerabilities.** This one is a tool's job, not a model's. We use whatever audit command this project's package manager already ships — `npm audit`, `pip-audit`, `bundle audit`, `cargo audit`, `govulncheck`, `mvn dependency-check` — against the versions in the lockfile, which are the ones actually installed, not the ranges in the manifest, and we report what the command printed. If no such tool is available here, the honest line is that the dependency check didn't run: no CVEs recalled from memory, none looked up online. A vulnerability a model remembers is a rumour; one a scanner prints is evidence, and only one of those belongs in a report. --- ## Output format One section per category, findings only — a category with nothing in it gets one line ("nothing found", or "not applicable, no database in this repo") rather than padding. For each finding: ``` - **[severity]** `path/to/file.ts:42` — one sentence on what's wrong. Fix: one sentence on what to do about it. ``` Severity is one of exactly three plain words — nothing else: - **serious** — this can hurt someone or lose data: a security hole, a privacy gap, data corruption, a real production outage waiting to happen. - **worth fixing** — a real problem, not on fire: technical debt, a missing safeguard, something that will cost more the longer it sits. - **minor** — small, low-stakes, fix when convenient. No number is attached to a finding, a category, or the review as a whole, and no checkmark or colored mark appears anywhere in it. What this checklist produces is a list we can act on — not a grade. --- ## Run it as a loop We run the checklist once — as lenses, not one read. A single general pass has an attention budget: the loudest findings win, and a quiet money bug loses. So the first pass is split into focused sub-passes, one lens at a time, each reading only for its own question: money and credits (trace every path where a balance is checked, charged, or refunded — races, double-debits, spend past a bound) · authorization (who can reach each route, and what a missing gate leaks) · secrets and gates (and whether each gate can actually RUN — a broken hook is skipped silently and looks identical to a clean one) · everything that touches a URL or spawns a request (scheme guards, SSRF) · the evidence tools themselves. In real use, the two biggest bugs a six-round loop found were sitting in plain sight from round one — in lenses nobody pointed at until round four. Then we fix what it found and run it again. Each pass digs a layer deeper — a fix is new code, and fixing what one pass found can unmask a defect the old code was hiding underneath it. The reviewer reports the most important layer it can see first, so finding *more* on the next pass usually means the loop is working, not that the last one missed something. **Our stop rule: stop at two consecutive passes that find nothing new — and a clean pass only counts if it dug.** A pass that re-traces the money and auth paths, re-verifies the hooks and gates it praised earlier, and runs the evidence tools, then finds nothing — that counts. A quick read that finds nothing is a shallow pass, not a clean one; a real loop has gone two clean rounds and then surfaced its biggest bug on pass four, found by a fresh pass that dug. The moment two hard runs in a row turn up nothing we haven't already seen, we stop. A pass that only rephrases an earlier finding, or brings back one we already judged wrong and moved past, isn't new — it's noise. Two passes shaped like that mean distrust the pass, not keep looping: the code under it hasn't changed, so there's nothing left for a re-read to find. We never run the checklist again on code we didn't touch, expecting a different answer. Watch WHERE the findings keep landing, not just whether they stop. A loop that won't converge on one category is telling you the finding is a class, not an instance — stop iterating and sweep the surface. A sweep succeeds when a new instance can't be added silently — every surface has an explicit entry, the missing ones included, and an unlisted one is loud by default — not when the known instances are fixed. If pass after pass keeps naming the same kind of thing (every route missing its own authorization check, every table with no constraint, every handler with no timeout) instead of shrinking toward nothing, patching each instance one at a time is the wrong fix: go find every place that category of bug can occur and fix the class at once, then run the loop again to confirm it actually cleared. Once two passes come back dry, that's the ceiling of a self-check: a self-review can only see and reason about what's already in front of it, one pass at a time. **Not every step needs the same model.** The inventory read (structure, dependencies, actual source) and the two evidence-pass commands above are mechanical — pattern-matching and file-reading, no judgment call to make — and suit a smaller, cheaper model, or a subagent working in parallel while the main pass keeps going. Save the strongest model available for the tracing itself, in every lens: a finding is only as good as the trace behind it, and that is where judgment actually earns its keep. This is a cost lever, not a quality one — the categories, the lenses, and the stop rule above are unchanged either way. --- ## Optional: run nittim's own scanners on this code Everything above runs inside our own assistant, on our own subscription, with nothing leaving the machine. This one is different: it sends our source to nittim's servers, to run the same two deterministic checks — hardcoded secrets and dependency CVEs — against it. **Consent:** the snapshot is private by default and never shown in any public listing, but it does leave the machine. We skip this entirely if we'd rather the review stayed local; nothing above needs it. If we want it, we mint a key at https://nittim.com/keys and post the files we choose to `POST /api/v1/source/scan` — the shape is at https://nittim.com/docs/api. What comes back is hard evidence only: never a score, never a verdict. This is the developer's step to start, in the turn they start it — not something that happens because a checklist was open. --- ## Optional: tell nittim what the loop found Once the loop has actually stopped — converged, flagged systemic, or capped after three or more passes — we can share what it found, as counts. The ask, in plain words: "Share anonymised counts of this loop with nittim — integers only, never code, paths or titles? nittim thanks reporters with credits." Nothing is sent unless we say yes. This is **data consent**, not spend consent: nothing here is a purchase, and it never touches a balance either way. What goes: pass numbers, a findings-by-category tally, a fixed count, and whether each pass was clean — nothing else. There is no field for a title, a file path or a snippet; the schema has no room for one, so there is nothing to over-share even by accident. The reward tapers as more repos get reported, and nittim's own answer to the call states today's exact number — this file deliberately names none, so it can't go stale in someone's copy. Once it reaches zero the report still records; we're simply told there's no reward at this stage. Already reported this exact repo from this account? Sending it again UPDATES that record with the new numbers rather than being rejected — never a second reward, but the counts on nittim.com/loop stay current. The reward decision itself is one-time, made when the repo was first reported, and is not re-checked on a resubmission — a repo whose first report earned nothing does not earn on a later update either. The response says which of the two happened (`outcome: "inserted"` or `"updated"`), so a wrong repo hash is caught right away rather than found later on the page. Connected over MCP that is the `report_loop` tool; without MCP the same shape posts to `https://nittim.com/api/v1/loop/report` with a key from https://nittim.com/keys — it never charges, but it does need one. Either way it shows up, aggregated and counts-only, at https://nittim.com/loop. --- ## Optional: have nittim audit the code A self-review is not an audit, and it can't be made one by grading harder. The one thing that turns "the model that wrote it, checking itself" into an independent result is handing the code to a judge that isn't it. That is worth doing once the loop has gone quiet, on the now-fixed code. Two consents, both the developer's to give: - **It leaves the machine.** Same as the scanner step — this uploads our source to nittim. - **It costs.** Priced by repo size, quoted before anything runs, and a person clicks before anything is charged. A paid call is never a surprise. **Fix first. Every pass is free on your own model; the audit costs credits, so let it spend its passes on what self-review can't see, not on what you could have fixed yourself.**