Connect a reserve source
How a custodian signs reserve balances, how Solva reads balances on chain, and how each kind of reserve source is connected today.
Each cycle, the orchestrator reads your reserve figures from your reserve source. With an attested source, the custodian signs each balance and the orchestrator checks the signature before it uses the figure. With a read on chain source, the orchestrator reads the balance of your Stellar accounts from the ledger. For how much each kind asks you to trust, see Where reserve figures come from.
Source types
| Source | Setting value | How the figure arrives |
|---|---|---|
| Attested (custodian-signed) | attested | The custodian's service signs each balance it reports. |
| Read on chain | onchain | The orchestrator reads the balance of Stellar accounts from Horizon. |
The public demo uses a mock custodian run by Solva for two example institutions, Kestrel Anchor and Solstice Exchange, so their figures are not real reserves. Its third example institution, Harbor Wallet, holds real Testnet USDC read on chain.
What the custodian signs
For each reserve account, the custodian's service answers GET {base}/v1/accounts/{account}/balance with a JSON body:
{
"source_id": "acct-anchor",
"balance": "8000000",
"currency": "USD",
"as_of": "2026-11-04T09:00:00Z",
"signature": "MEUCIQ..."
}| Field | Meaning |
|---|---|
source_id | The reserve account the figure belongs to. |
balance | The balance as a decimal string of integer minor units, such as cents. |
currency | The currency code of the balance. |
as_of | The time of the balance, in RFC 3339 format. |
signature | The custodian's signature over the other four fields, base64 encoded. |
The signature covers a canonical payload: the four fields as compact JSON, keys in alphabetical order, with no signature field:
{
"as_of": "2026-11-04T09:00:00Z",
"balance": "8000000",
"currency": "USD",
"source_id": "acct-anchor"
}The signature scheme
| Part | Value |
|---|---|
| Curve | P-256 (secp256r1) |
| Digest | SHA-256 over the canonical payload bytes |
| Signature | ECDSA, ASN.1 DER encoding of (r, s), then base64 |
| Key file | The custodian's public key as a PEM (PKIX) file |
How the orchestrator checks a balance
On every cycle the orchestrator:
- Authenticates to the source with an OAuth authorization code, when a client id is set, and gets a bearer token.
- Reads every configured reserve account in parallel.
- Rebuilds the canonical payload from each response and verifies the signature with the configured public key.
- Uses the figure only if the signature verifies.
A bad signature fails the whole cycle at once, and the orchestrator does not retry it. A network error or a 5xx answer from the source is retried up to three times with a growing delay. If any one account fails, no partial set of reserves is used.
The orchestrator checks the signature only. It does not check the age of as_of or the currency. Make sure every reserve figure and every customer balance use the same asset and the same minor unit, because the proof adds them up as plain numbers.
Connect an attested source
A reserve source is set in the orchestrator's configuration, not in the console. A tenant that is not routed to onchain (see Connect a read on chain source) uses the attested source. The engineer who runs the orchestrator sets these values and restarts it:
| Setting | Value |
|---|---|
ORCH_RESERVE_BASE_URL | The base URL of the custodian's balance service. |
ORCH_RESERVE_ACCOUNTS | The reserve account ids, comma separated. Up to 4 figures fit in one proof. |
ORCH_RESERVE_PUBLIC_KEY_PEM | The custodian's P-256 public key, PEM encoded. The orchestrator refuses to start without it. |
ORCH_RESERVE_CLIENT_ID | The OAuth client id the orchestrator presents. Leave it empty to send no token. |
Get the custodian's public key as a PEM file and the ids of the accounts that hold your reserves.
Set the four settings above on the orchestrator.
export ORCH_RESERVE_BASE_URL="https://custody.example.com"
export ORCH_RESERVE_ACCOUNTS="acct-anchor,acct-beacon,acct-cedar"
export ORCH_RESERVE_PUBLIC_KEY_PEM="$(cat custodian-public-key.pem)"
export ORCH_RESERVE_CLIENT_ID="solva-orchestrator"Restart the orchestrator. It reads the settings at start.
Run a cycle, as in Run cycles, and check that it returns 202.
One orchestrator holds one set of attested settings. Every institution that uses the attested source on that orchestrator reads the same accounts with the same key. Real custodian connections Coming soon give each institution its own.
Connect a read on chain source
An institution whose reserves sit in Stellar accounts uses the read on chain source. Two orchestrator settings route it. The example is Harbor Wallet's configuration in the demo:
ORCH_RESERVE_SOURCES="33333333-3333-3333-3333-333333333333=onchain"
ORCH_ONCHAIN_HOLDINGS="33333333-3333-3333-3333-333333333333=GCPBHMM7KSE74LO757SPKBE3PHWYRTKKKVBCVZURBFM6B2GSOPAJAR4R|USDC|GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"| Setting | Value |
|---|---|
ORCH_RESERVE_SOURCES | Comma-separated TENANT_ID=attested or TENANT_ID=onchain entries. A tenant not listed uses attested. |
ORCH_ONCHAIN_HOLDINGS | Comma-separated holdings, TENANT_ID=ADDRESS for XLM or TENANT_ID=ADDRESS|CODE|ISSUER for an issued asset. A tenant can list several accounts. |
ORCH_ONCHAIN_HORIZON_URL | The Horizon server to read from. Defaults to https://horizon-testnet.stellar.org. |
On every cycle the orchestrator reads each holding's account from Horizon in parallel and takes the balance of the named asset. Each balance becomes one reserve figure in the asset's seven-decimal unit, so 10 USDC is 100000000. Load customer balances in the same unit. A network error or a 5xx answer from Horizon is retried up to three times. An account that does not hold the named asset fails the cycle.
The orchestrator refuses to start if an entry is malformed, if an address is not a valid G... account, or if a tenant routed to onchain has no holdings.
Reserves read on chain move with the ledger. A proof is refused when reserves grow by more than 10 percent since the last record, so add funds in smaller steps.
Connect a source in the console
Console source connections Coming soon let each institution connect its own reserve sources. The console's Sources page has a Connect a source dialog with two choices:
- Custodian account. Choose your custodian from a list, then authorize read-only access to account balances. Solva never moves funds and never sees transactions. You can revoke access at any time.
- On-chain wallet. Give the source a display name and enter the Stellar address (
G...) that holds the reserves.
Each connected source appears on the Sources page. The dialog runs with example custodians, and no cycle reads what it connects.
Result
The orchestrator reads your reserve accounts each cycle. From an attested source it uses only figures whose signatures verify. From a read on chain source it uses the balances the ledger shows.
Next: Load what is owed.