# User Wallet Data Format

Ty Everett (<ty@projectbabbage.com>)

## Abstract

This specification defines a canonical, complete, portable file format for exporting Wallet Toolbox data for a single user as one blob.

The format is intended to support:

* strong user data portability
* GDPR and similar access/export requirements
* wallet import and export
* storage-provider migration
* backup, archival, and forensic recovery

This specification is based on the Wallet Toolbox storage implementation. It defines a canonical plaintext payload format for a single-user export. Encryption and protected transport are out of scope for this document and belong in a separate extension.

## Motivation

Wallet data portability is only meaningful if the exported artifact is complete, stable, and implementation-neutral.

Exporting only high-level balances or transaction lists is insufficient. A serious wallet export must preserve:

* all user-owned records
* all user-linked proof and broadcast state
* deleted/tombstoned rows
* wallet-local metadata needed to reconstruct labels, baskets, certificates, sync state, and action history

Wallet Toolbox already has a rich schema that captures this information, but it does not yet define a canonical single-file export format. This specification fills that gap.

## Specification

### 1. Scope

This specification defines the canonical plaintext payload for exporting all Wallet Toolbox data for one user.

It does not define:

* encryption of the export blob
* compression of the export blob
* transport protocols for moving the blob
* multi-user export containers
* chain-wide or storage-global operational logs unrelated to one user
* root-key, profile, or encrypted snapshot material that is not part of the Wallet Toolbox storage schema

### 2. Conformance

An implementation conforms to BRC-38 if it can:

1. export a single user's Wallet Toolbox data into the canonical format defined here
2. parse a BRC-38 blob and reconstruct the exported dataset semantics
3. preserve all included row values, relationships, tombstones, and binary payloads without loss

### 3. Canonical File Form

The canonical BRC-38 artifact is a UTF-8 JSON document serialized according to RFC 8785 JSON Canonicalization Scheme (JCS).

The canonical unencrypted file extension SHOULD be:

* `.brc38.json`

If a deployment wraps, compresses, or encrypts the document, the canonical BRC-38 payload remains the inner JSON document defined here.

### 4. Top-Level Document

The canonical top-level object MUST have the following shape:

```json
{
  "brc": 38,
  "title": "User Wallet Data Format",
  "formatVersion": 1,
  "exportedAt": "2026-04-23T20:00:00.000Z",
  "sourceStorage": {
    "created_at": "2026-04-01T00:00:00.000Z",
    "updated_at": "2026-04-23T20:00:00.000Z",
    "storageIdentityKey": "<hex>",
    "storageName": "Primary Wallet Storage",
    "chain": "main",
    "dbtype": "SQLite",
    "maxOutputScript": 1024
  },
  "user": {
    "...": "..."
  },
  "tables": {
    "provenTxs": [],
    "provenTxReqs": [],
    "outputBaskets": [],
    "transactions": [],
    "commissions": [],
    "outputs": [],
    "outputTags": [],
    "outputTagMaps": [],
    "txLabels": [],
    "txLabelMaps": [],
    "certificates": [],
    "certificateFields": [],
    "syncStates": []
  }
}
```

Top-level requirements:

* `brc` MUST equal `38`.
* `title` MUST equal `User Wallet Data Format`.
* `formatVersion` MUST equal `1` for this version of the format.
* `exportedAt` MUST be the UTC timestamp at which the export payload was finalized.
* `sourceStorage` MUST contain the exported storage's current `settings` row in portable form.
* `user` MUST contain exactly one exported user row in portable form.
* `tables` MUST contain every array listed above, even if empty.

### 5. Canonical Value Encoding

#### 5.1 Timestamps

All timestamp values MUST be exported as strings in UTC using the exact form:

* `YYYY-MM-DDTHH:MM:SS.sssZ`

Offsets other than `Z` MUST NOT be used.

#### 5.2 Binary Data

Fields stored in Wallet Toolbox as `number[]` byte arrays MUST be exported as standard RFC 4648 base64 strings with padding.

No whitespace is permitted in base64 values.

The following fields are binary:

* `TableCommission.lockingScript`
* `TableOutput.lockingScript`
* `TableProvenTx.merklePath`
* `TableProvenTx.rawTx`
* `TableProvenTxReq.rawTx`
* `TableProvenTxReq.inputBEEF`
* `TableTransaction.inputBEEF`
* `TableTransaction.rawTx`

#### 5.3 Structured JSON-in-String Fields

The Wallet Toolbox schema stores some structured values as JSON strings. In BRC-38 these MUST be exported as decoded JSON values, not as embedded JSON strings.

The affected fields are:

* `TableProvenTxReq.history`
* `TableProvenTxReq.notify`
* `TableSyncState.syncMap`
* `TableSyncState.errorLocal`
* `TableSyncState.errorOther`

Their portable BRC-38 forms are:

* `history`: object
* `notify`: object
* `syncMap`: object
* `errorLocal`: object or omitted
* `errorOther`: object or omitted

#### 5.4 Optional Fields

If a field is absent or undefined in the source dataset, it MUST be omitted from the exported JSON rather than emitted as `null`.

### 6. Export Closure

The exported dataset for a user is the following closure over the Wallet Toolbox schema.

#### 6.1 Required Singletons

The export MUST include:

* exactly one `user` row for the requested user identity
* exactly one `sourceStorage` object derived from the exporting storage's settings row

#### 6.2 User-Owned Tables

The export MUST include every row from the following tables where `userId` equals the exported user's `userId`:

* `output_baskets`
* `transactions`
* `commissions`
* `outputs`
* `output_tags`
* `tx_labels`
* `certificates`
* `certificate_fields`
* `sync_states`

#### 6.3 User-Linked Map Tables

The export MUST include:

* every `tx_labels_map` row whose `txLabelId` references an exported `tx_labels` row
* every `output_tags_map` row whose `outputTagId` references an exported `output_tags` row

An exporter SHOULD additionally verify that:

* every exported `tx_labels_map.transactionId` references an exported transaction
* every exported `output_tags_map.outputId` references an exported output

#### 6.4 User-Linked Proof Tables

The export MUST include:

* every `proven_tx_reqs` row whose `txid` matches the `txid` of an exported transaction
* every `proven_txs` row whose `provenTxId` is referenced by:
  * an exported transaction, or
  * an exported `proven_tx_reqs` row

#### 6.5 Excluded Storage-Global Tables

The export MUST NOT include storage-global tables that are not scoped to a single user.

In the Wallet Toolbox implementation this means:

* `monitor_events` MUST NOT be included

### 7. Portable Row Forms

The BRC-38 portable row forms are defined below.

#### 7.1 User

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "userId": 1,
  "identityKey": "<hex>",
  "activeStorage": "<storageIdentityKey>"
}
```

#### 7.2 Proven Transaction

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "provenTxId": 7,
  "txid": "<hex>",
  "height": 900000,
  "index": 12,
  "merklePath": "<base64>",
  "rawTx": "<base64>",
  "blockHash": "<hex>",
  "merkleRoot": "<hex>"
}
```

#### 7.3 Proven Transaction Request

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "provenTxReqId": 8,
  "provenTxId": 7,
  "status": "completed",
  "attempts": 2,
  "notified": true,
  "txid": "<hex>",
  "batch": "abc123",
  "history": {
    "notes": []
  },
  "notify": {
    "transactionIds": [42]
  },
  "rawTx": "<base64>",
  "inputBEEF": "<base64>"
}
```

`history` MUST conform to:

```json
{
  "notes": [
    {
      "when": "2026-04-23T20:00:00.000Z",
      "what": "sent"
    }
  ]
}
```

`notify` MUST conform to:

```json
{
  "transactionIds": [1, 2, 3]
}
```

#### 7.4 Output Basket

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "basketId": 2,
  "userId": 1,
  "name": "default",
  "numberOfDesiredUTXOs": 32,
  "minimumDesiredUTXOValue": 1000,
  "isDeleted": false
}
```

#### 7.5 Transaction

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "transactionId": 42,
  "userId": 1,
  "provenTxId": 7,
  "status": "completed",
  "reference": "<base64>",
  "isOutgoing": true,
  "satoshis": 1234,
  "description": "Payment",
  "version": 1,
  "lockTime": 0,
  "txid": "<hex>",
  "inputBEEF": "<base64>",
  "rawTx": "<base64>"
}
```

#### 7.6 Commission

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "commissionId": 3,
  "userId": 1,
  "transactionId": 42,
  "satoshis": 5,
  "keyOffset": "<string>",
  "isRedeemed": false,
  "lockingScript": "<base64>"
}
```

#### 7.7 Output

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "outputId": 11,
  "userId": 1,
  "transactionId": 42,
  "basketId": 2,
  "spendable": true,
  "change": false,
  "outputDescription": "payment output",
  "vout": 0,
  "satoshis": 1234,
  "providedBy": "you",
  "purpose": "payment",
  "type": "P2PKH",
  "txid": "<hex>",
  "senderIdentityKey": "<hex>",
  "derivationPrefix": "<base64>",
  "derivationSuffix": "<base64>",
  "customInstructions": "optional",
  "spentBy": 99,
  "sequenceNumber": 0,
  "spendingDescription": "optional",
  "scriptLength": 25,
  "scriptOffset": 0,
  "lockingScript": "<base64>"
}
```

#### 7.8 Output Tag

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "outputTagId": 4,
  "userId": 1,
  "tag": "invoice",
  "isDeleted": false
}
```

#### 7.9 Output Tag Map

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "outputTagId": 4,
  "outputId": 11,
  "isDeleted": false
}
```

#### 7.10 Transaction Label

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "txLabelId": 5,
  "userId": 1,
  "label": "expenses",
  "isDeleted": false
}
```

#### 7.11 Transaction Label Map

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "txLabelId": 5,
  "transactionId": 42,
  "isDeleted": false
}
```

#### 7.12 Certificate

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "certificateId": 6,
  "userId": 1,
  "type": "<base64>",
  "serialNumber": "<base64>",
  "certifier": "<hex>",
  "subject": "<hex>",
  "verifier": "<hex>",
  "revocationOutpoint": "<txid>.<vout>",
  "signature": "<hex>",
  "isDeleted": false
}
```

#### 7.13 Certificate Field

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "userId": 1,
  "certificateId": 6,
  "fieldName": "email",
  "fieldValue": "alice@example.com",
  "masterKey": "<base64>"
}
```

#### 7.14 Sync State

```json
{
  "created_at": "2026-04-01T00:00:00.000Z",
  "updated_at": "2026-04-23T20:00:00.000Z",
  "syncStateId": 9,
  "userId": 1,
  "storageIdentityKey": "<hex>",
  "storageName": "Backup Storage",
  "status": "success",
  "init": true,
  "refNum": "<string>",
  "syncMap": {
    "provenTx": { "entityName": "provenTx", "idMap": {}, "count": 0 },
    "outputBasket": { "entityName": "outputBasket", "idMap": {}, "count": 0 },
    "transaction": { "entityName": "transaction", "idMap": {}, "count": 0 },
    "provenTxReq": { "entityName": "provenTxReq", "idMap": {}, "count": 0 },
    "txLabel": { "entityName": "txLabel", "idMap": {}, "count": 0 },
    "txLabelMap": { "entityName": "txLabelMap", "idMap": {}, "count": 0 },
    "output": { "entityName": "output", "idMap": {}, "count": 0 },
    "outputTag": { "entityName": "outputTag", "idMap": {}, "count": 0 },
    "outputTagMap": { "entityName": "outputTagMap", "idMap": {}, "count": 0 },
    "certificate": { "entityName": "certificate", "idMap": {}, "count": 0 },
    "certificateField": { "entityName": "certificateField", "idMap": {}, "count": 0 },
    "commission": { "entityName": "commission", "idMap": {}, "count": 0 }
  },
  "when": "2026-04-23T20:00:00.000Z",
  "satoshis": 123456,
  "errorLocal": {
    "code": "example",
    "description": "example",
    "stack": "optional"
  },
  "errorOther": {
    "code": "example",
    "description": "example"
  }
}
```

Each sync error object MUST conform to:

```json
{
  "code": "string",
  "description": "string",
  "stack": "optional string"
}
```

### 8. Array Ordering

For canonical serialization, the arrays inside `tables` MUST be sorted ascending as follows:

* `provenTxs` by `provenTxId`
* `provenTxReqs` by `provenTxReqId`
* `outputBaskets` by `basketId`
* `transactions` by `transactionId`
* `commissions` by `commissionId`
* `outputs` by `outputId`
* `outputTags` by `outputTagId`
* `outputTagMaps` by `outputId`, then `outputTagId`
* `txLabels` by `txLabelId`
* `txLabelMaps` by `transactionId`, then `txLabelId`
* `certificates` by `certificateId`
* `certificateFields` by `certificateId`, then `fieldName`
* `syncStates` by `syncStateId`

### 9. Relationship Integrity

An exporter MUST ensure that every foreign-key-like reference inside the payload resolves within the exported document, except where the source schema intentionally stores an optional unresolved reference.

At minimum:

* every exported `transaction.userId` MUST equal `user.userId`
* every exported `output.userId` MUST equal `user.userId`
* every exported `output.transactionId` MUST reference an exported transaction
* every exported `commission.transactionId` MUST reference an exported transaction
* every exported `txLabelMap.txLabelId` MUST reference an exported transaction label
* every exported `txLabelMap.transactionId` MUST reference an exported transaction
* every exported `outputTagMap.outputTagId` MUST reference an exported output tag
* every exported `outputTagMap.outputId` MUST reference an exported output
* every exported `certificateField.certificateId` MUST reference an exported certificate

If `user.activeStorage` does not equal `sourceStorage.storageIdentityKey`, the exporter MUST still preserve the exact `user.activeStorage` value.

### 10. Import Semantics

BRC-38 defines the portable payload, not a mandatory database insertion strategy.

Importers:

* MUST preserve row values and relationships semantically
* MAY preserve numeric primary IDs exactly
* MAY remap numeric primary IDs during import, provided every internal reference is updated consistently
* MUST preserve tombstones such as `isDeleted`
* MUST preserve status fields exactly

Importers MAY treat the following as operationally sensitive and choose whether to activate them immediately after import:

* `user.activeStorage`
* `syncStates`

However, a conforming importer MUST still parse and preserve those values in the imported dataset.

### 11. Privacy and Security

BRC-38 exports are intentionally complete and sensitive.

A BRC-38 blob may contain:

* raw transactions
* merkle proofs
* wallet labeling and description data
* certificate contents
* encrypted field material and derivation metadata
* sync history and storage topology hints

Implementations MUST treat BRC-38 blobs as highly sensitive user data.

### 12. Implementation Notes

This specification is based on the Wallet Toolbox schema:

* `users`
* `proven_txs`
* `proven_tx_reqs`
* `output_baskets`
* `transactions`
* `commissions`
* `outputs`
* `output_tags`
* `output_tags_map`
* `tx_labels`
* `tx_labels_map`
* `certificates`
* `certificate_fields`
* `sync_states`
* storage `settings` as export metadata

The storage-global `monitor_events` table is intentionally excluded because it is not scoped to a single user.

## References

* [BRC-40: User Wallet Data Synchronization](/outpoints/0040.md)
* [BRC-100: Unified, Vendor-Neutral, Unchanging, and Open BSV Blockchain Standard Wallet-to-Application Interface](/wallet/0100.md)
* [RFC 8785: JSON Canonicalization Scheme (JCS)](https://www.rfc-editor.org/rfc/rfc8785)
* [Wallet Toolbox Repository](https://github.com/bsv-blockchain/wallet-toolbox)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bsv.brc.dev/outpoints/0038.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
