Skip to content

feat: add ThruBox relay service layer for encrypted invoice delivery - #194

Closed
Atharva0506 wants to merge 1 commit into
StabilityNexus:mainfrom
Atharva0506:feat/thrubox-relay-service
Closed

feat: add ThruBox relay service layer for encrypted invoice delivery#194
Atharva0506 wants to merge 1 commit into
StabilityNexus:mainfrom
Atharva0506:feat/thrubox-relay-service

Conversation

@Atharva0506

@Atharva0506 Atharva0506 commented Aug 8, 2026

Copy link
Copy Markdown
Member

Addressed Issues:

Part of #139 (2 of 5). Does not close it on its own.

Screenshots/Recordings:

Not applicable — this PR is purely additive and is not wired into any page yet, so there is no visible change. The full flow is demonstrated in #195.

Additional Notes:

Waku supplied two things: end-to-end encryption and a transport. ThruBox is a dumb mailbox that supplies only the transport, so this adds both halves as an independent service layer:

File Responsibility
invoiceCrypto.js ECIES over secp256k1 (eciesjs), base64 framing
relayKeyManager.js keypair derivation + on-chain key registry
relayInvoiceMessaging.js send / fetch / poll of encrypted envelopes
relayClient.js configured ThruBox client singleton
invoiceHashUtils.js deterministic keccak256 commitment over payloads

Nothing is wired into the invoice pages yet, so application behaviour is unchanged. This PR is safe to land on its own.

Design notes

Key derivation is unchanged from the Waku implementation, deliberately. Keys are already registered in the on-chain registry, and a different derivation message would silently break decryption for those users. The same 65-byte uncompressed public keys the contract already validates are what ECIES consumes, so no contract change is needed.

Three relay behaviours drove the design, each verified against a running instance:

  • Address lookup is an exact string match. A message sent to a checksummed address is invisible to a client polling the lowercase form. Every address crossing the boundary is normalised.
  • There is no cursor — each poll returns the whole mailbox. The poller tracks message IDs it has already surfaced rather than deleting from the relay, so a second device can still collect an invoice before it expires.
  • The SDK does not retry POSTs, since a retried send would duplicate the message. One timed-out send is therefore one undelivered invoice, so the timeout is configurable via VITE_RELAY_TIMEOUT_MS for hosts that suspend idle instances and cold-start slowly.

CORS

The relay serves no CORS headers, so a browser cannot reach it cross-origin: the preflight for POST /api/messages is rejected outright and GET responses carry no Access-Control-Allow-Origin. Two supported ways around it:

  • Proxy it — a Vite dev proxy on /relay in development; in production set VITE_RELAY_URL to a path (/relay) and let the host rewrite it (Vercel rewrites, Netlify redirects, nginx). The browser stays same-origin and no CORS is needed.
  • Address it directly with an absolute VITE_RELAY_URL, which requires CORS support on the relay.

Tests

19 new unit tests covering the crypto round-trip, envelope decoding, chain filtering, address normalisation, and poll deduplication.

Review order: independent of #193; can land in either order. Required by #195.

AI Usage Disclosure:

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: Claude Code (CLI), model Claude Opus 5

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

  • New Features
    • Added secure invoice sending and receiving through a relay service.
    • Added wallet-based encryption key setup with optional session persistence and on-chain registration.
    • Added invoice integrity verification to detect tampering.
    • Added relay health checks, configurable timeouts, API key support, and development proxying.
  • Bug Fixes
    • Improved handling of invalid, malformed, duplicate, or undecryptable invoice messages.
  • Tests
    • Added coverage for encryption, integrity validation, messaging, polling, and error handling.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Atharva0506, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 794cf48c-c742-4d4f-8906-28579f5ea98d

📥 Commits

Reviewing files that changed from the base of the PR and between 47ee6f1 and a6ea0f5.

📒 Files selected for processing (5)
  • README.md
  • frontend/README.md
  • frontend/src/hooks/useRelayKeys.js
  • frontend/src/services/relay/relayKeyManager.js
  • frontend/tests/services/relayInvoiceMessaging.test.js

Walkthrough

The frontend adds relay configuration, wallet-derived ECIES keys, encrypted invoice messaging, deterministic invoice hashes, mailbox polling, and React integration for key registration.

Changes

Relay invoice flow

Layer / File(s) Summary
Relay configuration and client foundation
frontend/.env.example, frontend/package.json, frontend/vite.config.js, frontend/src/services/relay/relayClient.js, frontend/src/services/relay/index.js, frontend/jest.config.cjs
The frontend defines relay URL, API key, and timeout settings. Vite proxies /relay requests. The relay client resolves URLs, applies configuration, caches clients, and checks health.
Key management and invoice cryptography
frontend/src/services/relay/relayKeyManager.js, frontend/src/services/relay/invoiceCrypto.js, frontend/src/services/relay/invoiceHashUtils.js, frontend/tests/services/invoiceCrypto.test.js, frontend/tests/services/invoiceHashUtils.test.js
The frontend derives and caches wallet-based key pairs, registers public keys on chain, encrypts invoice payloads with ECIES, and computes deterministic invoice hashes. Utility tests cover these behaviors.
Encrypted mailbox messaging
frontend/src/services/relay/relayInvoiceMessaging.js, frontend/tests/services/relayInvoiceMessaging.test.js
The messaging service sends encrypted invoice envelopes, decrypts and filters mailbox messages, and polls with duplicate suppression and stop control. Tests cover validation, filtering, polling, shutdown, and callback errors.
Wallet key hook integration
frontend/src/hooks/useRelayKeys.js
The useRelayKeys hook restores cached keys, derives keys from wallet signatures, tracks registration status, registers public keys, and refreshes state when wallet context changes.

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

Sequence Diagram(s)

sequenceDiagram
  participant Wallet
  participant useRelayKeys
  participant relayKeyManager
  participant relayInvoiceMessaging
  participant invoiceCrypto
  participant RelayClient
  Wallet->>useRelayKeys: provide account and wallet client
  useRelayKeys->>relayKeyManager: derive or restore relay key pair
  relayInvoiceMessaging->>invoiceCrypto: encrypt invoice envelope
  relayInvoiceMessaging->>RelayClient: submit encrypted mailbox message
  RelayClient-->>relayInvoiceMessaging: return mailbox messages
  relayInvoiceMessaging->>invoiceCrypto: decrypt and filter messages
Loading

Possibly related PRs

Suggested labels: Typescript Lang

Poem

A rabbit checks the relay key,
Seals invoices from sight.
Mailboxes poll quietly,
Hashes keep records right.
Wallets register.
Encrypted messages take flight.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the new ThruBox relay service layer and its encrypted invoice delivery purpose.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@socket-security

socket-security Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​aossie-org/​thrubox-client@​1.0.2751001008970
Addednpm/​eciesjs@​0.5.010010010083100

View full report

@Atharva0506

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 9

🤖 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 `@frontend/.env.example`:
- Around line 24-25: Update the comment above VITE_RELAY_API_KEY to explicitly
state that Vite exposes VITE_-prefixed variables in the client bundle, so this
key is visible to visitors and cannot be treated as a secret or application
access restriction. Mention that private keys require routing relay requests
through a same-origin server-side proxy; otherwise document the browser-exposure
limitation.

In `@frontend/package.json`:
- Line 17: Update the `@aossie-org/thrubox-client` dependency entry in
package.json to use the exact compatible version rather than the caret range,
and pin any related eciesjs dependency to the relay-compatible version so
encryption behavior cannot drift across installs.

In `@frontend/src/hooks/useRelayKeys.js`:
- Around line 142-152: Update the error handling in useRelayKeys to store stable
i18n error codes in the returned error state instead of user-visible English
strings. Add translations for both the default registration failure and
rejected-signature messages to the existing i18n resource files, then translate
the stored codes at component render time while preserving the current rejection
detection behavior.
- Around line 52-73: Update the rehydrate effect in useRelayKeys to use a
cache-only restoration helper that checks memory and sessionStorage without
deriving keys or invoking signMessage. Ensure it respects rememberSession,
avoids restoring stale or unavailable cache entries, and only updates
setKeys/setHasKeys when cached keys are found; replace the deriveRelayKeyPair
call in this effect while leaving normal key derivation behavior unchanged.

In `@frontend/src/services/relay/invoiceHashUtils.js`:
- Around line 47-49: Update the array handling in stableStringify so undefined
elements and sparse array slots are serialized as null, matching
invoiceCrypto.js JSON semantics and keeping [] distinct from [undefined]. Add
regression coverage for both an explicit undefined entry and a sparse array.

In `@frontend/src/services/relay/relayInvoiceMessaging.js`:
- Around line 30-38: Update buildEnvelope to validate chainId is present and
numeric, and invoiceId is neither null nor undefined, before converting or
constructing the envelope. Fail fast with explicit errors for invalid inputs,
while preserving the existing normalized chainId and stringified invoiceId
fields for valid values.
- Around line 90-109: Update decodeMessages so the returned sender field is
named claimedFrom instead of from, making clear that message.from is
unauthenticated relay metadata. Document in the
sendEncryptedInvoice/decodeMessages contract that callers must verify
envelope.data against the on-chain invoiceDataHash for envelope.invoiceId before
trusting or using the claimed sender.

In `@frontend/src/services/relay/relayKeyManager.js`:
- Around line 85-88: Remove privateKey persistence from the rememberSession
branch in the relay key-management flow, including the cacheKeysToSession call
from relay key derivation. Keep the ECIES private key memory-only so reloads
require a new signature; if persistence is necessary, replace browser storage
with expo-secure-store and do not enable it by default in useRelayKeys.

In `@frontend/tests/services/relayInvoiceMessaging.test.js`:
- Around line 186-196: Update the pollInvoiceMessages test helper capturePoll to
retain relayApi.poll’s third options argument, then add tests covering
intervalMs forwarding, onError delegation, and suppression of errors after
stopPolling(). Use the captured callback and stop handle to assert each behavior
while preserving existing message polling coverage.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 068ae92f-d400-4954-9b5c-999bed4f9eef

📥 Commits

Reviewing files that changed from the base of the PR and between 5080141 and 40d6e50.

📒 Files selected for processing (14)
  • frontend/.env.example
  • frontend/jest.config.cjs
  • frontend/package.json
  • frontend/src/hooks/useRelayKeys.js
  • frontend/src/services/relay/index.js
  • frontend/src/services/relay/invoiceCrypto.js
  • frontend/src/services/relay/invoiceHashUtils.js
  • frontend/src/services/relay/relayClient.js
  • frontend/src/services/relay/relayInvoiceMessaging.js
  • frontend/src/services/relay/relayKeyManager.js
  • frontend/tests/services/invoiceCrypto.test.js
  • frontend/tests/services/invoiceHashUtils.test.js
  • frontend/tests/services/relayInvoiceMessaging.test.js
  • frontend/vite.config.js

Comment thread frontend/.env.example
Comment thread frontend/package.json Outdated
Comment thread frontend/src/hooks/useRelayKeys.js Outdated
Comment thread frontend/src/hooks/useRelayKeys.js Outdated
Comment thread frontend/src/services/relay/invoiceHashUtils.js
Comment thread frontend/src/services/relay/relayInvoiceMessaging.js
Comment thread frontend/src/services/relay/relayInvoiceMessaging.js
Comment thread frontend/src/services/relay/relayKeyManager.js Outdated
Comment thread frontend/tests/services/relayInvoiceMessaging.test.js
@Atharva0506
Atharva0506 force-pushed the feat/thrubox-relay-service branch from 40d6e50 to 089ff04 Compare August 8, 2026 13:29

@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: 4

🤖 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 `@frontend/src/hooks/useRelayKeys.js`:
- Around line 87-102: Update checkRegistration in the useRelayKeys hook to track
the latest address with a ref synchronized by useEffect, capture the address
used for the chain read, and discard the resolved result when it no longer
matches the current address before calling setIsRegistered or returning the
registration state.

In `@frontend/src/services/relay/relayInvoiceMessaging.js`:
- Around line 189-198: Update pollInvoiceMessages in
frontend/src/services/relay/relayInvoiceMessaging.js:189-198 so
seen.add(item.messageId) runs only after await onInvoice(item) succeeds,
allowing failed handlers to be retried. Update
frontend/tests/services/relay/relayInvoiceMessaging.test.js:387-408 to assert
the delivered messageId sequence instead of only call count and add coverage
proving a failed message is delivered again on the next poll.

In `@frontend/tests/services/invoiceHashUtils.test.js`:
- Around line 42-68: Add a test in the existing array edge-case describe block
that serializes a representative object through JSON.stringify and JSON.parse,
then compares stableStringify values before and after the round trip to verify
the sender/receiver hash contract. Include values such as Date and bigint to
cover types that stableStringify may represent but relay JSON cannot preserve,
while keeping the test focused on detecting mismatched hashes.

In `@frontend/tests/services/relayInvoiceMessaging.test.js`:
- Around line 387-408: Update the “keeps polling when a handler throws” test to
assert the delivered invoice IDs, ensuring the failed m1 is retried before m2 on
the next poll. Add or adjust a focused case that verifies a handler failure
leaves that message eligible for retry on the subsequent poll, rather than only
asserting onInvoice’s call count.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3dd65896-02be-4a52-8ba1-4aaa6c74f929

📥 Commits

Reviewing files that changed from the base of the PR and between 40d6e50 and 089ff04.

📒 Files selected for processing (8)
  • frontend/.env.example
  • frontend/package.json
  • frontend/src/hooks/useRelayKeys.js
  • frontend/src/services/relay/invoiceHashUtils.js
  • frontend/src/services/relay/relayInvoiceMessaging.js
  • frontend/src/services/relay/relayKeyManager.js
  • frontend/tests/services/invoiceHashUtils.test.js
  • frontend/tests/services/relayInvoiceMessaging.test.js

Comment thread frontend/src/hooks/useRelayKeys.js Outdated
Comment thread frontend/src/services/relay/relayInvoiceMessaging.js
Comment thread frontend/tests/services/invoiceHashUtils.test.js
Comment thread frontend/tests/services/relayInvoiceMessaging.test.js Outdated
@Atharva0506
Atharva0506 force-pushed the feat/thrubox-relay-service branch from 089ff04 to 8c3003c Compare August 8, 2026 13:47
@Atharva0506
Atharva0506 force-pushed the feat/thrubox-relay-service branch from 8c3003c to 3c4303d Compare August 9, 2026 08:07

@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: 6

🤖 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 `@frontend/src/hooks/useRelayKeys.js`:
- Around line 56-61: Update the cleanup logic in useRelayKeys so clearCachedKeys
runs only when the wallet address actually transitions to disconnected, not on
ordinary unmounts or address changes. Preserve the shared/session cache across
StrictMode remounts, route unmounts, and multiple hook instances; use a ref or
equivalent lifecycle tracking to reliably compare the previous and current
address while respecting effect cleanup ordering.
- Around line 68-69: Replace the computed import.meta.env access in the relay
key lookup with a static contract-address map keyed by chainId, following the
existing frontend pattern. Read the address from that map in the surrounding
lookup flow, retain the null return when no address is configured, and ensure
all environment keys are statically referenced for Vite replacement.

In `@frontend/src/services/relay/relayClient.js`:
- Around line 64-69: Remove the privileged-key usage from the browser-side
RelayClient initialization in resolveBaseUrl/client setup, or replace it with a
server-side proxy that injects the key; do not read a secret from
VITE_RELAY_API_KEY. If the relay key is intentionally public and used only for
rate limiting, document that constraint in frontend/.env.example and ensure no
sensitive credential is exposed.

In `@frontend/src/services/relay/relayKeyManager.js`:
- Around line 161-175: Update getSessionCachedKeys to validate the byte lengths
of both decoded keys against the required key sizes before returning the pair;
return null when either length is invalid so getCachedKeyPair does not promote
corrupted data and the next call re-derives the keys.
- Around line 181-191: Update clearCachedKeys and the derivation completion path
to track a per-address generation or cleared marker, incrementing or setting it
when keys are cleared and capturing its value when derivation starts. Before the
pending derivation writes to memoryCache or sessionStorage, verify that its
captured generation is still current; skip both writes when clearCachedKeys has
invalidated it.

In `@frontend/tests/services/relayInvoiceMessaging.test.js`:
- Around line 313-330: Add an overlapping-poll test alongside “emits each
message only once across polls” that starts two pump(batch) calls before
awaiting either promise, then awaits both and asserts onInvoice was called once.
Keep the same mailbox batch and receiver setup so the test verifies message
claiming occurs before the asynchronous handler completes.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cd8cd6a6-3585-4f9e-a343-93fe9b7ac877

📥 Commits

Reviewing files that changed from the base of the PR and between 089ff04 and 3c4303d.

📒 Files selected for processing (7)
  • frontend/src/hooks/useRelayKeys.js
  • frontend/src/services/relay/index.js
  • frontend/src/services/relay/relayClient.js
  • frontend/src/services/relay/relayInvoiceMessaging.js
  • frontend/src/services/relay/relayKeyManager.js
  • frontend/tests/services/invoiceHashUtils.test.js
  • frontend/tests/services/relayInvoiceMessaging.test.js

Comment thread frontend/src/hooks/useRelayKeys.js Outdated
Comment thread frontend/src/hooks/useRelayKeys.js
Comment thread frontend/src/services/relay/relayClient.js
Comment thread frontend/src/services/relay/relayKeyManager.js
Comment thread frontend/src/services/relay/relayKeyManager.js
Comment thread frontend/tests/services/relayInvoiceMessaging.test.js
@Atharva0506
Atharva0506 force-pushed the feat/thrubox-relay-service branch from 3c4303d to 47ee6f1 Compare August 9, 2026 09:15

@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: 6

🤖 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 `@frontend/src/hooks/useRelayKeys.js`:
- Around line 74-81: The getContract/checkRegistration flow must distinguish a
disconnected wallet from an unsupported chain with no configured contract
address. Update getContract and its callers so missing wallet or chain remains a
disconnected state, while an absent VITE_CONTRACT_ADDRESS_${chainId} produces a
dedicated unsupported-network result; ensure checkRegistration and the
registration UI do not set isRegistered=false or offer registration for that
unsupported state.
- Around line 123-126: Update the catch block in the registry-read flow around
isStale and setIsRegistered to capture the thrown error and log it with
sufficient context before returning false, while preserving the existing
stale-state behavior. Ensure the failed RPC read is distinguishable from a valid
unregistered result.

In `@frontend/src/services/relay/relayKeyManager.js`:
- Around line 292-298: Update fetchPublicKeyFromChain to validate the registry
key using the same expected size and prefix requirements enforced by
getSessionCachedKeys before calling hexToBytes. Return null or otherwise reject
malformed keys so invalid entries cannot reach encryptPayload or downstream key
comparisons.

In `@frontend/tests/services/relayInvoiceMessaging.test.js`:
- Around line 73-75: Update the “throws on a missing address” test around
toMailboxAddress to use a regex error-message matcher like the chainId and
invoiceId validation tests, and add equivalent cases for null and undefined
alongside the empty string.
- Around line 79-96: Update the test for sendEncryptedInvoice to decrypt
sent.payload using receiver.privateKey and assert the resulting envelope,
including invoiceId as String(invoiceId). Validate the decrypted envelope’s
expected fields so the test covers buildEnvelope output, while retaining the
existing participant address assertions.
- Around line 461-481: Add a test in the pollInvoiceMessages suite covering a
stop triggered while processing a batch: have the first onInvoice handler invoke
the returned stop mechanism, then verify subsequent messages in the same batch
are not handled. Keep the existing later-message-after-error test unchanged and
assert the expected handler calls using the existing capturePoll and
relayMessage helpers.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1e108b17-e545-4780-a0a1-6816d26a02ff

📥 Commits

Reviewing files that changed from the base of the PR and between 3c4303d and 47ee6f1.

📒 Files selected for processing (3)
  • frontend/src/hooks/useRelayKeys.js
  • frontend/src/services/relay/relayKeyManager.js
  • frontend/tests/services/relayInvoiceMessaging.test.js

Comment thread frontend/src/hooks/useRelayKeys.js
Comment thread frontend/src/hooks/useRelayKeys.js Outdated
Comment thread frontend/src/services/relay/relayKeyManager.js
Comment thread frontend/tests/services/relayInvoiceMessaging.test.js Outdated
Comment thread frontend/tests/services/relayInvoiceMessaging.test.js
Comment thread frontend/tests/services/relayInvoiceMessaging.test.js

@rohans02 rohans02 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.

reviewed, no blocking issues. But address the coderabbit comments, if they aren't relevant resolve them with reason.

@Atharva0506
Atharva0506 force-pushed the feat/thrubox-relay-service branch from 47ee6f1 to 5d013ef Compare August 10, 2026 12:51
Waku supplied two things: end-to-end encryption and a transport. ThruBox
is a dumb mailbox that supplies only the transport, so this adds both
halves as an independent service layer:

  invoiceCrypto.js         ECIES over secp256k1 (eciesjs), base64 framing
  relayKeyManager.js       keypair derivation + on-chain key registry
  relayInvoiceMessaging.js send / fetch / poll of encrypted envelopes
  relayClient.js           configured ThruBox client singleton
  invoiceHashUtils.js      deterministic keccak256 commitment over payloads

Key derivation is unchanged from the Waku implementation, deliberately:
keys are already registered in the on-chain registry, and a different
derivation message would silently break decryption for those users. The
same 65-byte uncompressed public keys the contract already validates are
what ECIES consumes here, so no contract change is needed.

Three relay behaviours drove the design:

- Address lookup is an exact string match, so a message sent to a
  checksummed address is invisible to a client polling the lowercase
  form. Every address crossing the boundary is normalised.
- There is no cursor; each poll returns the whole mailbox. The poller
  tracks message IDs it has already surfaced rather than deleting from
  the relay, so a second device can still collect an invoice before it
  expires.
- The SDK does not retry POSTs, since a retried send would duplicate the
  message. One timed-out send is therefore one undelivered invoice, so
  the timeout is configurable via VITE_RELAY_TIMEOUT_MS for hosts that
  suspend idle instances and cold-start slowly.

The relay serves no CORS headers, so a browser cannot reach it
cross-origin: the preflight for POST /api/messages is rejected outright
and GET responses carry no Access-Control-Allow-Origin. A Vite dev proxy
on /relay keeps development requests same-origin; production needs CORS
on the relay or a reverse proxy in front of it.

Nothing is wired into the invoice pages yet, so behaviour is unchanged.
@github-actions

Copy link
Copy Markdown

⚠️ This PR has merge conflicts.

Please resolve the merge conflicts before review.

Your PR will only be reviewed by a maintainer after all conflicts have been resolved.

📺 Watch this video to understand why conflicts occur and how to resolve them:
https://www.youtube.com/watch?v=Sqsz1-o7nXk

@Atharva0506

Copy link
Copy Markdown
Member Author

Closing as superseded — this PR has nothing left to contribute.

It was part 2 of a 5-part stack, and #195 was stacked on top of it, so merging #195 brought this branch's entire contribution onto main with it. I verified the relay service layer, useRelayKeys, and the dependency additions are byte-for-byte identical between this branch and main:

git diff a6ea0f5 origin/main -- frontend/src/services/relay frontend/src/hooks/useRelayKeys.js frontend/package.json
# (no output)

The conflict is not a merge that needs resolving. This branch is now behind main — every remaining difference is a deletion of the page wiring #195 added, so merging it would revert that work:

git diff origin/main a6ea0f5 --stat
# 29 files changed, 204 insertions(+), 1911 deletions(-)

Nothing is lost by closing. #196 and #197 carry the rest of the stack and are both rebased onto current main and mergeable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants