Profile Slugs
Status Note
This doc captures the slug contract for #10.
Profiles use one global slug namespace across both people and communities, even though public pages are planned as /p/<slug> and /c/<slug>. This avoids ambiguous API, card, and search lookups.
Rules
- slugs are lowercase ASCII only
- allowed characters are
a-z,0-9, and- - length must be 3 to 64 characters
- leading hyphens are invalid
- trailing hyphens are invalid
- consecutive hyphens are invalid
- reserved slugs are invalid
- canonical slugs are independent from Discord, VRChat, Google, email, or any other login identifier
Reserved Slugs
The reserved list lives in convex/_profileSlugs.ts and protects current plus likely future public routes, including app routes, auth routes, profile collection routes, API surfaces, account/settings routes, and support/legal pages.
Generation
Initial slug generation starts from a display name or owner-provided text:
- normalize to lowercase ASCII
- strip combining marks
- convert non-alphanumeric runs to hyphens
- trim leading/trailing hyphens
- collapse repeated hyphens
- append and revalidate a safe suffix when the base would be too short or reserved
- trim and revalidate overlong bases
- append numeric suffixes such as
-2,-3, and later attempts when a slug is already taken
Uniqueness
Convex does not enforce unique indexes at the schema layer. Profile slug uniqueness is enforced by mutations using the by_slug index before insert or update.
profiles:submitCommunityProfile now creates initial public slugs for authenticated community submissions. Mutations that create or update profiles must:
- normalize and validate the candidate slug
- reject invalid or reserved slugs
- query
by_slugto reject collisions outside the current profile - patch
updatedAtwith the slug write
Out of Scope
- slug history and redirects
- custom domains
- SEO metadata
- public pages and API routes that consume slugs