Course · Term 5 of 30
API Keys
Every lesson in this course builds one system: a stablecoin reserve attestation registry that discovers issuers, reads the PDFs they publish, and refuses to publish a field it could not read. What it is and where this lesson sits.
Short unit, short practice, and not optional here. This repo opens pull requests against a published registry. A leaked token with write scope on that repo is not an inconvenience, it is someone else editing the record of what issuers claimed about their reserves.
The mechanism
Three failures, in the order they actually bite.
A key in a log. The most common leak by far and the easiest to prevent. The
fix is that redaction happens at the log boundary, not the call site, because
every call site is somewhere a person can forget. src/keys.ts exports a log
that cannot print a credential:
KEY=[redacted:anthropic:33chars]
Keeping the length is deliberate. It stays debuggable, and a 33-character redaction next to an expected 108-character one tells you something.
A key in a commit. Rare per commit, catastrophic per occurrence, and
unfixable by deletion because git remembers. scanForSecrets() takes an explicit
file list rather than walking the tree, so the caller decides what "tracked"
means. Here it is git ls-files, which cannot be fooled by a working tree that
differs from the index.
A key with more scope than the job needs. The engine needs pull-request rights on one repository. It does not need, and must not have, merge. That one belongs to term 30, where the credential boundary is the teaching point, and it is named here so the reader knows it is coming rather than absent.
Build it
Seven patterns, deliberately conservative: Anthropic, OpenAI, both GitHub token shapes, AWS access keys, PEM private key headers, and long bearer values. A false positive costs someone thirty seconds. A false negative costs a rotation and an audit trail with a hole in it.
The receipt
artifacts/05-keys.json. A scan of ninety real tracked files finding nothing,
plus a negative control that plants a key and confirms it is caught.
The clean scan alone would be worthless. It looks identical whether the scanner
works or returns an empty array unconditionally. This project has now shipped
that exact failure three times: a PDF that dropped every glyph in its central
table and still exited zero, a regex that matched inside join(...) and silently
skipped process.cwd() calls, and an exposure guard that early-exited on the one
platform it was written for. The control is the whole point.
Nine tests, including a regression guard for the /g lastIndex bug that makes
a reused regex report every other match rather than every match. That one was
found by writing a test with three identical keys on three lines and expecting
three findings.
Where it overlaps
Line E is the tool surface and trust boundary, and a credential is the boundary in its most literal form. Line D is execution surface: which process holds the key decides where work can run, which is why term 18 revisits this the moment there is no human to approve anything. Line F is determinism: redaction is a deterministic wrapper around output that is otherwise whatever the model said.
A personal teaching project, in development. The registry it builds is a teaching artifact and is not an assurance opinion about any issuer. Not affiliated with or endorsed by any employer, or by any vendor whose tools it describes. Where a unit depends on a specific flag or path, it names the version it was verified against. All units.