Context
abuse.service.ts already reads users.phone_hash to block related-account abuse (assertNotRelatedAccounts — two accounts sharing the same phone_hash can't trade with each other). POST /users/register (routes/users.ts) already accepts an optional phone_hash and stores it if provided.
The problem: no frontend flow ever sends one. registerUser() (services/api.ts:126) only posts { username, stellar_address, challenge, signature } — no phone number, no hash. Register.tsx never asks for a phone number at all. So this anti-abuse control has been dormant since it was built (#82) — every user registers with phone_hash = NULL, and #314's "[4a] Tiered KYC Gate Engine" repeated the same "populate phone_hash" acceptance criterion assuming it was a free, already-wired activation. It isn't: the backend plumbing exists, but there's no data source feeding it yet.
What this issue proposes
Add a phone number step to registration and hash it client-side before sending, so phone_hash actually gets populated going forward.
- Add a phone number input to
Register.tsx (or wherever the onboarding flow collects username today) — basic format validation, no OTP/SMS verification required for this issue (that's a separate, bigger scope)
- Hash the phone number client-side (same approach the codebase already documents for
phone_hash: a stable one-way hash, not the raw number) before calling registerUser()
- Extend
registerUser() in services/api.ts to accept and send the computed phone_hash in the existing POST /users/register body field (no backend change needed — it already accepts this)
- Decide and document: is the phone number field required or optional at registration? (Recommendation: optional for now, since making it mandatory is a product/legal decision beyond this issue's scope — but even optional collection is strictly better than the current zero.)
Acceptance criteria
Out of scope
- SMS/OTP verification of the phone number (this issue just collects + hashes it; verifying ownership is a separate, larger feature)
- Backfilling
phone_hash for existing registered users (out of scope — this issue is forward-looking only)
- Making the phone field mandatory (product/legal decision, not this issue's call)
Related
Context
abuse.service.tsalready readsusers.phone_hashto block related-account abuse (assertNotRelatedAccounts— two accounts sharing the samephone_hashcan't trade with each other).POST /users/register(routes/users.ts) already accepts an optionalphone_hashand stores it if provided.The problem: no frontend flow ever sends one.
registerUser()(services/api.ts:126) only posts{ username, stellar_address, challenge, signature }— no phone number, no hash.Register.tsxnever asks for a phone number at all. So this anti-abuse control has been dormant since it was built (#82) — every user registers withphone_hash = NULL, and #314's "[4a] Tiered KYC Gate Engine" repeated the same "populate phone_hash" acceptance criterion assuming it was a free, already-wired activation. It isn't: the backend plumbing exists, but there's no data source feeding it yet.What this issue proposes
Add a phone number step to registration and hash it client-side before sending, so
phone_hashactually gets populated going forward.Register.tsx(or wherever the onboarding flow collects username today) — basic format validation, no OTP/SMS verification required for this issue (that's a separate, bigger scope)phone_hash: a stable one-way hash, not the raw number) before callingregisterUser()registerUser()inservices/api.tsto accept and send the computedphone_hashin the existingPOST /users/registerbody field (no backend change needed — it already accepts this)Acceptance criteria
Register.tsxcollects a phone number during registrationregisterUser()passes the computedphone_hashthrough toPOST /users/registerassertNotRelatedAccountsabuse check (abuse.service.ts) starts seeing non-nullphone_hashvalues for new registrations — verify with a manual test: two new accounts using the same phone number are blocked from trading with each othertsc --noEmitpasses on the frontendOut of scope
phone_hashfor existing registered users (out of scope — this issue is forward-looking only)Related
kyc-gate.service.ts's companion commit6a2892f) and indocs/GRANTFOX_KYC_QUEUE_2026-07.mdabuse.service.ts—assertNotRelatedAccounts(the dormant control this activates)services/api.ts:126,143—registerUser(), the call site to extend