Skip to content

feat(arcium): beacon-privacy integration (operator flow)#87

Draft
epicexcelsior wants to merge 8 commits into
anonmesh:stagingfrom
epicexcelsior:epic/arcium-beacon-privacy
Draft

feat(arcium): beacon-privacy integration (operator flow)#87
epicexcelsior wants to merge 8 commits into
anonmesh:stagingfrom
epicexcelsior:epic/arcium-beacon-privacy

Conversation

@epicexcelsior

Copy link
Copy Markdown
Collaborator

Draft — proven plumbing + full e2e; the production UX surface is a deliberate follow-up.

What this is

Integrates the anonbeta1 Arcium program (devnet) for the beacon-operator privacy flow: register a relay beacon whose RNS destination is encrypted + bound via Arcium MPC (never public), with relay throughput tracked in an encrypted on-chain counter only the operator can decrypt.

  • src/services/arcium/ — vendored MPC crypto (RescueCipher + Arcium PDA derivations, extracted from @arcium-hq/client@0.9.3 with node/anchor deps stripped and @noble v2 paths; validated byte-for-byte against the SDK), raw @solana/web3.js instruction builders, the operator service (register / init-stats / record-relay / status / decrypt), and the operator x25519 key in SecureStore.
  • app/dev/arcium-beacon.tsx — a __DEV__-only screen that drives the flow.

Why draft

This is the plumbing + proof, not a finished user feature — the only caller today is the dev route. Where (and whether) "run a private beacon" lives as a user-facing surface is an open product decision, intentionally a follow-up.

Tested end-to-end

  • Node devnet round-trip of the real modules: register → beacon_bind MPC → verified → init → record → relay_increment MPC → decrypted count = 1.
  • In-app crypto self-test under Hermes: 12/12 (RescueCipher matches the SDK golden vector; PDA derivations; instruction discriminators/sizes/account counts).
  • tsc --noEmit clean; Metro Android bundle clean.
  • Full signed round-trip on a physical Solana Seeker — real Seed Vault signatures + live Arcium MPC on devnet → on-screen decrypted relay count = 1.

Safety

  • Dev screen is hard-guarded on __DEV__ — production builds redirect home, so the surface never ships.
  • Operator x25519 secret lives only in the OS keystore (SecureStore); never logged or committed. No keys or .env in the diff.
  • No native-module / beacon co-sign dependency — the operator signs with its own wallet.
  • Targets devnet program anon7uu8UtVoFgS8GCSfw2RqyphJhkN3xEjgPwznYDe.

Out of scope

The public payment path (execute_cosigned_transfer) — that needs a co-sign change in the React Native LXMF module (separate lane).

Beacon-operator privacy flow for the anonbeta1 Arcium program (devnet):
- vendor/arciumCrypto.ts: RescueCipher + Arcium PDA derivations, extracted from
  @arcium-hq/client@0.9.3 with node/anchor deps stripped and @noble v2 paths;
  validated byte-for-byte against the upstream SDK.
- beaconInstructions.ts: raw @solana/web3.js builders for register_beacon_private,
  init_relay_stats, record_relay + manual account decoders (no anchor at runtime).
- constants.ts: program id, cluster offset 456, comp-def offsets, decode offsets.
- __tests__: deterministic crypto/encoding checks + a devnet integration test that
  drives the real modules through the full register -> bind -> relay -> decrypt
  round-trip. Both green against devnet.

Operator signs with its own wallet; no native-module or co-sign dependency.
- beaconClient.ts: registerBeacon / waitForBindingVerified / initRelayStats /
  recordRelay / waitForRelayRecorded / getBeaconStatus / getDecryptedRelayCount,
  driven through IWalletAdapter + IRpcAdapter. Async MPC steps are polled via
  getAccountInfo against the decoded account flags.
- beaconKeys.ts: operator x25519 keypair generated + persisted in the OS keystore
  (SecureKeys.ARCIUM_X25519_SECRET); secret never leaves the device.
- reuse: export signAndSubmitTransaction from sendTransaction (local + MWA signing);
  route x25519/randomBytes through the vendored module to avoid @noble v2 path churn.
- storage: register the new secure key explicitly per the key-registry convention.
app/dev/arcium-beacon.tsx — reachable via anonmesh://dev/arcium-beacon. Shows live
beacon status (registered / binding verified / relay stats / settlements /
decrypted relay count) and drives the 3-step operator flow with per-phase busy
state + error surfacing. Uses the wallet + network-mode adapters; no prod surface.
Adds runCryptoSelfTest() + a 'Run crypto self-test' button on the dev screen
(reachable without a wallet). Runs the golden-vector encrypt/decrypt + instruction
discriminator/size/account-count checks inside the React Native (Hermes) runtime
and logs the result. Verified PASS on a physical Solana Seeker — vendored
RescueCipher + builders are byte-identical to the SDK on-device.
Wrap the dev screen so production builds (where __DEV__ is statically false)
redirect home instead of rendering it — keeps the beacon-privacy dev surface
out of prod even via deep link. Operator key stays in SecureStore; no key material
is logged or committed.
@epicexcelsior epicexcelsior force-pushed the epic/arcium-beacon-privacy branch from 4f96f83 to 95154f9 Compare May 26, 2026 09:57
- Rename uppercase ARCIUM_* identifiers to precise MXE_*/ARX_* terms
  (MXE_FEE_POOL, MXE_CLOCK, ARX_PROGRAM_ADDR, SecureKeys.BEACON_X25519_SECRET;
  stored key string unchanged). The CI honesty guard bans the literal token to
  stop fake 'ARCIUM MPC' UI labels; the real integration shouldn't trip it.
- getMxeX25519Pubkey: validate length + reject an all-zero read so a layout
  drift fails loudly instead of as an opaque downstream MPC decrypt failure.
- buildCipher: zero the operator x25519 secret + derived shared secret after the
  cipher absorbs them (mirrors sendTransaction secret-key hygiene).
- registerBeacon: TODO marking the random rnsDestHash as a dev placeholder —
  production must pass the real Reticulum/LXMF destination to bind meaningfully.
…ps in prod

Move the operator screen to components/dev/ArciumBeaconScreen and have the route
require() it only inside an if(__DEV__) branch. In production __DEV__ is statically
false, so the require (and the screen's arcium-service imports) is dead-code-
eliminated — the dev surface never imports, initializes, or renders in prod.
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