domains: sqid — frozen alphabet, frozen goldens
Base-23 over abcdefghjkmnpqrstuvwxyz (no i/l/o), encoding id+23² so every id is ≥3 chars via one monotonic bijection — no padding case-split, no collisions. Goldens frozen from the first green run: ops (id 1) is "bab" forever.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+76
−0
internal/domains/sqid.go+34 −0internal/domains/sqid_test.go+42 −0
diff --git a/internal/domains/sqid.go b/internal/domains/sqid.go| new file mode 100644 |
| index 000000000..978adda17 |
| --- /dev/null |
| +++ b/internal/domains/sqid.go |
| @@ -0,0 +1,34 @@ |
| +// Package domains is the platform-domains machinery: the account-sqid |
| +// encoder and the agent's reconcile pass that mints platform domains, stamps |
| +// DNS/TLS health, and materializes bucket account manifests into the |
| +// registry. See docs/superpowers/specs/2026-08-02-custom-domains-design.md. |
| +package domains |
| + |
| +// sqidAlphabet is FROZEN FOREVER: lowercase base-23 with the lookalikes |
| +// i, l, o removed. Every platform domain ever minted embeds a sqid over this |
| +// alphabet — changing it (or the offset below) re-homes every one of them. |
| +// The golden test (sqid_test.go) exists to make that impossible to do by |
| +// accident. Deliberately not configurable: vanity account names in the |
| +// platform domain invite squatting; custom domains are the vanity mechanism. |
| +const sqidAlphabet = "abcdefghjkmnpqrstuvwxyz" |
| + |
| +// sqidOffset guarantees a minimum length of 3: encoding id+23² in plain |
| +// base-23 means every id ≥ 1 needs at least three digits, with no padding |
| +// case-split (a single strictly-monotonic bijection, so distinct ids can |
| +// never collide). Frozen with the alphabet, for the same reason. |
| +const sqidOffset = 23 * 23 |
| + |
| +// Sqid encodes an account's registry row id as its frozen domain label — |
| +// e.g. the built-in ops account (id 1) is Sqid(1) forever, on every |
| +// deployment. |
| +func Sqid(id int64) string { |
| + n := id + sqidOffset |
| + var buf [16]byte // 23^16 dwarfs int64; never grows |
| + i := len(buf) |
| + for n > 0 { |
| + i-- |
| + buf[i] = sqidAlphabet[n%23] |
| + n /= 23 |
| + } |
| + return string(buf[i:]) |
| +} |
diff --git a/internal/domains/sqid_test.go b/internal/domains/sqid_test.go| new file mode 100644 |
| index 000000000..0f9b4263f |
| --- /dev/null |
| +++ b/internal/domains/sqid_test.go |
| @@ -0,0 +1,42 @@ |
| +package domains_test |
| + |
| +import ( |
| + "strings" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/domains" |
| +) |
| + |
| +func TestSqidGolden(t *testing.T) { |
| + // FROZEN 2026-08-02 (first green run of the id+23² base-23 scheme). If |
| + // this test fails you are re-homing every platform domain ever minted — |
| + // ops's sqid is baked into every <app>.bab.<apps-domain> URL in the |
| + // wild. Do not "fix" the expectations; fix your code. |
| + golden := map[int64]string{ |
| + 1: "bab", // ops — the built-in account, on every deployment |
| + 2: "bac", |
| + 23: "bba", |
| + 24: "bbb", |
| + 529: "caa", |
| + 1000: "cxn", |
| + } |
| + goldenSeen := map[string]int64{} |
| + for id, want := range golden { |
| + s := domains.Sqid(id) |
| + if s != want { |
| + t.Errorf("Sqid(%d) = %q, want frozen %q", id, s, want) |
| + } |
| + if len(s) < 3 { |
| + t.Errorf("Sqid(%d)=%q shorter than 3", id, s) |
| + } |
| + for _, c := range s { |
| + if !strings.ContainsRune("abcdefghjkmnpqrstuvwxyz", c) { |
| + t.Errorf("Sqid(%d)=%q uses %q outside the frozen alphabet", id, s, c) |
| + } |
| + } |
| + if prev, dup := goldenSeen[s]; dup { |
| + t.Errorf("collision: %d and %d both %q", prev, id, s) |
| + } |
| + goldenSeen[s] = id |
| + } |
| +} |