Profile Access And Claims
Status Note
This doc captures the permission and claim-state baseline for #12 and #13, plus later auth, ownership, field-visibility, Discord claim, and VRChat proof-code slices.
It intentionally does not add moderation UI, role delegation, ownership transfer, contested-claim resolution, or a hard-coded VRCLinking API integration.
Read Baseline
- public users can read
publishedprofiles draft_privateprofiles are not public- claimed owners can read their own profiles regardless of publication state once ownership is modeled
- moderators can read profiles regardless of publication state once moderator authority exists
Edit Baseline
Ordinary public users cannot edit profiles.
Community submitters may populate only a narrow safe field set for unclaimed profiles through profiles:submitCommunityProfile:
displayNamealiasestagspersontype-specific fieldscommunitytype-specific fields
Community submitters must not set fields that imply verified authority, private contact details, billing state, ownership, custom slugs, or sensitive visibility choices. Profile creation can still generate an initial slug from submitted display text.
The current public mutation requires a Convex authenticated identity and stores source attribution. Freeform bios, about text, avatar URLs, banner URLs, private contact details, and custom slugs are intentionally outside the ordinary community-submission field set.
Claimed owners may edit normal profile fields after a claim attaches authority to the existing profile record. This baseline assumes claimed owners can edit identity, presentation, slug, tags, and type-specific profile fields, subject to future field-level visibility and abuse controls.
Moderators may override profile fields later for safety, corrections, and abuse handling. The moderation UI and detailed audit model are deferred.
Ownership Records
profileOwners records are the durable owner authority link between Convex Auth users and profiles.
roleKeyis currently the singleton literalowner- only one active owner may exist for a profile at a time
- repeated grants for the same active owner are idempotent
- grants to a different active owner must fail until a future transfer or moderation flow revokes the old owner
- claim approval must update the profile search document because trust rank and public trust labels can change
Claim States
claimState describes owner authority:
unclaimed: no owner authority is attached yetclaimed_unverified: a claimant controls the profile, but stronger verification is not completeclaimed_verified: owner control and stronger verification are both established
Claim transitions preserve the same profile record and slug. Claiming a profile should not create a duplicate identity record.
Allowed ordinary transitions are real state changes only:
unclaimed->claimed_unverifiedunclaimed->claimed_verifiedclaimed_unverified->claimed_verified
Downgrades, contested claims, transfer flows, and suppression flows require explicit moderation or ownership workflows later.
A weaker approval method must not downgrade an already verified profile. For example, a later Discord person claim leaves an existing claimed_verified profile verified instead of moving it back to claimed_unverified.
Claim Methods
Current claim-level actions require a signed-in Convex Auth user with a verified email address.
- Discord person claims require a linked Discord provider account and grant
claimed_unverifiedowner control for an existing person profile. - Discord community claims require a linked Discord provider account and create a pending
discord_community_adminrequest. The profile is not granted untilprofileClaims:verifyDiscordCommunityAdminClaimverifies full Administrator permission through a Discord bot token. - VRChat user proof requires a person profile and creates a proof-code attempt with
targetType: "vrchat_user". - VRChat group proof requires a community profile and creates a proof-code attempt with
targetType: "vrchat_group". - VRCLinking currently uses the same proof-code attempt table and adapter seam with
targetType: "vrclinking"until a stable API contract is confirmed.
The automated proof reader lives behind profileClaims:verifyVrchatProofViaAdapter. It calls VRCHAT_PROOF_ADAPTER_URL for VRChat user/group proofs and VRCLINKING_PROOF_ADAPTER_URL for VRCLinking proofs. The adapter receives the target type, target external id, proof code, and safe profile context, then returns whether the code was found plus an evidence summary.
Field Visibility
Profile field visibility supports three states:
public: visible on direct profile pages and eligible for discovery/search/card projectionsunlisted: visible on direct profile pages but omitted from discovery/search/card projectionsprivate: omitted from all public projections
displayName, slug, profileType, and trust labels remain public while the profile itself is public.
Trust Labels
Initial trust labels map from claimState plus creationSource:
community_submitted:claimState: "unclaimed"andcreationSource: "community"unclaimed: no owner authority has been attachedclaimed_unverified: owner control exists, stronger verification is pendingclaimed_verified: owner control and verification are established
These labels are business-logic helpers only. Final UI copy and visual treatment belong to public profile and trust-label issues.
Mutation Contracts
Future claim mutations must:
- validate allowed claim-state transitions
- handle no-op writes outside the claim transition helper
- set
claimedAtwhenclaimStateleaves"unclaimed" - preserve the profile
_idand slug when authority changes - patch
updatedAton every profile write - use
profileOwnersfor durable owner authority instead of interpreting provider login as ownership by itself