Skip to content

fix(wallet): mint slash-free tiny.place identity keys#4779

Open
CodeGhost21 wants to merge 1 commit into
tinyhumansai:mainfrom
CodeGhost21:fix/tinyplace-slash-free-identity
Open

fix(wallet): mint slash-free tiny.place identity keys#4779
CodeGhost21 wants to merge 1 commit into
tinyhumansai:mainfrom
CodeGhost21:fix/tinyplace-slash-free-identity

Conversation

@CodeGhost21

@CodeGhost21 CodeGhost21 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Mint slash-free tiny.place identity keys so newly-derived identities can actually publish Signal pre-keys and receive DMs.
  • Derive the identity seed through slash_free_identity_seed() at the point the seed is handed to LocalSigner.
  • counter 0 == the raw wallet seed, so every already-clean identity is byte-identical — no migration for the common case.
  • Only slash-bearing raw keys are remapped, deterministically, via a domain-separated SHA-256 counter.

Problem

The tiny.place backend serves the Ed25519 identity public key as the Signal bundle identityKey in STANDARD base64 and keys its /keys/{identityKey}/… routes on that string verbatim. Standard base64's alphabet includes /, which becomes %2F in the request path — and those key routes 404 on it. So an identity whose public key's base64 contains a / cannot publish its Signal pre-keys and cannot receive DMs.

Our tiny.place identity is deterministic from the Solana wallet key, and roughly half of all Ed25519 public keys contain at least one / in standard base64. So ~half of new wallets mint a silently-unusable tiny.place identity (bundle publish + inbound DM both 404). This is the same class of failure the plugin-tinyplace SDK already sidesteps by regenerating slash-free wallets.

Solution

tinyplace_signer_seed() now returns slash_free_identity_seed(signing_key.to_bytes()):

  • counter 0 = the raw wallet seed. If the raw key's base64 is already slash-free (the common case), it passes through byte-identical — no identity change, nothing to migrate.
  • Only when the raw key's base64 contains / do we deterministically derive a fresh seed from SHA-256("openhuman.tinyplace.identity.slashfree.v1" ‖ seed ‖ counter), bumping counter until the resulting public key is slash-free. Same wallet always maps to the same lowest-counter slash-free identity.
  • Astronomically-unlikely all-slash exhaustion falls back to the raw seed rather than panicking.

The tiny.place plugin regenerates slash-free wallets; here the identity must remain a pure function of the wallet key, so we remap the seed deterministically instead.

Submission Checklist

  • Tests added or updated — slash_free_identity_seed_cleans_slash_keys_deterministically covers the clean-passthrough path (counter 0, unchanged) and the slashed→remapped path (slash-free + deterministic).
  • Diff coverage ≥ 80% — N/A to run full merged report locally; the added fn + its unit test are fully exercised (both branches). Flagging for the coverage gate to confirm.
  • Coverage matrix updated — N/A: internal key-derivation fix, no user-facing feature row.
  • No new external network dependencies introduced — N/A, uses already-imported sha2 / ed25519_dalek / base64.
  • Manual smoke checklist updated — N/A: no release-cut surface touched.
  • Linked issue closed — no dedicated issue; surfaced while wiring the Cursor/Windsurf harness observer against staging.

Impact

  • Runtime: desktop/CLI wallet identity derivation only.
  • Security: domain-separated SHA-256; the remapped seed is still 32 bytes of Ed25519 secret material. No key logged (existing debug log omits the seed).
  • ⚠️ MIGRATION CAVEAT (needs a rollout decision): for a wallet whose raw key's base64 contains /, this changes that wallet's advertised tiny.place identity to a different (slash-free) key. In practice those identities were already non-functional — they 404 on bundle-publish and on inbound DMs — so there is no working relay state to preserve, and remapping is strictly a repair. But if any such identity was partially advertised (e.g. published out-of-band, printed in a UI, or saved by a peer), those references will no longer resolve after upgrade. Recommend the team confirm no persisted slash-bearing identities need an explicit migration/announcement before merge. Clean identities (counter 0) are unaffected.

Related

  • Closes: (none — no tracked issue; discovered during the Cursor/Windsurf harness observer spike)
  • Follow-up PR(s)/TODOs: base58↔base64 reply-address unification (OpenHuman→runtime reply path), tracked separately.

AI Authored PR Metadata

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/tinyplace-slash-free-identity
  • Commit SHA: 010c364

Validation Run

  • pnpm --filter openhuman-app format:check — N/A (no app/ changes)
  • pnpm typecheck — N/A (no TS changes)
  • Focused tests: cargo test -p openhuman --lib slash_free_identity_seed_cleans_slash_keys_deterministically → 1 passed
  • Rust fmt/check (if changed): cargo fmt --check -- src/openhuman/wallet/chains/solana.rs → clean
  • Tauri fmt/check (if changed): N/A

Behavior Changes

  • Intended behavior change: newly-derived tiny.place identities are always slash-free and therefore able to publish pre-keys / receive DMs.
  • User-visible effect: none for clean identities; for slash-bearing wallets, the tiny.place identity (not the Solana wallet address) changes to a working one.

Parity Contract

  • Legacy behavior preserved: clean identities are byte-identical (counter 0 == raw seed).
  • Guard/fallback/dispatch parity checks: deterministic remap; bounded loop with raw-seed fallback.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Solana wallet identity seed handling to avoid problematic slash characters in encoded identity keys.
    • Preserved existing seeds when no adjustment is needed and ensured deterministic results when remapping is required.

The tiny.place backend serves the Ed25519 identity public key as the
Signal bundle `identityKey` (STANDARD base64) and keys its
`/keys/{identityKey}/...` routes on it verbatim. A `/` in the base64
becomes `%2F` in the request path and the route 404s — so an identity
whose public key contains `/` can neither publish its Signal pre-keys
nor receive DMs. Our tiny.place identity is deterministic from the
Solana wallet key, so ~half of new wallets mint an unusable identity.

Derive a slash-free identity seed at the point we hand the seed to
LocalSigner:
- counter 0 == the raw wallet seed, so every already-clean identity is
  byte-identical (the common case is unchanged, no migration),
- only when the raw key's base64 contains `/` do we deterministically
  bump a domain-separated SHA-256 counter until the public key is
  slash-free. Same wallet always maps to the same lowest-counter
  slash-free identity.

Mirrors the workaround the tiny.place plugin already uses (regenerating
slash-free wallets); here we remap deterministically instead since the
identity must stay a pure function of the wallet key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@CodeGhost21 CodeGhost21 requested a review from a team July 10, 2026 20:56
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Solana tiny.place signer seed generation now remaps seeds whose derived Ed25519 public-key Base64 contains /, using deterministic SHA-256 candidates. Unit tests cover unchanged clean seeds and deterministic slash-free remapping.

Changes

Solana identity seed handling

Layer / File(s) Summary
Deterministic seed remapping
src/openhuman/wallet/chains/solana.rs
Adds bounded, domain-separated SHA-256 candidate generation and selects the first seed with a slash-free standard Base64 identity public key.
Signer integration and validation
src/openhuman/wallet/chains/solana.rs
Routes tinyplace_signer_seed() through the remapping helper and tests clean-seed passthrough plus deterministic remapping.
Estimated code review effort: 3 (Moderate) ~20 minutes

Possibly related issues

  • tinyhumansai/tiny.place#213 — Addresses slash-containing Base64 Ed25519 identity keys by ensuring derived seeds produce slash-free keys.

Poem

A bunny found a slash in the key,
And hopped through hashes, one, two, three.
A cleaner seed came bouncing through,
With stable bytes and Base64 blue.
Tiny.place now hops slash-free!

🚥 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 clearly summarizes the main change: generating slash-free tiny.place identity keys in the wallet.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/wallet/chains/solana.rs`:
- Around line 733-744: Add a golden-vector assertion to the slash-bearing seed
test around slash_free_identity_seed, asserting the exact expected output seed
or derived public key for the fixed slashed input; retain the existing
slash-free and determinism checks to prevent accidental identity migration from
changes to hashing or encoding.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eb23741a-fd38-4538-a1c0-daeca88b22f8

📥 Commits

Reviewing files that changed from the base of the PR and between b709a99 and 010c364.

📒 Files selected for processing (1)
  • src/openhuman/wallet/chains/solana.rs

Comment on lines +733 to +744
// Slashed seed is remapped to a slash-free, deterministic result.
let out = slash_free_identity_seed(slashed);
assert_ne!(out, slashed, "slashed seed must be remapped");
assert!(
!pub_b64(&out).contains('/'),
"result pubkey must be slash-free"
);
assert_eq!(
slash_free_identity_seed(slashed),
out,
"must be deterministic"
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Pin a golden remapping vector to prevent accidental identity migration.

Calling the helper twice only proves current-run determinism. Changes to the domain, counter encoding, or hash inputs would still pass while silently changing TinyPlace and Signal identities. Assert the exact output seed or public key for a fixed slash-bearing seed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/wallet/chains/solana.rs` around lines 733 - 744, Add a
golden-vector assertion to the slash-bearing seed test around
slash_free_identity_seed, asserting the exact expected output seed or derived
public key for the fixed slashed input; retain the existing slash-free and
determinism checks to prevent accidental identity migration from changes to
hashing or encoding.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 010c36441f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Never logged: the log below omits the seed.
log::debug!("[tinyplace] signer seed derived (seed not logged)");
Ok(signing_key.to_bytes())
Ok(slash_free_identity_seed(signing_key.to_bytes()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep signer seed aligned with the wallet address

For any wallet whose raw public key base64 contains /, this now returns a seed for a different Ed25519 key, so LocalSigner.agent_id() is no longer the Solana wallet address derived by the normal wallet path. The existing tiny.place payment and registry flows still treat signer.agent_id() as the wallet owner, e.g. wallet_usdc_balance(&signer.agent_id()), x402 from, and RegisterRequest.crypto_id, while execute_prepared spends from the original Solana wallet. In that slash-bearing case users can see/authorize under an unfunded or different cryptoId, and paid actions can be rejected after the on-chain transfer; keep the spending identity tied to the wallet and fix the /keys path/messaging key separately.

Useful? React with 👍 / 👎.

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