Skip to content

Native HMAC apiSigner and signed infoRollup appKeys - #6183

Open
paullinator wants to merge 7 commits into
developfrom
paul/nativeHmacApiSigner
Open

Native HMAC apiSigner and signed infoRollup appKeys#6183
paullinator wants to merge 7 commits into
developfrom
paul/nativeHmacApiSigner

Conversation

@paullinator

@paullinator paullinator commented Aug 29, 2026

Copy link
Copy Markdown
Member

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

Requirements

If you have made any visual changes to the GUI. Make sure you have:

  • Tested on iOS device
  • Tested on Android device
  • Tested on small-screen device (iPod Touch)
  • Tested on large-screen device (tablet)

Description

Splits runtime env.json into non-secret config.json and secret keys.json, with four plugin maps (corePlugins, swapPlugins, guiApiKeys, rampPlugins) plus globalKeys. WalletConnect is globalKeys.WALLETCONNECT_PROJECT_ID. Plugin maps are opaque objects (no field-by-field *_INIT / *_API_KEY flattening).

Fetches remote secrets from signed GET /v1/infoRollup/:appId as sibling appKeys (partner id config.appId ?? 'edge'), with DeviceSettings getKeysCache and baked-in keys.json as fallbacks. pluginApiKeys.posthog is never served.

Adds a native Edge API HMAC signer (edgeKey.json + XOR-split C shards) so login-server requests can be signed outside the JS bundle via apiSigner, with JS KEYS.EDGE_API_* remaining as a fallback.

Rebased onto current develop (keeps Swapter and marketing-push tracking).


Note

High Risk
Changes authentication (native HMAC + infoRollup signing), boot-time secret resolution, and how every plugin receives credentials—misconfiguration or timing bugs could break login, partner APIs, or silently pin stale keys.

Overview
Replaces the monolithic env.json / ENV setup with config.json (non-secret), keys.json (secrets), and build-only edgeKey.json, exposed at runtime as CONFIG, KEYS / globalKeys, and resolved pluginMaps. Plugin configuration moves to four ID-keyed maps (corePlugins, swapPlugins, guiApiKeys, rampPlugins) instead of flat *_INIT fields; deploy and local tooling now patch configJson / keysJson per branch (legacy envJson is ignored here).

Adds native Edge API HMAC signing on iOS and Android: build scripts XOR-shard the secret from edgeKey.json into generated C sources, expose EdgeApiSigner to React Native, and wire apiSigner into edge-core for login-server auth while keeping secrets out of the Metro bundle (JS KEYS.EDGE_API_* remains a fallback when the native module is absent).

Adds signed remote plugin secrets via GET /v1/infoRollup/:appId appKeys, merged over baked-in keys.json with a DeviceSettings keysCache tier and cold-start timeouts; EdgeCoreManager waits on key resolution before building plugins, and consumers that need rotatable secrets must read them lazily after overlay apply.

Ships migration scripts (split-env-json, split-baked-and-server-keys), expanded docs (CONFIG_KEYS_ARCHITECTURE.md, HMAC_SIGNING.md), and tests for merge semantics, HMAC vectors, keysServer, and serialized settings writes.

Reviewed by Cursor Bugbot for commit 568056f. Bugbot is set up for automated code reviews on this repo. Configure here.

@paullinator
paullinator force-pushed the paul/nativeHmacApiSigner branch 4 times, most recently from c8f9826 to 44a9d19 Compare August 31, 2026 23:52
@paullinator
paullinator marked this pull request as ready for review September 1, 2026 16:24
paullinator and others added 7 commits September 4, 2026 16:45
Apply strict-boolean, nullish, and return-type fixes in files leaving
the relaxed-rules list.
Single-flight the initial load and serialize every write through a
promise chain so overlapping patches cannot clobber each other or
blank on-disk fields. Adds keysCache fields for remote key fetch.
Replace the flat env.json/ENV singleton with config.json + keys.json and
runtime CONFIG, KEYS, globalKeys, and pluginMaps accessors. Partner
secrets live nested under globalKeys.
Boot from baked-in KEYS, then overlay a signed infoRollup appKeys payload and device
cache. Mutate KEYS and globalKeys in place and rebuild pluginMaps.
Print only LAYER-* overlay markers from the local info_keys seed, plus whether the native signer loaded, so device e2e can confirm remote key fetch without dumping secrets.
Plugins whose API keys are absent or malformed do not register with the
core, which leaves them out of `currencyConfig` and `swapConfig`. Diff the
plugin list we handed to `makeEdgeContext` against what the account came
back with, and show the missing plugin IDs in an error drop-down so a
misconfigured key surfaces instead of silently removing assets and
exchanges from the app.

Plugin loading happens once per core context, so this reports once per
session rather than on every login.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@paullinator
paullinator force-pushed the paul/nativeHmacApiSigner branch from 44a9d19 to 568056f Compare September 5, 2026 00:20

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Token-only API key ignored
    • buildContextOptions now forwards a usable EDGE_API_KEY to MakeEdgeContext even when EDGE_API_SECRET is missing, so core can use legacy Token auth.

Create PR

Or push these changes by commenting:

@cursor push 61a8982da7
Preview (61a8982da7)
diff --git a/src/components/services/EdgeCoreManager.tsx b/src/components/services/EdgeCoreManager.tsx
--- a/src/components/services/EdgeCoreManager.tsx
+++ b/src/components/services/EdgeCoreManager.tsx
@@ -123,10 +123,12 @@
   const { EDGE_API_KEY: apiKey, EDGE_API_SECRET: apiSecret } = KEYS
   const nativeKey = hasNativeApiSigner() ? await warmNativeApiKey() : ''
   const nativeApiSigner = nativeKey !== '' ? makeNativeApiSigner() : undefined
-  const jsPair =
-    isUsableApiKey(apiKey) && apiSecret != null && apiSecret.byteLength > 0
+  // Token-only keys are valid: core uses `Authorization: Token {apiKey}`.
+  const jsPair = isUsableApiKey(apiKey)
+    ? apiSecret != null && apiSecret.byteLength > 0
       ? { apiKey, apiSecret }
-      : undefined
+      : { apiKey }
+    : undefined
   console.log(
     `[apiSigner] native=${nativeApiSigner != null} keysFallback=${
       jsPair != null

You can send follow-ups to the cloud agent here.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 568056f. Configure here.

return {
...(nativeApiSigner != null
? { apiSigner: nativeApiSigner }
: jsPair ?? {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Token-only API key ignored

Medium Severity

buildContextOptions only forwards JS credentials when both KEYS.EDGE_API_KEY and KEYS.EDGE_API_SECRET are present. An apiKey without a secret is dropped, so core never receives the legacy Token {apiKey} pair that HMAC_SIGNING.md still describes. The README also tells developers to set only EDGE_API_KEY in keys.json, which the native stub path then ignores.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 568056f. Configure here.

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