Repository navigation
Conversation
Record old usernames and Space slugs in slug_history so old public URLs resolve to the current owner and Space in one step, and retired slugs are never reassigned. Username renames are limited to once per 7 days. Cohub Session: https://cohub.live/spaces/5089f055-9dbb-4fcd-b98c-fb232353281a/sessions/7e36f245-32c7-40bd-b8fa-15e1b67e55c2
The Space page renders client-side, so its server load never ran for the document. Resolve retired usernames and Space slugs in handle() for document requests instead, and follow redirectTo in the client load so in-app navigation adds no __data.json request. Cohub Session: https://cohub.live/spaces/5089f055-9dbb-4fcd-b98c-fb232353281a/sessions/7e36f245-32c7-40bd-b8fa-15e1b67e55c2
Old slugs are no longer reserved: the live holder always wins, and only an unclaimed old slug redirects, following whoever gave it up last. History indexes become non-unique, and redirects are cached for at most 60 seconds. Cohub Session: https://cohub.live/spaces/5089f055-9dbb-4fcd-b98c-fb232353281a/sessions/7e36f245-32c7-40bd-b8fa-15e1b67e55c2
Old names can be claimed at any time, so 308s to the current address are sent with Cache-Control: no-store and the next request after a claim resolves to the new holder. Cohub Session: https://cohub.live/spaces/5089f055-9dbb-4fcd-b98c-fb232353281a/sessions/7e36f245-32c7-40bd-b8fa-15e1b67e55c2
This branch has not been deployed
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.
Routed task
cohub-slug-history-redirect. The brief named no GitHub issue.When a user or Space slug is renamed, the old public address redirects to the new one. Old names are not reserved, the same as on GitHub. Anyone can claim one at any time, and from then on the address belongs to the new holder.
Summary
v2.slug_historyrecords every old username and Space slug. Public lookups resolve in two steps, and the current holder always wins:Indexes on
slug_historyare plain, not unique: oneold_slugcan belong to several former holders in turn. They are(entity_type, old_slug, created_at desc),(entity_type, owner_uuid, old_slug, created_at desc)and(entity_type, entity_id, created_at desc), the last one for the rate limit.Old names are claimable
/alice/...→ 308 to/alice3/...alice/alice/...is bob's. No redirect, even though history still hasalicealice→bobby/alice/...→ 308 to/bobby/.... The most recent former holder wins (created_at DESC)Redirects are never cached. A 308 is normally cached by browsers indefinitely, and the target here changes when someone claims the old name. Every 308 is sent with
cache-control: no-store, the same as GitHub, so the first request after a claim already resolves to the new holder.Home
ensureHomeSpacere-creates Home on demand under the fixed slughome. That needs no special case anymore: the live Home always wins, and after a rename/owner/homefollows the most recent former Home until a new Home exists.Evidence
apps/api/tests/slug-history.integration.tsruns on PGlite with the real migrations and a mocked Logto. It covers:UsernameChangeTooSoonErrorand adds no row; the first rename is free.aliceand holds it with no redirect. After bob renames,aliceredirects to bob, the latest former holder.vite dev:GET /olduser/oldslug?session=abc(document) →308,location: /newuser/newslug?session=abc,cache-control: no-storeGET /olduser/oldslug/w/myapp?cohub_v=2&x=1→308,location: /newuser/newslug/w/myapp?cohub_v=2&x=1,cache-control: no-store/olduser/oldslug?session=1lands on/newuser/newslug?session=1.history.lengthgoes up by one and no__data.jsonis requested.apps/api: tsc, biome,pnpm test,db:check-driftpackages/sdk: tsc, biome, testsapps/web: typecheck, biome,pnpm build,pnpm testCost
/:username/:spaceSlugcosts one lightweight resolve request (worker →/api/spaces/by-slug) before the shell is returned. The(app)group renders client-side (ssr = false), so the 308 has to come fromhooks.server.ts.__data.json.+page.tsreadsredirectTofrom thegetBySlugcall it already makes, insideuntrack, so query-only navigations don't rerun it.redirect()instead ofgoto(…, { replaceState: true }): during a navigation,redirect()continues the same navigation and the old URL never enters history. AgotowithreplaceStateinside a load would replace the previous page's history entry.Known gaps
redirectTois attached. These open in client mode at the old URL, without a 308.redirectTo, socreateSlugAppIdResolveralready reads the target app's id.AppPublishDialogstill shows raw English API errors, the same as before this change.Defaults I added (L007)
SELECT … FOR UPDATEper profile syncSELECT … FOR UPDATEper slug change/api/apps/by-slug/api/users/by-usernamesendsCache-Control: no-storeon redirected responsesmax-age=60, stale-while-revalidate=300could keep serving a redirect after the old name has been claimed, unlike the web 308sidPK and anentity_typeCHECKNo retries, TTLs, grace periods or migration guards.
Merge Danger
Door: two-way. Migration
0084_slug_historyis new in this branch and was rewritten in place, from unique to plain indexes. It has not run in any shared environment (deploy-backendonly runs onmain). A local database that applied an earlier version of 0084 needsDROP TABLE v2.slug_historybefore migrating.Blast Radius: username and Space slug writes (
/api/me/profilesync now runs in a transaction), public slug lookups, and the document load of the Space pretty-URL page (one extra API request in the web worker).