Skip to content

fix(sdk/ts): address relay by base58 cryptoId, not base64 public key#226

Merged
sanil-23 merged 2 commits into
tinyhumansai:mainfrom
sanil-23:fix/ts-sdk-base58-relay-ids
Jul 6, 2026
Merged

fix(sdk/ts): address relay by base58 cryptoId, not base64 public key#226
sanil-23 merged 2 commits into
tinyhumansai:mainfrom
sanil-23:fix/ts-sdk-base58-relay-ids

Conversation

@sanil-23

@sanil-23 sanil-23 commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Problem

The relay's canonical routing/identity id is the base58 cryptoId (the Solana address = base58 of the 32-byte Ed25519 public key), per the backend spec (crypto-identity.md:7, identity-registry.md:44). The TS SDK was addressing messages by the base64 Ed25519 public key (signer.publicKeyBase64) for from/to, mailbox list, key-publish address, and group-member handoff.

The relay accepted it because it treats the address as an opaque mailbox key — which masked the non-conformance. But a strict, spec-conformant consumer (the Rust SDK / openhuman) base58-decodes the id to recover the public key, and a base64 string fails to decode (base64's alphabet includes +///= and 0/O/I/l, which base58 forbids) → it can't verify signatures, open a Signal session, or route a reply. Cross-SDK interop (TS agent ↔ Rust agent) broke.

Fix

Normalize the identity/relay-id surface to base58 cryptoId (signer.agentId):

  • messaging: from/to, resolveRecipientKey (a base64 messaging-key input is now normalized to its cryptoId), readMessages/list, publishKeys address.
  • encryption: EncryptionContext.address + envelope to/from decode via the new crypto.cryptoIdToPublicKey (base58 → raw Ed25519 bytes → X25519 for ECDH).
  • group: address the 1:1 key handoff by the member's base58 cryptoId.
  • cli: harness-wrapper / workflows / status use agentId.

Crucially base64 is preserved for Signal key material + ciphertext — the published identityKey field stays the base64 Ed25519 key (a peer decodes it to bootstrap X3DH; it is not a routing address). Mental model: base58 = "who" (address); base64 = "the bytes".

Testing

tsc clean; 354 unit tests pass (98 staging tests skipped — need network). Tests updated to assert base58 addressing (agent-messaging, encrypted-messaging, cli, codex-cli).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Messaging now consistently uses agent IDs / relay routing keys for sending, replies, inbox lookups, group handoffs, and encrypted message handling.
    • Handle-based recipient resolution now works with base58 identifiers and falls back more reliably when only identity details are available.
  • Bug Fixes

    • Fixed cases where messaging and status checks could fail when a signer’s base64 public key wasn’t available.
    • Improved round-trip messaging behavior so sender and recipient identifiers stay consistent across the app and CLI.

The relay's canonical routing/identity id is the base58 cryptoId (the Solana
address = base58 of the 32-byte Ed25519 public key), per the backend spec
(crypto-identity.md, identity-registry.md). The TS SDK was addressing messages by
the base64 Ed25519 public key (`signer.publicKeyBase64`) for `from`/`to`, mailbox
list, key publish, and group-member handoff. The relay accepted it (it treats the
address as an opaque mailbox key), which masked the non-conformance — but a strict,
spec-conformant consumer (the Rust SDK / openhuman) base58-decodes the id to recover
the public key, and a base64 string fails to decode (illegal base58 chars) → it
can't verify signatures, open a Signal session, or route a reply. Cross-SDK interop
(TS agent ↔ Rust agent) broke.

Normalize the identity/relay-id surface to base58 cryptoId (`signer.agentId`):
- messaging: from/to, resolveRecipientKey (a base64 messaging-key input is now
  normalized to its cryptoId), readMessages/list, publishKeys address.
- encryption: EncryptionContext.address + envelope to/from decode via the new
  crypto.cryptoIdToPublicKey (base58 → raw Ed25519 bytes → X25519 for ECDH).
- group: address the 1:1 key handoff by the member's base58 cryptoId.
- cli: harness-wrapper / workflows / status use agentId.

Crucially KEEP base64 for Signal key material and ciphertext — the published
`identityKey` field stays the base64 Ed25519 key (a peer decodes it to bootstrap
X3DH; it is not a routing address). Base58 = "who" (address); base64 = "the bytes".

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

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

@sanil-23 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 Jul 5, 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: 3abdd438-4b93-4343-a592-05c750421e3e

📥 Commits

Reviewing files that changed from the base of the PR and between d81148e and 3bc931c.

📒 Files selected for processing (2)
  • sdk/typescript/src/cli/harness-wrapper.ts
  • sdk/typescript/src/messaging/encryption.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdk/typescript/src/messaging/encryption.ts

📝 Walkthrough

Walkthrough

Agent addressing across the TypeScript SDK is migrated from base64 Ed25519 public keys to base58 cryptoId (signer.agentId). A new cryptoIdToPublicKey helper decodes cryptoId to raw bytes; encryption, messaging, group messaging, and CLI workflows are updated to resolve, route, and store identities via cryptoId, with tests and mocks adjusted accordingly.

Changes

CryptoId-based addressing migration

Layer / File(s) Summary
cryptoId decoding helper
sdk/typescript/src/crypto.ts
Adds exported cryptoIdToPublicKey to decode and validate base58 cryptoId into raw bytes; cryptoIdToPublicKeyBase64 delegates to it.
Encryption context routing key handling
sdk/typescript/src/messaging/encryption.ts
Peer keys for ECDH are derived from envelope.to/envelope.from cryptoId via cryptoIdToPublicKey; address returns signer.agentId; key bundles stored under routing key while identityKey stays base64.
Agent messaging and identity address usage
sdk/typescript/src/agent/messaging.ts, sdk/typescript/src/agent/identity.ts, sdk/typescript/src/cli/harness-wrapper.ts
resolveRecipientKey resolves inputs to base58 cryptoId; publishKeys, sendMessage, readMessages, pollUpdates, and self-recipient bypass logic switch from publicKeyBase64 to agentId.
Group messaging handoff addressing
sdk/typescript/src/messaging/group.ts
Sender-key handoff DM targets recipient via card.cryptoId directly, removing resolveEncryptionAddress, with updated docs.
CLI workflow identity resolution
sdk/typescript/src/cli/workflows.ts
statusFlow, messageFlow, readFlow, replyFlow, and resolveRecipient switch from publicKey-based identity to agentId, normalizing raw base64 messaging keys to cryptoId.
Tests updated for agentId addressing
sdk/typescript/tests/agent-messaging.test.ts, sdk/typescript/tests/agent.test.ts, sdk/typescript/tests/cli.test.ts, sdk/typescript/tests/codex-cli.test.ts, sdk/typescript/tests/encrypted-messaging.test.ts
Unit and E2E tests, mocks, and relay assertions are updated to use agentId/cryptoId addressing.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Sender
  participant EncryptionContext
  participant CryptoUtils
  participant KeyServer
  participant Recipient

  Sender->>EncryptionContext: sendMessage(to=recipient.agentId)
  EncryptionContext->>CryptoUtils: cryptoIdToPublicKey(envelope.to)
  CryptoUtils-->>EncryptionContext: recipient Ed25519 public key
  EncryptionContext->>KeyServer: fetch/publish bundle (address=agentId, identityKey=publicKeyBase64)
  EncryptionContext-->>Sender: encrypted envelope (from=signer.agentId)
  Sender->>Recipient: deliver envelope
  Recipient->>EncryptionContext: decryptEnvelope(envelope)
  EncryptionContext->>CryptoUtils: cryptoIdToPublicKey(envelope.from)
  CryptoUtils-->>EncryptionContext: sender Ed25519 public key
  EncryptionContext-->>Recipient: decrypted message
Loading

Possibly related PRs

  • tinyhumansai/tiny.place#205: The self-recipient logic switch from publicKeyBase64 to agentId in harness-wrapper.ts builds directly on the harness wrapper implementation introduced there.

Poem

Hop, hop, cryptoId in tow,
No more base64 keys to show,
Addresses now base58 bright,
Every message finds its right flight,
A rabbit's routing, clean and true 🐇✨

🚥 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 matches the main change: switching SDK relay addressing from base64 public keys to base58 cryptoIds.
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.

…elay-ids

# Conflicts:
#	sdk/typescript/src/messaging/encryption.ts
@sanil-23 sanil-23 merged commit c6fe0c2 into tinyhumansai:main Jul 6, 2026
9 of 10 checks passed
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