WORK / SCIPHR

Self-custody identity where the backend cannot sign for a user.

Identity on the XRP Ledger, with an iOS app in the App Store and a backend that has no access to user keys.

Role
Design and engineering
Period
2024 to present
Status
Live, App Store
Surface
iOS app, API, XRPL
Stack
React Native, Node, GCP
Architecture
Device

Secure Enclave

  • Device auth key, P-256
  • Challenge and response

Keychain

  • XRPL master key
  • Regular key, rotatable

V5 envelope

  • AES-256-GCM
  • Device, iCloud and recovery-code wraps

WYSIWYS

  • Renders the exact intent
  • Signs locally
Backend, Cloud Run

Register

  • Public metadata only
  • Never a private key

Prepare

  • Builds the unsigned transaction
  • Returns the intent hash

Verify

  • Signed blob against the intent
  • A mismatch is rejected

Broadcast

  • Submits to the ledger
  • Returns the transaction hash
XRP Ledger

NFTokenMint

  • XLS-20 identity anchor
  • URI sciphr:v5:cid#sha256

SetRegularKey

  • Installs the regular signing key
  • Master stays cold

DIDSet

  • References the public DID document
  • Publicly resolvable

SignerListSet

  • Records signer-list authorization
  • Operator-assisted restore not deployed

No plaintext wallet key crosses the line between the device and the backend.

  1. The backend builds an unsigned transaction and returns it with the hash of what it intends.
  2. The device renders that exact intent for the user, who approves it, and signs on the device.
  3. The backend checks the signed blob against the intent it prepared. If they disagree, nothing is broadcast.
Content-addressed store. Envelopes live in GCS under a server-computed CIDv1, and the NFT URI anchors one by hash, so the ledger itself proves which envelope is authentic.
SciPHR. The backend prepares and verifies but cannot sign user-wallet transactions; wallet keys are generated on-device and only encrypted recovery material is stored off-device.

The backend was never allowed to hold a key.

Private keys and recovery secrets are generated on the device. Plaintext wallet secrets and day-to-day signing keys stay device-local; an encrypted master-key envelope can be stored off-device for recovery. SciPHR services cannot open that envelope or sign user-wallet transactions.

Most of the engineering in the product follows from that rule.

An encrypted key envelope, a rotating signing key, and verification before broadcast.

Identity is an XLS-20 NFT whose URI anchors an encrypted envelope by content hash. The master key is wrapped inside that envelope. Day-to-day signing uses a separate regular key that can be rotated and thrown away, so the master almost never has to come out.

The backend prepares an unsigned transaction, the device shows the user exactly what they are about to authorize and signs it locally, and the backend verifies the signed blob against the intent it prepared before broadcasting. If those two disagree, nothing goes to the ledger.

Recovery without a server-held key.

Recovery is where a self-custody design is easiest to compromise. An earlier version of ours had a service holding a co-signing seed. Holding that seed would have been custody, so it was removed.

What shipped instead: the user restores with a written recovery code. The ledger identifies the encrypted envelope in content-addressed storage; the device retrieves and verifies it, unwraps the master key locally and signs a transaction to install a fresh regular key. It was proven end to end on live testnet.

Approved by App Review. Live on the App Store.

Recovery was proven end to end on live testnet before any of it shipped, including the master-signed key rotation a real recovery depends on.

The app is in the App Store. The recovery path is the first thing I would put through an external audit, because it was redesigned late.

Next
Appraisal Intelligence
Case study →