custody.unykorn.ai · native multisig custody

Custody that runs on your machine and refuses on its own.

Two signer processes, a control plane and a command line. Every movement is an intent that policy evaluates, a named person signs, and each signer rebuilds from the inputs before it will put a signature on it. No threshold-signing ceremony, no custodian API in the loop, no key that can leave the machine it was made on. Proven end to end on XRPL, Stellar and an EVM Safe on 12 September 2026 — every figure on this page resolves to a transaction hash or a receipt chain you can check.

3rails, live
12issuer operations, validated
20reference cases, byte-identical
293tests, Windows build
68 + 11 + 11receipts, chains verified
0keys in the control plane

Proof

The same pipeline, on three ledgers, with the hashes attached.

Intent → policy → counsel's signed approval → signer-a rebuilds and signs → signer-b rebuilds and signs → assemble → submit → validated → receipt. Testnets and a local chain, on purpose: the mainnet path is gated behind a typed phrase and a per-signature cap, described under Keys.

RailControlMovementResult
XRPL testnet Signer list K1×1 + K3×2, quorum 3. Clawback, default-ripple and require-auth set in the ledger's required order.rpP4bKgADf2d4JtsCYegPB1i9q98NBosyu 1.5 XRP tesSUCCESS, validated in 6.5 sC4CEB09DF5F84A698BA3911FCBFF53105FCB85B8F4D56FABE85E04E9A4821532
Stellar testnet Master weight 0, thresholds 3/3/3, K1×1 + K3×2.GBXCB5ZZIK7FBB5H2KNEMNJ2FDJ42IRX3FLOHZZQEVCSOACFUOIN25OX 2.5 XLM Ledger 4634955ffe8159c34c0a06799989ac353ebc910842725038f1b32c8eeb64f4bbda3de61
EVM · Safe v1.4.1 Real Safe bytecode at the canonical addresses on a local chain, 2-of-2 owners, relayer paid gas.0x62d36a993DE9cea424b7b6c7E209290c6C1D13b2 250 test tokens via execTransaction Receipt success, reconciliation clean0x3f723c5af0a711f35db90be0ae1251bc9bcef7071c5b4db41b961dd8caa9b24e (local chain — no public explorer)
What the reconciliation also said

The XRPL testnet account's master key is still enabled, so the signer list is not yet its only control. The check reported that as drift rather than hiding it. Disabling the master key is one more AccountSet and is part of the mainnet ceremony, not the testnet one.

The encoders were checked against ripple-binary-codec, @stellar/stellar-sdk and ethers on 20 reference cases (11 XRPL, 7 Stellar, 2 EVM — the Safe case covering the transaction hash, packed signatures and the execTransaction, setup and createProxyWithNonce calldata) before any of the above was submitted. Byte-identical, or the test fails.

Issuing, not only moving — 12 September, same signers

A stablecoin's whole lifecycle went through the pipeline on both testnets: issuer flags, holder authorisation, mint, clawback, and on XRPL an MPT issuance created and minted. Each row is one intent, one signed approval, two signer rebuilds, one validated transaction.

OperationRailWhat the ledger didTransaction
authorize_holderXRPL testnetIssuer-side TrustSet with tfSetfAuth for the holder's UNYD lineEA0658E9…8766E0D
issueXRPL testnet1,000 UNYD minted to the holder (a Payment from the issuer)88F1B907…8CD94F6
clawbackXRPL testnet250 UNYD clawed back from the holder6F0A745A…BD9BAAE8
mpt_createXRPL testnetMPTokenIssuanceCreate, scale 6, cap 1,000,000, RequireAuth · CanTransfer · CanClawback. Issuance id 013BB7940F334D03C35934FFC86FCB5124BA1B00FBDB7D65AC3BF038…B614737D6
authorize_holderXRPL testnetMPTokenAuthorize for the holder42E4FED0…8E43BCAF
issueXRPL testnet500 units of the MPT minted to the holderE50D67F5…690C12B8
account_flagsStellar testnetSetOptions: AUTH_REQUIRED · AUTH_REVOCABLE · AUTH_CLAWBACK_ENABLED on the issuer, through the multisig2b9322d9…ef5cd428a
authorize_holderStellar testnetSetTrustLineFlags AUTHORIZED on the holder's UNYD lineab1908af…fbb7a721
issueStellar testnet1,000 UNYD minted to the holder84b7e59b…52aeabc9
clawbackStellar testnet250 UNYD clawed back; holder ends at 750, authorised, clawback-enabled42b7299a…27608b0211
Refused on the way

Issuing on a wallet whose policy did not name issue — denied at intent. Issuing an asset whose issuer is somebody else's account — refused before any signer was asked. A self-issued “USDC” — blocked as counterfeit. A Stellar clawback before the issuer had the clawback flag — the ledger said op_not_clawback_enabled, the pipeline recorded the failure, the flag was set through account_flags, and a fresh trust line clawed back cleanly.

Twelve issuer operations validated in all. The two not in the table — the first Stellar holder's authorisation and mint, on a trust line that predates the clawback flag — are in the transcript. Full record: docs/e2e-2026-09-12/ISSUANCE-TRANSCRIPT.md.

Pipeline

Nine steps between “I want to move this” and a validated ledger entry.

Each step writes a receipt. The control plane keeps one chain; each signer keeps its own, so a control plane that lies has two witnesses that do not.

01
Intent

Wallet, destination, amount, asset, memo. Content-addressed: the same intent twice returns the original.

control plane
02
Protections

Counterfeit tickers, address-poisoning look-alikes, burned keys, EIP-7702 delegation on a signer, mainnet/testnet mixing. Findings are recorded whether or not they block.

control plane
03
Policy

Ceilings, whitelists, approver weights, operating windows. Deny by default. The decision and the policy digest are written to the receipt chain.

control plane
04
Signed approval

A named approver signs (approver, intent id, verdict, time) with an ed25519 key. The initiator may reject their own intent; they may not approve it.

a person
05
Signer-a

Verifies the intent id, the policy digest and pin, every approval signature against its own registry; runs policy and protections again; rebuilds the transaction; compares digests; scope-checks the key; signs; self-verifies; writes a receipt.

signer process
06
Signer-b

The same, independently, with its own key set, its own burned list and its own receipt chain.

signer process
07
Assemble

Signatures sorted the way each ledger requires — by account id on XRPL, by hint on Stellar, by owner address on Safe — and packed into the final transaction.

control plane
08
Submit and wait

Submitted, then polled until validated. Rate-limited public nodes are retried with backoff; a non-JSON answer is treated as “not validated”, never as success.

control plane
09
Receipt

Transaction hash, ledger index and the assembled bytes, hash-chained onto the record. /reconcile later finds the transaction on chain again and says so.

control plane

Invariants

The four things that make this different from a script that signs.

01
Signers rebuild. They never sign a handed digest.

The control plane sends inputs and a claimed digest. The signer recomputes the transaction from the inputs with the same deterministic builder and refuses on mismatch. There is no code path that signs an arbitrary 32 bytes.

02
Approvals are signatures, not database rows.

An approval is ed25519 over a domain-separated hash. The signer verifies it against a registry pinned at its own startup. A signed rejection outranks any number of approvals, from anyone, forever.

03
Policy runs three times.

Control plane, signer-a, signer-b. Each signer uses only the approval weight it verified itself. Editing the control plane's state to say “allowed” changes nothing the signers believe.

04
Protections run inside the signer.

Counterfeit assets, poisoned addresses, burned keys, 7702-delegated owners and testnet-to-mainnet mixing are refused in the signer with its own lists merged in — not only at the edge where a compromised edge could skip them.

The distinction between “we promise not to” and “the system will not permit it” is the whole difference between a counterparty taking our word and a counterparty not needing to.

Refused, live

What the running services would not do on 12 September.

Through the HTTP surface, against the same signers that produced the hashes above. Each refusal is in the receipt chains.

  • 60 XRP against a 50 XRP hard ceiling — denied at intent, before any signer saw it.
  • A self-issued “USDC” on XRPL — blocked as a counterfeit asset. Circle's issuer is rHuGNhqTG32mfmAvWA8hUyWRLV3tCSwKQt; nothing else gets to use the ticker. A local EVM token calling itself USDC — blocked, because there is no canonical USDC on that chain.
  • An approval signed by a key not in the registry — rejected.
  • A signed rejection after a valid approval — hard stop. The control plane's state.json was then edited by hand to flip the intent back to “allowed”. Both signers still refused: rejected by ["kevan"].
  • A Safe owner given an EIP-7702 delegation to a known drainer — execute denied, and /reconcile reported the delegation as critical drift.

State

Every component, with exactly how far it is built.

The same convention used across unykorn.ai. A component is not described as operating until it is.

REAL Running and proven against a ledger. GATED Built and tested; blocked on a named item. THIN Designed. Not built.
Chain adapters — XRPL, Stellar, EVM/SafeREAL

Address codecs, binary/XDR/RLP encoding, native-multisig signing data and assembly, RPC clients, the canonical stablecoin registry, the pre-sign protections. 20 reference cases, byte-identical to the reference libraries.

Encrypted software keystoreREAL

Argon2id → XChaCha20-Poly1305, secrets zeroized. Testnet-only by default; mainnet scope needs a typed phrase, an asset key and a per-signature cap.

Signer core and signer serviceREAL

The decision sequence under Pipeline, one process per key set, loopback only, constant-time token auth, its own receipt chain.

Control planeREAL

Intents, signed approvals, execute, protect, estate, reconcile, canonical assets, receipt chain. HTTP on loopback; the desktop shell and the command line both drive it.

Signed approvals and approver registryREAL

ed25519 approvals, domain-separated, verified in each signer against a pinned registry. Rejection outranks approval.

Operator surfaces — command line and desktopREAL

uny keys, uny approver, uny native; a desktop shell that spawns the built binaries on loopback and hosts the cockpit. The command line is the surface that has run the proof; the desktop shell is built and has not yet been run on the Windows box.

YubiHSM 2 backendGATED

Non-exportable keys, exercised against the mock HSM. Gated on hardware: plug-in is a configuration change.

Mainnet executionGATED

Nothing technical stops it once a key is mainnet-scoped. The gate is the typed phrase, the cap, and the recommendation that mainnet keys live in the HSM.

Safe on public testnetsGATED

Deploy works anywhere the canonical 1.4.1 factory exists. Run locally so far; gated on a funded relayer on a public testnet.

Custodian proxy — North Capital, Zero HashTHIN

A backend where a chartered custodian holds the signing key and this system is the policy co-signer and record. Needs their API credentials to build against.

Balance-vs-ledger reconciliationTHIN

/reconcile verifies control (signers, thresholds, flags, delegation) and history (every confirmed intent found on chain). Balances against an accounting ledger need the ledger to exist first.

Issuer operations — issue, authorise, claw back, MPT create, account flagsREAL

An operation on every intent, refused by policy unless the wallet's policy names it, rebuilt and checked by each signer like any transfer. Proven on XRPL and Stellar testnets on 12 September; EVM mint via the Safe is unit-tested against the mint(address,uint256) selector and not yet run on a chain.

Developer documentationREAL

Architecture, every HTTP endpoint, operations per ledger, policy rules, keys, CLI, runbook, integration guide and an OpenAPI 3.1 document, generated from the repository: custody.unykorn.ai/docs.

Threshold signing (TSS)THIN

Deferred on purpose. The maintained CGGMP21 crate carried a published advisory and its successor is pre-release; native on-ledger multisig gives the same “no single party” property with the ledger as the referee.

Keys

Who holds what, and the rule that keeps a laptop key from moving real money.

K1 · weight 1
The client

Their own key, on their own hardware. Signs under policy, never alone.

K3 · weight 2
Policy co-signer

Two signer processes that re-run policy on the byte-identical intent and sign only on match. Mandatory on every movement by weight configuration: a human and the policy, never two humans alone.

K2 · weight 1
A second person

Counsel, a director, a partner. Held by a different person than K1.

Quorum 3 on XRPL (SignerListSet), thresholds 3/3/3 on Stellar, an owner threshold on Safe. K1 + K2 alone is 2 and fails. K3 alone is 2 and fails. Nobody, including the operator of the co-signer, can act alone.

Key scope

Every software key is TestnetOnly unless it was created with the exact phrase uny keys phrase prints — “I understand this key lives on this computer and can move real funds” — plus an asset key and a per-signature cap. A signer refuses a mainnet payload from a testnet key, refuses a mainnet payload above the cap, and refuses a different asset. HSM keys carry the scope in hardware.

No wallet is created without a recovery plan that has been rehearsed: the key is reconstructed from real shares and one share short is confirmed to fail. If the rehearsal does not pass, no wallet exists and no funds can reach it.

Run it

Your machine. Two signers, a control plane, a shell.

Rust workspace, builds on Windows and Linux. Secrets are read from the environment or typed once into the unlock screen; nothing here takes a passphrase as a command-line argument, and keystores, seeds and state are never tracked.

# build once
cargo build --release

# keys, approvers, XRPL + Stellar testnet accounts, wallets, policies
$env:UNY_KEYSTORE_PASSPHRASE = "<long passphrase>"
.\run\bootstrap-testnet.ps1

# two signers on 7333 / 7334, control plane on 7331 — loopback only
$env:UNY_OPERATOR_PASSPHRASE = "<operator passphrase>"
.\run\start.ps1

# a movement: intent → policy → approval → two signers → ledger
.\run\move.ps1 -Wallet wal_treasury-xrp -Destination r... -Amount 1500000

.\run\stop.ps1

# or the desktop shell, after bootstrap
cd desktop; npm install; npm start
Where the record lives

Three receipt chains — control plane, signer-a, signer-b — each hash-chained, each verifiable with a public key, offline, without contacting anyone. GET /ledger/verify walks the control plane's chain; each signer answers /receipts for its own. On 12 September: 68, 11 and 11 receipts, all three chains verified.

Access

Look at the file, or run the software.

There is no sales process. The control architecture, the receipt chains and the transcript of the proof above are the file. We will walk any control through with your counsel alongside ours.

Control architecture review
Lenders · custodians · counsel · auditors

The invariants, the signer's decision sequence, the key-scope rule and the receipt format, against your own diligence standard.

Request the file
Policy co-signer for your wallets
Issuers · SPVs · treasuries

Your keys stay yours. K3 sits on your signer list at weight 2 and signs only what your own policy permits, with the record to prove it.

Start a conversation
Client portal
Live engagements

Per-deal workspace: custody statements, attestation history, and the append-only record of every sign-off on your file.

portal.unykorn.ai
Developer documentation
Engineers · integrators

Every endpoint, operation, rule and command, with the OpenAPI document. Built from the repository, so it cannot describe something that does not exist.

custody.unykorn.ai/docs
The platform
Real-world asset infrastructure

Custody is one layer. The gate, securitization, settlement and credit — how an asset becomes financeable — are on the main site.

unykorn.ai