Security
Security and assurance
What an integrator needs to know before routing funds through Valtu, and how to check it yourself instead of taking our word for it. Each item says plainly whether it exists today.
| Item | Status |
|---|---|
| Enclave attestation | Available: verify the running image with AWS's signature |
| Reproducible builds and measurements | Available. A second independent build is still owed. |
| Production keys | Created in an offline key ceremony (13 October 2026); public keys published below |
| External security audit | Not done yet: required before significant funds. An internal security review is done. |
| Supported chains, finality and reorganizations | Available |
| Webhook delivery guarantees | Available |
| Recovery procedures | Documented; restart, rollback and host failover drilled; full disaster recovery not yet drilled |
| Availability | Two enclave hosts in two zones, public status page. No uptime commitment yet. |
| SDK checksums | Available: reproducible package, published SHA-256 |
| Reporting a vulnerability | security@valtu.io |
Enclave attestation
Keys are generated, held and used only inside AWS Nitro Enclaves. You can check which image is running, at any moment, without trusting Valtu:
- Pick a random nonce (8 to 64 bytes) and call
GET /v1/attestation?nonce=<hex>. No API key is needed. - Each returned document is signed by AWS (COSE_Sign1, ES384) with a certificate that chains to the AWS Nitro Enclaves root. It carries the image measurements (PCR0/1/2), your nonce, so it was made just now for you, and the enclave's statement of the public keys it holds.
- Compare PCR0 with the published measurements, and the keys with the published public keys. Rulings, notarizations and signatures come from those keys, so they come from that image. Production runs two enclave hosts: you get one document per host, and every one must pass.
import { randomBytes } from "node:crypto";
import { verifyAttestation } from "@valtu/wallet-kit";
const nonce = randomBytes(32).toString("hex");
const { attestations } = await valtu.attestation(nonce);
for (const a of attestations) {
const v = verifyAttestation(a.document, { nonce, expectedPcr0: PUBLISHED_PCR0 });
console.log(v.pcrs[0], v.statement?.keys); // throws AttestationError if anything doesn't hold
}
GET /v1/attestation?nonce=9f2c…e1
200 OK
{"nonce": "9f2c…e1", "format": "aws-nitro-cose-sign1",
"attestations": [{"document": "hEShATgioFkRX…", "statement": {"v": 1, "service": "valtu-enclave", "role": "all",
"keys": {"policy": "02…", "signer": "03…", "notary": "02…", "quorum": "…"}}}]}
verifyAttestation has no dependencies, so you can read it in full. It checks the certificate chain up to AWS's root (pinned by its published SHA-256), each certificate's validity, the ES384 signature, the nonce and PCR0. Any COSE and X.509 library can do the same.
Reproducible builds and published measurements
The enclave image is built reproducibly. Everything that affects PCR0 is pinned: the Rust image, a Debian snapshot, timestamps, the Amazon Linux image and the nitro-cli version. Anyone with the source at the release commit gets the same measurements. KMS releases the keys' secrets only to an attestation whose image hash is the published PCR0.
| Release | Measurements |
|---|---|
Production, since 15 October 2026c3bdf5ecd389 | PCR0 3dc42f070f94fc08fda5d8e4beb9251321fbd1d2bf360f0d6c34b33c5c844e7ee712c1e05aa4bcaef920088b51523c30PCR1 4b4d5b3661b3efc12920900c80e126e4ce783c522de6c02a2a5bf7af3a2b9327b86776f188e4be1c1c404a129dbda493PCR2 8b61f0b81fbf6a146d0b275cbf2c8065a5842e2cffe6a787c186c19146ff637ecce715c28f442d0dea5e33e94428dd6fAnchors file SHA-256 769069031898576240b4189357a73eb728a78ee41728723216ac1ec03a4ee286 |
Previous, 14–15 October 2026b1a851982639 | PCR0 2f05574996f4a721ac86766860287a842f7424c41a1d80795bbbb0226a32d624ca0b64b665b89473eb5eacfefb7196d1PCR1 4b4d5b3661b3efc12920900c80e126e4ce783c522de6c02a2a5bf7af3a2b9327b86776f188e4be1c1c404a129dbda493PCR2 32d5c2fd1b2c6d046391153f6e4307dea9bf69811ad60dcd602f92883aee021794c97db8f8c49b4895094b3302bc6773Anchors file SHA-256 769069031898576240b4189357a73eb728a78ee41728723216ac1ec03a4ee286 |
This release was built twice from scratch with identical results. A second build by an independent person, on their own machine, is still owed before real funds. Source access for auditors and partners is available on request.
Production keys
The enclaves' keys were created on 13 October 2026 in an offline key ceremony, on a machine never connected to a network. Their public keys are compiled into the image (the anchors file above), so the measurement covers them, and every enclave reports them in its attestation. Signed results from Valtu verify against these keys:
| Quorum (seals wallet seeds) | 3697da95fcd2aa08a3a4499a8d1feb5938dfd23c91492068c905afdcddc91138 |
| Policy (rules on every request) | 0322b364e672d8158565a19cf299fbf8b8c580129e4e018827a6de1731412c5469 |
| Signer (signs transactions) | 0370f63d286dd7468e19448aa9ec912cc7965c45efe2b53e429cca5429034b1a43 |
| Notary (signs organization state) | 039e94af4524592494852ec55c64b7fab17613e4c20b1cd0aecf3d460d4ac201bf |
A backup of the secrets is split with Shamir secret sharing and held offline; recovering it needs a threshold of holders together (see the whitepaper). Wallets created before 14 October 2026 used keys from a test ceremony; production moved to these keys that day, and those wallets were given new addresses.
Audit, reserves and records
- External security audit: not done yet. The enclave applications, the coordinator, the deployment and these procedures still need review by an independent firm. It's required before the service holds significant customer funds.
- Internal security review: done (October 2026), by the team with automated review tools. Its findings were fixed. The last, released on 15 October, makes the enclave take its time from the hardware's signed attestation rather than the host's clock. It doesn't replace the external audit.
- Reserves.
GET /v1/balancesand every wallet address can be checked on-chain. Signed liability attestations aren't offered yet. - Records. The workspace audit log is append-only, enforced by the database, and exports to CSV. Enclave organization state is versioned append-only and notarized every 12 hours.
Supported chains, finality and reorganizations
Default confirmation depths for every network Valtu supports. Which networks are live, and the values in force, are what GET /v1/networks reports; the tokens per workspace are at GET /v1/assets. Live today: Ethereum, BNB Chain, Polygon, Base, Lisk, TRON and Bitcoin. Solana is paused.
| Network | Native | A deposit is final at | Notes |
|---|---|---|---|
| Ethereum | ETH | 12 blocks | |
| BNB Chain | BNB | 15 blocks | |
| Polygon | POL | 64 blocks | |
| Base | ETH | 12 blocks | |
| Arbitrum One | ETH | 20 blocks | Not live yet |
| OP Mainnet | ETH | 20 blocks | Not live yet |
| Avalanche C-Chain | AVAX | 12 blocks | Not live yet |
| Lisk | ETH | 12 blocks | |
| TRON | TRX | solidified block | Withdrawals final at 19 blocks. Fees can be paid with rented energy, using your own energy provider key (Gas station). |
| Solana | SOL | finalized commitment | Paused. Payments that wait for an approval use a durable nonce. |
| Bitcoin | BTC | 3 blocks | A payment unconfirmed for 30 minutes is replaced with a higher fee (replace-by-fee). No CPFP. |
Deposits are recorded PENDING when seen and become CONFIRMED only once they're deep enough and still on the canonical chain. That's the only state to credit on. Each block is checked to link to the one before it. A deposit dropped by a reorganization before then becomes ORPHANED. One dropped after confirmation becomes NEEDS_REVIEW, which means the depth was too shallow for that chain. Each status has its own webhook.
Withdrawals are CONFIRMED at the same depth. The same externalId always returns the same transfer, so a retry never pays twice. A withdrawal that can't be sent within 30 minutes (no funds or gas, signer unavailable, rejected by nodes) is CANCELLED. That happens only from states where the network never acknowledged the transaction. A broadcast transaction that's never accepted becomes NEEDS_REVIEW.
Known gaps:
- EVM deposits made by contract-internal transfers aren't detected without a tracing node.
- TRON, Solana and Bitcoin workspace transfers have run end to end only on test networks. Use capped amounts on them first.
Webhook delivery guarantees
| Delivery | At least once. De-duplicate on the event id. |
| Ordering | Not guaranteed. Act on the state in the event, not on arrival order. |
| Signatures | Ed25519 (X-Valtu-Signature-Ed25519, key at GET /v1/webhooks/public-key) and HMAC-SHA256 (X-Valtu-Signature), both over "<t>.<raw body>". Reject anything signed more than 5 minutes ago. |
| Success | Any 2xx within 10 seconds. |
| Retries | After failure n, the next attempt waits min(2n s, 1 h): 15 attempts over about 4 hours. Then the delivery is marked failed, and you can resend it from the dashboard. Attempts are kept for 30 days. |
| Catch-up | Webhooks are a notification, not the record. Reconcile with GET /v1/deposits?after= and GET /v1/transfers?after=. |
Recovery procedures
| Event | What happens and how it's recovered | Drilled |
|---|---|---|
| Enclave restart | Signing pauses. Deposits keep being recorded, and waiting withdrawals resume. The host fetches the published image, and KMS releases the secrets only to that measured image. About 1–2 minutes. | Yes |
| Bad release | The previous image is re-bound and restarted in about 2 minutes. Workspaces already using team signing move forward with a fixed release instead. | Yes, on staging |
| Enclave host down | The second host, in another zone, takes every request; the failed one rejoins after a health check. Requests in flight are retried on the other host. | Yes (13 October 2026) |
| Host loss | A replacement host boots the same image and is provisioned from KMS. | Not yet timed |
| Cloud keys or account lost | Threshold key holders recover the secrets on an air-gapped machine and re-encrypt them to new keys. | Not yet |
| Database | Multi-AZ, encrypted, 30 days of point-in-time backups, deletion protection. Signed organization state is append-only. A rollback window of up to 24 hours remains open until a monotonic counter is added. | Restore not yet drilled |
Availability
Production runs two enclave hosts and two API instances, each pair spread over two availability zones, with a Multi-AZ database. A monitor in a separate AWS region checks the website, the dashboard, the API, signing and each network's deposits every minute and publishes the results at status.valtu.io, with 90 days of history. The same summary is at GET /v1/status (no API key needed).
There's no uptime commitment yet.
SDK checksums
The wallet kit package is reproducible: the copy served here and a local build from the same commit are byte-for-byte identical. Checksums for the current release are at /site/downloads/SHA256SUMS.txt.
curl -sSLO https://valtu.io/site/downloads/valtu-wallet-kit-0.8.2.tgz
curl -sSL https://valtu.io/site/downloads/SHA256SUMS.txt | grep 0.8.2 | shasum -a 256 -c -Pin the exact version and its integrity hash in your lockfile; don't install -latest in production.
Reporting a vulnerability
Email security@valtu.io with the details and how to reproduce them, and please give us reasonable time to fix the issue before you disclose it. Don't access other customers' data or move funds beyond what you need to show the issue. Contact details are also at /.well-known/security.txt.