Skip to content

feat(ads): short impression codes so paid terminal ads fit the box - #176

Merged
ralyodio merged 1 commit into
masterfrom
feat/short-impression-codes
Jul 31, 2026
Merged

ralyodio merged 1 commit into
masterfrom
feat/short-impression-codes

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

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:

https://crawlproof.com/a/2b1f0c94-8a1e-4c3d-9b77-1f0a2c3d4e5f          61 chars
https://crawlproof.com/a/2b1f0c94-8a1e-4c3d-9b77-1f0a2c3d4e5f?s=motd   68 chars

A 44-col box has 40 usable columns, so this was always pushed outside the frame. Now 37 chars:

https://crawlproof.com/a/zCjQTqLAGEJJ

Two changes

1. A 12-character base62 code addresses the impression. The length is derived, not picked:

cols 44 → inner 40 usable columns
  − 25   "https://crawlproof.com/a/"
  =====
    15   columns available for the code   → 12, keeping 3 for a longer origin

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 plain byte % 62 would 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 for utm_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:

  • Both columns are additive, nullable, if not exists.
  • The insert retries without them when they're missing.
  • The click URL falls back to the UUID form when the code couldn't be stored.

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.sql needs 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 named crawlproof.com (ywcizjsgrcmhgyplldac). Worth resolving before you apply.

Verification

  • npm run typecheck clean.
  • Full suite 1251 passed, 7 skipped (1233 + 18 new).
  • New tests confirmed to fail on master — 6 of 18 with serve.ts and /a/[id] reverted.
  • Paid click URL verified inside the frame at cols 44/52/60/72/120, verbatim, nothing dangling.

🤖 Generated with Claude Code

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>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio marked this pull request as ready for review July 31, 2026 12:50
@ralyodio
ralyodio merged commit f36009a into master Jul 31, 2026
8 checks passed
@ralyodio
ralyodio deleted the feat/short-impression-codes branch July 31, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant