Repository navigation
feat(ads): short impression codes so paid terminal ads fit the box - #176
Merged
Merged
Conversation
Follow-up to #175, which fixed the house ad's click URL but left the paid one. A paid terminal ad printed: https://crawlproof.com/a/<uuid> 61 chars https://crawlproof.com/a/<uuid>?s=motd 68 chars against the 40 usable columns of a 44-col box, so the URL was always pushed outside the frame. Two changes bring it to 37: 1. A 12-character base62 short code addresses the impression instead of its UUID. The length is derived, not picked: 40 usable columns minus the 25-character "https://crawlproof.com/a/" prefix leaves 15, and 12 keeps three columns of headroom for a longer origin while still being 71 bits. Generated with rejection sampling — a plain byte % 62 would have skewed the first four symbols and quietly cost entropy the width budget is already tight on. 2. The publisher's surface tag moves out of the URL and onto the impression row. As "&s=<tag>" it cost up to 35 more columns of a box that has 40; /a/<code> now reads it back off the row to build utm_content. It is also queryable there, which the query string never was. Entropy matters because the code is all that stands between a stranger and a click charge on someone else's campaign, so this is deliberately well above the ~42 bits a 7-character code would have given. Compatibility, both directions: - /a/<id> still accepts UUIDs. Click URLs printed before this change are sitting in people's MOTDs, SSH banners and BBS screens and are never reissued; they must keep resolving. The ?s= query form is still read as a fallback for the same reason. - Migrations here are applied by hand, so the deploy can land before the schema. Both new columns are additive and nullable, the impression insert retries without them if they are missing, and the click URL falls back to the UUID form. A wide URL is cosmetic; a dropped impression is a lost sale. Either apply order is safe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #175, which fixed the house ad's click URL but explicitly left the paid one. This finishes it.
The problem
A paid terminal ad printed:
A 44-col box has 40 usable columns, so this was always pushed outside the frame. Now 37 chars:
Two changes
1. A 12-character base62 code addresses the impression. The length is derived, not picked:
12 base62 chars ≈ 71 bits. That matters: the code is the only thing between a stranger and a click charge on someone else's campaign, since
/a/<code>meters against the campaign on the impression row. Deliberately well above the ~42 bits a 7-char code would have given. Generated with rejection sampling — a plainbyte % 62would skew the first four symbols and quietly cost entropy the budget is already tight on. (Verified: 200k codes, 0 collisions, all 62 symbols, max 1.21% deviation from uniform.)2. The surface tag moves onto the impression row. As
&s=<tag>it cost up to 35 more columns — a sanitized tag can be 32 chars, so no code length could have saved it./a/<code>reads it back off the row forutm_content, and it's now queryable for per-surface reporting.Compatibility — please check this part
Old URLs must keep working. Click URLs printed before this change are sitting in people's MOTDs, SSH banners and BBS screens and are never reissued.
/a/<id>still accepts UUIDs, and?s=is still read as a fallback.Deploy ordering. Migrations here are applied by hand (no CI step), so the app can land before the schema. If a naive version shipped first, every paid impression insert would fail on the unknown columns and take all paid serving down. So:
if not exists.Either apply order is safe. A wide URL is cosmetic; a dropped impression is a lost sale. There's a test for exactly this (
keeps serving when the migration has not been applied yet).Action required
supabase/migrations/20260731130000_ad_impression_short_codes.sqlneeds applying. I did not apply it — and note I couldn't confirm which Supabase project is production: impressions from live API calls don't land in the project namedcrawlproof.com(ywcizjsgrcmhgyplldac). Worth resolving before you apply.Verification
npm run typecheckclean.master— 6 of 18 withserve.tsand/a/[id]reverted.🤖 Generated with Claude Code