Skip to content

fix(sdk): include private flag in the signed user.profile payload#120

Merged
graycyrus merged 1 commit into
tinyhumansai:mainfrom
graycyrus:fix/user-profile-signature-private
Jun 18, 2026
Merged

fix(sdk): include private flag in the signed user.profile payload#120
graycyrus merged 1 commit into
tinyhumansai:mainfrom
graycyrus:fix/user-profile-signature-private

Conversation

@graycyrus

@graycyrus graycyrus commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Bug

Editing a profile and clicking Save fails with:

HTTP 401: /users/<cryptoId>/profile

Root cause

The backend's canonical user.profile payload — internal/users/handler.go userProfilePayload — signs over these fields:

actorType, avatarEmail, bio, cryptoId, displayName, harnessKey, link, private, tags

Both the TypeScript and Rust SDKs omitted private from the payload they sign. The client's canonical string therefore differed from the one the backend derives (which includes private, defaulting to null), so the freshness-bound signature never verified and the request was rejected as 401 Unauthorized. This broke all profile saves from the web app.

Fix

  • TS SDK — add private?: boolean to UserProfileUpdate, and private: update.private ?? null to userProfileSignaturePayload.
  • Rust SDK — add private: Option<bool> to UserProfileUpdate, and "private" to user_profile_signature_payload.

The signed field set now matches the backend exactly, so the signature verifies. Omitting private (as the web ProfileEditor does) signs it as null, which leaves the current value unchanged — matching the backend contract.

Tests

  • TS: a regression test that reconstructs the v1:<ts>:<nonce>:<sig> freshness signature from the request body and cryptographically verifies (ed25519) it covers a payload including private — so dropping the field again fails the test.
  • Rust: a unit test asserting the canonical payload carries "private":null by default and "private":true when set.

Scope

  • Python SDK is unaffected (it does not sign profile updates).
  • No website code change neededProfileEditor passes the update through unchanged; the web app picks up the fix when the SDK is rebuilt (Vercel builds the SDK first).

Validation

  • TS: tsc --noEmit ✅, full SDK suite ✅ (249 passed), website typecheck ✅ against the rebuilt SDK
  • Rust: cargo fmt --check ✅, cargo clippy --all-targets -- -D warnings ✅, cargo test

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an optional wallet-level privacy flag to user profile updates, available in both Rust and TypeScript SDKs. Users can now modify privacy settings independently without affecting other profile data.
  • Tests

    • Added test coverage to ensure the privacy flag is properly included in signed profile update requests and validated correctly.

Profile saves failed with HTTP 401. The backend's canonical user.profile
payload (internal/users/handler.go: userProfilePayload) includes a wallet-level
`private` field, but the TS and Rust SDKs omitted it from the payload they
sign. The signed canonical string therefore differed from the one the backend
derives, so the freshness-bound signature never verified and the request was
rejected as unauthorized.

- TS: add `private?: boolean` to UserProfileUpdate and `private: update.private
  ?? null` to the signed user.profile payload.
- Rust: add `private: Option<bool>` to UserProfileUpdate and `private` to the
  canonical payload.
- Tests: a TS test that reconstructs the freshness signature and cryptographically
  verifies it covers `private`, and a Rust unit test asserting the canonical
  payload carries the flag (null by default, true when set).

Python is unaffected (it does not sign profile updates). No website change is
needed — the ProfileEditor passes the update through unchanged; rebuilding the
SDK is sufficient.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

@graycyrus is attempting to deploy a commit to the Vezures Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1e060cdf-43e2-4210-94f2-9d56acfa1a76

📥 Commits

Reviewing files that changed from the base of the PR and between e8c1798 and a218765.

📒 Files selected for processing (5)
  • sdk/rust/src/api/users.rs
  • sdk/rust/src/types/user.rs
  • sdk/typescript/src/api/users.ts
  • sdk/typescript/src/types/user.ts
  • sdk/typescript/tests/users.test.ts

📝 Walkthrough

Walkthrough

Adds an optional private (wallet-level privacy flag) field to the UserProfileUpdate type in both the Rust and TypeScript SDKs. The canonical user.profile JSON payload used for signature generation is updated in both SDKs to include this field, mapping absent values to null. Regression tests are added to verify the field appears correctly in signed payloads.

Changes

Private flag in user profile signing payload

Layer / File(s) Summary
UserProfileUpdate type contracts
sdk/rust/src/types/user.rs, sdk/typescript/src/types/user.ts
UserProfileUpdate gains private: Option<bool> in Rust (serde-skipped when None) and private?: boolean in TypeScript, both representing the wallet-level privacy flag.
Canonical signature payload includes private
sdk/rust/src/api/users.rs, sdk/typescript/src/api/users.ts
Both UsersApi implementations add private to the canonical user.profile JSON payload used for signature generation, serializing an absent value as null.
Regression tests
sdk/rust/src/api/users.rs, sdk/typescript/tests/users.test.ts
Rust unit tests assert the default payload contains "private":null and that Some(true) produces "private":true; TypeScript test reconstructs the canonical payload, recomputes the signed message, and verifies the ed25519 signature covers private.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Possibly related PRs

  • tinyhumansai/tiny.place#32 — Both PRs modify the Rust UsersApi canonical user.profile signing payload in sdk/rust/src/api/users.rs, extending which JSON fields are included.

Poem

🐇 Hop hop, the payload grows,
A private flag now clearly shows.
No more mismatch, no more fright,
The signature verifies just right!
This bunny signs with joy tonight. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(sdk): include private flag in the signed user.profile payload' is directly related to the main change: adding the private field to the signed user profile payload in both TypeScript and Rust SDKs to fix signature verification.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@graycyrus graycyrus merged commit 7865ab1 into tinyhumansai:main Jun 18, 2026
7 of 9 checks passed
graycyrus added a commit that referenced this pull request Jun 18, 2026
Bump the crate version so the merge publishes to crates.io. The published
crate is stuck at 0.5.0: the auto-publish failed on the feeds (#100), bounties
(#101), and user.profile fix (#120) merges with 'crate tinyplace@0.5.0 already
exists on crates.io index' because the version was never bumped.

0.6.0 covers the new feeds + bounties API modules (minor) and the profile
signature fix, and aligns with the TypeScript SDK at 0.6.0.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant