G.A.N.D.A.L.F. · gandalf-v1-draft · HTML edition

G.A.N.D.A.L.F. Protocol

Version 1 Draft · Guarded Authenticated Non-custodial Distributed Access for Local Failover

  1. Guarded
  2. Authenticated
  3. Non-custodial
  4. Distributed
  5. Access
  6. Local
  7. Failover

Readable rendering of the early-draft gandalf-v1-draft plain-text specification. This page is not an IETF Internet-Draft and has not been submitted to the IETF. The downloadable .txt remains the archival RFC-style source. The provisional wire profile still uses the wallet-backup discriminator in the Gandalf Wallet reference implementation.

Protocol
GANDALF (G.A.N.D.A.L.F.)
Draft ID
gandalf-v1-draft
Status
Early Draft
Published
Wire label
wallet-backup · v1 (provisional)
Media type
application/json

Abstract

This document specifies GANDALF (Guarded Authenticated Non-custodial Distributed Access for Local Failover), an open, wallet-neutral encrypted backup family for password, custodian, and M-of-N wallet recovery. A wallet payload is encrypted once with a random content- encryption key. That key is divided into threshold shares, and every share is protected independently by either a password or an X25519 recipient.

Creation and recovery are local operations. No server, proxy, cloud service, analytics system, or other network participant is part of the cryptographic protocol.

Status of This Memo

This is the RFC-style text edition of the gandalf-v1-draft specification. It is not an IETF Internet-Draft and has not been submitted to the IETF. The draft is experimental: implementers SHOULD expect incompatible revisions before a frozen profile is declared.

The Gandalf Wallet reference implementation currently serializes the first concrete wire profile of this draft using the provisional discriminator "wallet-backup" and schema identifiers under gandalfwallet.com. Those identifiers are provisional wire labels for the gandalf-v1-draft profile; a later revision MAY rename them to align with the GANDALF public name without changing the security goals.

Implementations claiming compatibility with this draft MUST document which profile revision and vectors they consume.

Requirements Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC 2119] [RFC 8174] when, and only when, they appear in all capitals.

Table of Contents

1. Introduction

GANDALF (gandalf-v1-draft) is an encrypted backup family that separates an encrypted wallet payload from the credentials needed to recover it. The payload is encrypted exactly once with a uniformly random 32-byte content-encryption key (CEK). The CEK is divided into M-of-N bytewise Shamir shares. Each share is then protected independently by either a normalized password or an X25519 recipient. Mixed factor types are permitted. A password backup is the 1-of-1 case.

Media type
application/json
Protocol name
GANDALF (G.A.N.D.A.L.F.)
Draft identifier
gandalf-v1-draft
Wire discriminator
wallet-backup (provisional profile label)
Wire version
1

1.1. Goals

The protocol permits wallet software to express password-only, collaborative, and institutional recovery policies without relying on a wallet-specific recovery server. Backup files may be copied or stored by untrusted systems. Factor credentials are distributed separately from the backup envelope.

1.2. Threat Model

The protocol protects wallet metadata and secrets against disclosure and undetected modification when backup files are copied, stored by an untrusted cloud, or held by a custodian.

Confidentiality fails if an attacker obtains any valid threshold of factors, guesses enough weak passwords, compromises the creating or restoring endpoint, or replaces the wallet software. Availability is not guaranteed: factor loss, malicious deletion, or denial of service can make recovery impossible.

Backup creation and recovery are entirely local. No server, proxy, cloud service, analytics call, or other network participant is part of the cryptographic protocol.

2. Mandatory Algorithm Suite

Version 1 has one mandatory suite. Algorithm names are case-sensitive. Unknown values MUST be rejected.

Mandatory algorithms
PurposeMandatory algorithm
Payload and password-shareAES-256-GCM, 12-byte nonce,
authenticated encryption16-byte tag
Password KDFArgon2id v1.3, 65,536 KiB, 3 iterations, parallelism 1, 32-byte output
Password saltUnique random 16 bytes for each protected object
Public-key factorRFC 9180 base mode, DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-256-GCM
Secret sharingBytewise Shamir over GF(256), polynomial x^8+x^4+x^3+x+1
Hashes and fingerprintsSHA-256
Authenticated JSONRFC 8785 JSON Canonicalization Scheme (JCS)
Text and passwordUTF-8 after Unicode NFC
normalizationnormalization

Passwords MUST contain at least 12 Unicode scalar values after NFC normalization. Share indices are distinct integers from 1 through 255. Interactive version 1 clients MUST cap N at 16 even though the wire algorithm supports 255.

3. Encoding, Parsing, and Limits

Binary fields use padded standard Base64 as specified by Section 4 of [RFC 4648]. SHA-256 values use the form "sha256:" followed by exactly 64 lowercase hexadecimal digits. X25519 public and private keys are raw 32-byte values. An advanced raw private-key user interface MAY also accept exactly 64 hexadecimal digits.

Implementations MUST reject all of the following:

  • documents larger than 16 MiB or decrypted payloads larger than 8 MiB;
  • non-I-JSON values, floating-point protocol values, and integers outside the exact I-JSON range;
  • duplicate JSON members, factor identifiers, share indices, account identifiers, or context identifiers;
  • zero indices, empty identifiers, invalid thresholds, malformed keys, non-contributory X25519 inputs, unknown mandatory algorithms, and KDF parameters other than the exact version 1 values; and
  • payload digest mismatches, authenticated metadata changes, and shares or packages bound to another backup, ciphertext, threshold, index, or factor.

Parsers MUST reject unknown object members. Producers SHOULD serialize the complete document as JCS. Consumers authenticate the specified JCS projections and MAY accept insignificant wire whitespace or member ordering.

4. Public Backup Envelope

The normative envelope schema is wallet-backup-v1.schema.json.

Only the following values are deliberately public:

  • format and version;
  • algorithm identifiers;
  • backup identifier and creation time;
  • threshold policy;
  • factor identifiers and labels;
  • share indices and factor kinds;
  • X25519 fingerprints; and
  • cipher parameters and ciphertexts.

A factor label is a human-readable factor identifier and MUST NOT contain a wallet or account name.

Wallet names, source vault identifiers, account names and aliases, account identifiers, public keys, addresses, derivation data, secret sources, ledgers, genesis identities, and network contexts occur only in the encrypted payload.

payload.digest is SHA-256 over the decoded payload ciphertext, including the GCM authentication tag. The nonce is not included.

5. Encrypted Payload

The plaintext payload is the RFC 8785 encoding of this logical record:

   scope = VAULT | ACCOUNT(accountId, contextId)
   sourceVaultId, sourceCreatedAt, sourceNetworkId
   accounts[] = { metadata, secret }
   networkContexts[] = {
     contextId, accountId, ledger, networkId, genesisId,
     builtinProfileId?, alias?, finalityThreshold?
   }

metadata is the portable account identity. It includes the account identifier and name, typed account kind, account index, public key, primary address, derivation and address scheme, ledger, and every known derived address with its address-specific metadata.

secret is typed as Tetra entropy, validator private key, BIP-39 entropy, or Bitcoin WIF. New secret types require a new protocol version or an explicitly negotiated algorithm extension.

5.1. Account and Vault Scope

Every recovered account MUST have at least one immutable context.

An account-scope payload contains exactly one account and exactly its selected context. If its secret is shared BIP-39 entropy, clients MUST warn that the entropy can derive sibling accounts even though their metadata is not restored.

A vault-scope payload contains the selected vault's portable accounts and their required network contexts. Recovery always creates a fresh normal local vault; it does not restore the source vault envelope.

5.2. Network Identity and Excluded Data

A context identifies a ledger by ledger, numeric networkId, and immutable genesisId. builtinProfileId is only a remapping hint. Provider endpoints are never included.

Balances, UTXOs, transaction history, pending or provisional transactions, fee and provider responses, indexes, synchronization timestamps, explorer and provider endpoints, and all other caches MUST NOT be included. Restored clients reconstruct those values by synchronization.

6. Canonical Authenticated Data

Authenticated additional data (AAD) is the ASCII domain, followed by one zero byte, followed by the RFC 8785 bytes of the stated object.

6.1. Payload Protection

The payload domain is:

   wallet-backup-v1/payload

The payload AAD object is:

   {
     format, version, algorithms, backupId, createdAt,
     threshold, shareCount,
     factors: [{
       factorId, label, shareIndex, kind,
       recipientFingerprint?
     }]
   }

The producer generates a random 32-byte CEK and a random 12-byte nonce, then seals the canonical payload using AES-256-GCM and this AAD.

6.2. Protected Shares

The share binding is:

   {
     backupId, payloadDigest, threshold, shareIndex, factorId
   }

The share domain is:

   wallet-backup-v1/share

Its AAD object is { binding, factor }, where factor is the same descriptor used by the payload AAD.

For a password factor, the producer derives a key using the factor's KDF object and seals the 32 share bytes with AES-256-GCM.

For an HPKE factor, info is the domain "wallet-backup-v1/hpke-info" applied to the same { binding, factor }. HPKE seals the 32 share bytes with the share AAD. encapsulatedKey is the RFC 9180 enc output.

7. Shamir Construction

For each CEK byte independently, choose M-1 uniformly random coefficient bytes. The CEK byte is the constant term. Evaluate the polynomial at each public share index using GF(256) addition (XOR) and multiplication modulo 0x11b. Recovery uses Lagrange interpolation at x=0.

Coefficients, shares, and the reconstructed CEK MUST be zeroized promptly.

8. Recovery Procedure

A conforming implementation performs recovery as one atomic flow:

  1. Strictly parse and validate the envelope, mandatory suite, limits, identifiers, threshold, ciphertext lengths, unique salts and indices, and payload digest.
  2. Inspect the manifest without decrypting any content. Manifest inspection MUST NOT decrypt anything.
  3. Open supplied password factors, HPKE factors, or bound recovery-share packages. Duplicate credentials do not count twice. Invalid extra credentials MAY be ignored if at least M valid, distinct shares remain.
  4. Interpolate exactly M valid shares, decrypt the payload using the payload AAD, require canonical payload bytes, and validate its scope and context coverage.
  5. Re-derive every declared public key and address from its typed secret, derivation metadata, and network identity. Any mismatch aborts the entire operation.
  6. Create a fresh normal local vault with a new vault identifier, creation time, password, salt, and nonce. Plaintext secrets and shares MUST NOT cross a WASM or FFI boundary.

Legacy raw Gandalf VaultEnvelope files are not wallet-backup documents and MUST NOT be imported by this flow.

8.1. Network Remapping and Duplicate Accounts

Built-in network identities remap automatically by immutable identity. A custom identity requires explicit profile or connection confirmation. Endpoints are never silently restored.

Duplicate local account identifiers require an atomic review. Exact identities may remain while missing contexts and settings are added. Conflicting identities require typed confirmation plus the affected current vault passwords.

Replacement removes only colliding accounts, preserves unrelated accounts, clears the colliding accounts' synchronization, history, and pending caches, and deletes an empty superseded vault only after the transaction succeeds.

9. Custodian Key Files

The normative schema is wallet-custodian-key-v1.schema.json.

The public key and its fingerprint are visible. The raw 32-byte private key is encrypted with the mandatory password KDF and AES-256-GCM.

The AAD domain is:

   wallet-backup-v1/custodian-key

Its object contains format, version, algorithms, publicKey, fingerprint, kdf, cipherAlgorithm, and nonce. When opening a key file, implementations MUST derive the public key again and compare both the public key and fingerprint fields.

10. Recovery-Share Packages

The normative schema is wallet-recovery-share-v1.schema.json.

A custodian may open one original HPKE factor and re-encrypt only its 32-byte share with a new password.

The AAD domain is:

   wallet-backup-v1/recovery-share

Its object contains format, version, algorithms, the complete binding, kdf, cipherAlgorithm, and nonce.

Final recovery always requires the original backup. An implementation MUST reject a package whose binding differs byte for byte from the original factor binding.

11. Conformance Vectors

wallet-backup-v1.json contains deterministic, non-secret vectors for password-only, HPKE-only, mixed-factor, account-scope, vault-scope, and recovery-share-package cases.

Vector-only randomness is an incrementing byte stream beginning at the published seed. Production implementations MUST use a cryptographically secure random source.

Native Rust, WASM, and UniFFI implementations MUST produce and consume the vector bytes exactly. Passing the published draft vectors is the current interoperability contract for the provisional wallet-backup wire profile of gandalf-v1-draft.

12. Security Considerations

Implementations MUST observe all of the following requirements:

  • Password and private-key fields are cleared after every attempt, whether successful or not.
  • CEKs, Argon2 keys, private custodian keys, decrypted payload bytes, vault secrets, Shamir coefficients, and shares are zeroized as soon as practical and are never logged.
  • Failed recovery does not partially persist accounts, contexts, settings, or caches.
  • AES-GCM nonce uniqueness is REQUIRED per key. Random 96-bit nonces are generated independently.
  • Recipients compare displayed X25519 fingerprints before export. Duplicate public keys are rejected.
  • User-interface errors use the client's noninteractive error toast while leaving a retryable flow intact.

Password strength remains part of the security boundary. An attacker with an encrypted password factor can attempt guesses offline. The mandatory Argon2id parameters increase guess cost but cannot make a weak password strong.

Threshold protection does not protect a compromised creating or restoring endpoint. It also does not guarantee availability after factor loss or deletion. Users SHOULD keep factors geographically and organizationally separate where their recovery policy requires independence.

13. Gandalf Reference Implementation Boundary

gandalf-core is the sole owner of plaintext backup material. Its WASM and UniFFI adapters expose operations to:

  • generate encrypted custodian keys;
  • inspect manifests;
  • create backups;
  • open one factor for validation;
  • rewrap one HPKE share;
  • restore a threshold directly into a fresh VaultEnvelope; and
  • reconcile duplicate accounts by resealing affected vaults.

Factor-open calls return only the public factor descriptor. CEKs, shares, decrypted payloads, vault secrets, and private custodian keys never cross either boundary.

Duplicate reconciliation accepts exact and conflicting decisions, plus current passwords only for local vaults containing conflicts. It returns encrypted replacement envelopes and identifiers of now-empty vaults. Clients validate the result against current state and commit vaults, contexts, settings, and cache removals in one state transition.

14. IANA Considerations

This document has no IANA actions. "application/json" is used as the media type; "wallet-backup" and version 1 are fields within the JSON format and are not IANA registrations.

15. References

15.1. Normative References

  • [RFC 2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
  • [RFC 4648] Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, October 2006.
  • [RFC 8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, May 2017.
  • [RFC 8785] Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, June 2020.
  • [RFC 9180] Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid Public Key Encryption", RFC 9180, February 2022.

[SCHEMA-BACKUP]

           "wallet-backup-v1 JSON Schema", frozen version 1.

[SCHEMA-CUSTODIAN]

           "wallet-custodian-key-v1 JSON Schema", frozen version 1.

[SCHEMA-SHARE]

           "wallet-recovery-share-v1 JSON Schema", frozen version 1.
  • [VECTORS] "wallet-backup-v1 Deterministic Conformance Vectors", frozen version 1.

15.2. Informative References

  • [ARGON2] Biryukov, A., Dinu, D., Khovratovich, D., and S. Josefsson, "Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications", RFC 9106, September 2021.

Appendix A. Mandatory Algorithms Object

The algorithms object MUST equal the following JSON value:

{ "payloadCipher": "AES-256-GCM", "passwordKdf": "ARGON2ID-1.3", "passwordCipher": "AES-256-GCM", "secretSharing": "SHAMIR-GF256-BYTEWISE", "hpke": "DHKEM(X25519,HKDF-SHA256)/HKDF-SHA256/AES-256-GCM", "canonicalJson": "RFC8785" }

Appendix B. Normative Artifacts

The frozen schema identifiers remain under gandalfwallet.com. A documentation-domain change does not change these identifiers.

Backup envelope schema identifier: https://gandalfwallet.com/protocol/wallet-backup-v1.schema.json

Custodian key schema identifier: https://gandalfwallet.com/protocol/wallet-custodian-key-v1.schema.json

Recovery-share schema identifier: https://gandalfwallet.com/protocol/wallet-recovery-share-v1.schema.json

Repository source, schemas, vectors, and reference implementation: https://github.com/lmrrcc/gandalf/tree/main/gandalf-wallet

Authors' Address

Gandalf Contributors https://github.com/lmrrcc/gandalf

Plain-text source (ASCII) Same bytes as gandalf-v1-draft.txt — expand for monospaced archival view
Gandalf Protocol                                         Gandalf Contributors
Specification: gandalf-v1-draft                               21 July 2026
Category: Experimental / Standards Track (Draft)
Status: Early Draft


                    G.A.N.D.A.L.F. Protocol
                         Version 1 Draft

   Guarded Authenticated Non-custodial Distributed Access
                    for Local Failover


Abstract

   This document specifies GANDALF (Guarded Authenticated Non-custodial
   Distributed Access for Local Failover), an open, wallet-neutral
   encrypted backup family for password, custodian, and M-of-N wallet
   recovery.  A wallet payload is encrypted once with a random content-
   encryption key.  That key is divided into threshold shares, and every
   share is protected independently by either a password or an X25519
   recipient.

   Creation and recovery are local operations.  No server, proxy, cloud
   service, analytics system, or other network participant is part of
   the cryptographic protocol.

Status of This Memo

   This is the RFC-style text edition of the gandalf-v1-draft
   specification.  It is not an IETF Internet-Draft and has not been
   submitted to the IETF.  The draft is experimental: implementers
   SHOULD expect incompatible revisions before a frozen profile is
   declared.

   The Gandalf Wallet reference implementation currently serializes the
   first concrete wire profile of this draft using the provisional
   discriminator "wallet-backup" and schema identifiers under
   gandalfwallet.com.  Those identifiers are provisional wire labels for
   the gandalf-v1-draft profile; a later revision MAY rename them to
   align with the GANDALF public name without changing the security
   goals.

   Implementations claiming compatibility with this draft MUST document
   which profile revision and vectors they consume.

Requirements Language

   The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD
   NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in
   this document are to be interpreted as described in BCP 14
   [RFC2119] [RFC8174] when, and only when, they appear in all capitals.

Table of Contents

   1.  Introduction
     1.1.  Goals
     1.2.  Threat Model
   2.  Mandatory Algorithm Suite
   3.  Encoding, Parsing, and Limits
   4.  Public Backup Envelope
   5.  Encrypted Payload
     5.1.  Account and Vault Scope
     5.2.  Network Identity and Excluded Data
   6.  Canonical Authenticated Data
     6.1.  Payload Protection
     6.2.  Protected Shares
   7.  Shamir Construction
   8.  Recovery Procedure
     8.1.  Network Remapping and Duplicate Accounts
   9.  Custodian Key Files
   10. Recovery-Share Packages
   11. Conformance Vectors
   12. Security Considerations
   13. Gandalf Reference Implementation Boundary
   14. IANA Considerations
   15. References
   Appendix A. Mandatory Algorithms Object
   Appendix B. Normative Artifacts


1.  Introduction

   GANDALF (gandalf-v1-draft) is an encrypted backup family that separates
   an encrypted wallet payload from the credentials needed to recover it.
   The payload is encrypted exactly once with a uniformly random 32-byte
   content-encryption key (CEK).  The CEK is divided into M-of-N bytewise
   Shamir shares.  Each share is then protected independently by either
   a normalized password or an X25519 recipient.  Mixed factor types are
   permitted.  A password backup is the 1-of-1 case.

   Media type:             application/json
   Protocol name:          GANDALF (G.A.N.D.A.L.F.)
   Draft identifier:       gandalf-v1-draft
   Wire discriminator:     wallet-backup   (provisional profile label)
   Wire version:           1

1.1.  Goals

   The protocol permits wallet software to express password-only,
   collaborative, and institutional recovery policies without relying
   on a wallet-specific recovery server.  Backup files may be copied or
   stored by untrusted systems.  Factor credentials are distributed
   separately from the backup envelope.

1.2.  Threat Model

   The protocol protects wallet metadata and secrets against disclosure
   and undetected modification when backup files are copied, stored by
   an untrusted cloud, or held by a custodian.

   Confidentiality fails if an attacker obtains any valid threshold of
   factors, guesses enough weak passwords, compromises the creating or
   restoring endpoint, or replaces the wallet software.  Availability
   is not guaranteed: factor loss, malicious deletion, or denial of
   service can make recovery impossible.

   Backup creation and recovery are entirely local.  No server, proxy,
   cloud service, analytics call, or other network participant is part
   of the cryptographic protocol.


2.  Mandatory Algorithm Suite

   Version 1 has one mandatory suite.  Algorithm names are
   case-sensitive.  Unknown values MUST be rejected.

   Purpose                         Mandatory algorithm
   ------------------------------  -----------------------------------
   Payload and password-share      AES-256-GCM, 12-byte nonce,
     authenticated encryption        16-byte tag
   Password KDF                    Argon2id v1.3, 65,536 KiB,
                                     3 iterations, parallelism 1,
                                     32-byte output
   Password salt                   Unique random 16 bytes for each
                                     protected object
   Public-key factor               RFC 9180 base mode,
                                     DHKEM(X25519, HKDF-SHA256),
                                     HKDF-SHA256, AES-256-GCM
   Secret sharing                  Bytewise Shamir over GF(256),
                                     polynomial x^8+x^4+x^3+x+1
   Hashes and fingerprints         SHA-256
   Authenticated JSON              RFC 8785 JSON Canonicalization
                                     Scheme (JCS)
   Text and password               UTF-8 after Unicode NFC
     normalization                   normalization

   Passwords MUST contain at least 12 Unicode scalar values after NFC
   normalization.  Share indices are distinct integers from 1 through
   255.  Interactive version 1 clients MUST cap N at 16 even though the
   wire algorithm supports 255.


3.  Encoding, Parsing, and Limits

   Binary fields use padded standard Base64 as specified by Section 4 of
   [RFC4648].  SHA-256 values use the form "sha256:" followed by exactly
   64 lowercase hexadecimal digits.  X25519 public and private keys are
   raw 32-byte values.  An advanced raw private-key user interface MAY
   also accept exactly 64 hexadecimal digits.

   Implementations MUST reject all of the following:

   * documents larger than 16 MiB or decrypted payloads larger than
     8 MiB;

   * non-I-JSON values, floating-point protocol values, and integers
     outside the exact I-JSON range;

   * duplicate JSON members, factor identifiers, share indices, account
     identifiers, or context identifiers;

   * zero indices, empty identifiers, invalid thresholds, malformed
     keys, non-contributory X25519 inputs, unknown mandatory algorithms,
     and KDF parameters other than the exact version 1 values; and

   * payload digest mismatches, authenticated metadata changes, and
     shares or packages bound to another backup, ciphertext, threshold,
     index, or factor.

   Parsers MUST reject unknown object members.  Producers SHOULD
   serialize the complete document as JCS.  Consumers authenticate the
   specified JCS projections and MAY accept insignificant wire
   whitespace or member ordering.


4.  Public Backup Envelope

   The normative envelope schema is wallet-backup-v1.schema.json.

   Only the following values are deliberately public:

   * format and version;
   * algorithm identifiers;
   * backup identifier and creation time;
   * threshold policy;
   * factor identifiers and labels;
   * share indices and factor kinds;
   * X25519 fingerprints; and
   * cipher parameters and ciphertexts.

   A factor label is a human-readable factor identifier and MUST NOT
   contain a wallet or account name.

   Wallet names, source vault identifiers, account names and aliases,
   account identifiers, public keys, addresses, derivation data, secret
   sources, ledgers, genesis identities, and network contexts occur only
   in the encrypted payload.

   payload.digest is SHA-256 over the decoded payload ciphertext,
   including the GCM authentication tag.  The nonce is not included.


5.  Encrypted Payload

   The plaintext payload is the RFC 8785 encoding of this logical
   record:

      scope = VAULT | ACCOUNT(accountId, contextId)
      sourceVaultId, sourceCreatedAt, sourceNetworkId
      accounts[] = { metadata, secret }
      networkContexts[] = {
        contextId, accountId, ledger, networkId, genesisId,
        builtinProfileId?, alias?, finalityThreshold?
      }

   metadata is the portable account identity.  It includes the account
   identifier and name, typed account kind, account index, public key,
   primary address, derivation and address scheme, ledger, and every
   known derived address with its address-specific metadata.

   secret is typed as Tetra entropy, validator private key, BIP-39
   entropy, or Bitcoin WIF.  New secret types require a new protocol
   version or an explicitly negotiated algorithm extension.

5.1.  Account and Vault Scope

   Every recovered account MUST have at least one immutable context.

   An account-scope payload contains exactly one account and exactly its
   selected context.  If its secret is shared BIP-39 entropy, clients
   MUST warn that the entropy can derive sibling accounts even though
   their metadata is not restored.

   A vault-scope payload contains the selected vault's portable accounts
   and their required network contexts.  Recovery always creates a fresh
   normal local vault; it does not restore the source vault envelope.

5.2.  Network Identity and Excluded Data

   A context identifies a ledger by ledger, numeric networkId, and
   immutable genesisId.  builtinProfileId is only a remapping hint.
   Provider endpoints are never included.

   Balances, UTXOs, transaction history, pending or provisional
   transactions, fee and provider responses, indexes, synchronization
   timestamps, explorer and provider endpoints, and all other caches
   MUST NOT be included.  Restored clients reconstruct those values by
   synchronization.


6.  Canonical Authenticated Data

   Authenticated additional data (AAD) is the ASCII domain, followed by
   one zero byte, followed by the RFC 8785 bytes of the stated object.

6.1.  Payload Protection

   The payload domain is:

      wallet-backup-v1/payload

   The payload AAD object is:

      {
        format, version, algorithms, backupId, createdAt,
        threshold, shareCount,
        factors: [{
          factorId, label, shareIndex, kind,
          recipientFingerprint?
        }]
      }

   The producer generates a random 32-byte CEK and a random 12-byte
   nonce, then seals the canonical payload using AES-256-GCM and this
   AAD.

6.2.  Protected Shares

   The share binding is:

      {
        backupId, payloadDigest, threshold, shareIndex, factorId
      }

   The share domain is:

      wallet-backup-v1/share

   Its AAD object is { binding, factor }, where factor is the same
   descriptor used by the payload AAD.

   For a password factor, the producer derives a key using the factor's
   KDF object and seals the 32 share bytes with AES-256-GCM.

   For an HPKE factor, info is the domain
   "wallet-backup-v1/hpke-info" applied to the same
   { binding, factor }.  HPKE seals the 32 share bytes with the share
   AAD.  encapsulatedKey is the RFC 9180 enc output.


7.  Shamir Construction

   For each CEK byte independently, choose M-1 uniformly random
   coefficient bytes.  The CEK byte is the constant term.  Evaluate the
   polynomial at each public share index using GF(256) addition (XOR)
   and multiplication modulo 0x11b.  Recovery uses Lagrange
   interpolation at x=0.

   Coefficients, shares, and the reconstructed CEK MUST be zeroized
   promptly.


8.  Recovery Procedure

   A conforming implementation performs recovery as one atomic flow:

   1.  Strictly parse and validate the envelope, mandatory suite,
       limits, identifiers, threshold, ciphertext lengths, unique salts
       and indices, and payload digest.

   2.  Inspect the manifest without decrypting any content.  Manifest
       inspection MUST NOT decrypt anything.

   3.  Open supplied password factors, HPKE factors, or bound
       recovery-share packages.  Duplicate credentials do not count
       twice.  Invalid extra credentials MAY be ignored if at least M
       valid, distinct shares remain.

   4.  Interpolate exactly M valid shares, decrypt the payload using the
       payload AAD, require canonical payload bytes, and validate its
       scope and context coverage.

   5.  Re-derive every declared public key and address from its typed
       secret, derivation metadata, and network identity.  Any mismatch
       aborts the entire operation.

   6.  Create a fresh normal local vault with a new vault identifier,
       creation time, password, salt, and nonce.  Plaintext secrets and
       shares MUST NOT cross a WASM or FFI boundary.

   Legacy raw Gandalf VaultEnvelope files are not wallet-backup
   documents and MUST NOT be imported by this flow.

8.1.  Network Remapping and Duplicate Accounts

   Built-in network identities remap automatically by immutable
   identity.  A custom identity requires explicit profile or connection
   confirmation.  Endpoints are never silently restored.

   Duplicate local account identifiers require an atomic review.  Exact
   identities may remain while missing contexts and settings are added.
   Conflicting identities require typed confirmation plus the affected
   current vault passwords.

   Replacement removes only colliding accounts, preserves unrelated
   accounts, clears the colliding accounts' synchronization, history,
   and pending caches, and deletes an empty superseded vault only after
   the transaction succeeds.


9.  Custodian Key Files

   The normative schema is wallet-custodian-key-v1.schema.json.

   The public key and its fingerprint are visible.  The raw 32-byte
   private key is encrypted with the mandatory password KDF and
   AES-256-GCM.

   The AAD domain is:

      wallet-backup-v1/custodian-key

   Its object contains format, version, algorithms, publicKey,
   fingerprint, kdf, cipherAlgorithm, and nonce.  When opening a key
   file, implementations MUST derive the public key again and compare
   both the public key and fingerprint fields.


10.  Recovery-Share Packages

   The normative schema is wallet-recovery-share-v1.schema.json.

   A custodian may open one original HPKE factor and re-encrypt only its
   32-byte share with a new password.

   The AAD domain is:

      wallet-backup-v1/recovery-share

   Its object contains format, version, algorithms, the complete
   binding, kdf, cipherAlgorithm, and nonce.

   Final recovery always requires the original backup.  An
   implementation MUST reject a package whose binding differs byte for
   byte from the original factor binding.


11.  Conformance Vectors

   wallet-backup-v1.json contains deterministic, non-secret vectors for
   password-only, HPKE-only, mixed-factor, account-scope, vault-scope,
   and recovery-share-package cases.

   Vector-only randomness is an incrementing byte stream beginning at
   the published seed.  Production implementations MUST use a
   cryptographically secure random source.

   Native Rust, WASM, and UniFFI implementations MUST produce and
   consume the vector bytes exactly.  Passing the published draft
   vectors is the current interoperability contract for the provisional
   wallet-backup wire profile of gandalf-v1-draft.


12.  Security Considerations

   Implementations MUST observe all of the following requirements:

   * Password and private-key fields are cleared after every attempt,
     whether successful or not.

   * CEKs, Argon2 keys, private custodian keys, decrypted payload bytes,
     vault secrets, Shamir coefficients, and shares are zeroized as soon
     as practical and are never logged.

   * Failed recovery does not partially persist accounts, contexts,
     settings, or caches.

   * AES-GCM nonce uniqueness is REQUIRED per key.  Random 96-bit
     nonces are generated independently.

   * Recipients compare displayed X25519 fingerprints before export.
     Duplicate public keys are rejected.

   * User-interface errors use the client's noninteractive error toast
     while leaving a retryable flow intact.

   Password strength remains part of the security boundary.  An
   attacker with an encrypted password factor can attempt guesses
   offline.  The mandatory Argon2id parameters increase guess cost but
   cannot make a weak password strong.

   Threshold protection does not protect a compromised creating or
   restoring endpoint.  It also does not guarantee availability after
   factor loss or deletion.  Users SHOULD keep factors geographically
   and organizationally separate where their recovery policy requires
   independence.


13.  Gandalf Reference Implementation Boundary

   gandalf-core is the sole owner of plaintext backup material.  Its
   WASM and UniFFI adapters expose operations to:

   * generate encrypted custodian keys;
   * inspect manifests;
   * create backups;
   * open one factor for validation;
   * rewrap one HPKE share;
   * restore a threshold directly into a fresh VaultEnvelope; and
   * reconcile duplicate accounts by resealing affected vaults.

   Factor-open calls return only the public factor descriptor.  CEKs,
   shares, decrypted payloads, vault secrets, and private custodian keys
   never cross either boundary.

   Duplicate reconciliation accepts exact and conflicting decisions,
   plus current passwords only for local vaults containing conflicts.
   It returns encrypted replacement envelopes and identifiers of
   now-empty vaults.  Clients validate the result against current state
   and commit vaults, contexts, settings, and cache removals in one state
   transition.


14.  IANA Considerations

   This document has no IANA actions.  "application/json" is used as the
   media type; "wallet-backup" and version 1 are fields within the JSON
   format and are not IANA registrations.


15.  References

15.1.  Normative References

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119, March 1997.

   [RFC4648]  Josefsson, S., "The Base16, Base32, and Base64 Data
              Encodings", RFC 4648, October 2006.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, May 2017.

   [RFC8785]  Rundgren, A., Jordan, B., and S. Erdtman, "JSON
              Canonicalization Scheme (JCS)", RFC 8785, June 2020.

   [RFC9180]  Barnes, R., Bhargavan, K., Lipp, B., and C. Wood,
              "Hybrid Public Key Encryption", RFC 9180, February 2022.

   [SCHEMA-BACKUP]
              "wallet-backup-v1 JSON Schema", frozen version 1.

   [SCHEMA-CUSTODIAN]
              "wallet-custodian-key-v1 JSON Schema", frozen version 1.

   [SCHEMA-SHARE]
              "wallet-recovery-share-v1 JSON Schema", frozen version 1.

   [VECTORS]  "wallet-backup-v1 Deterministic Conformance Vectors",
              frozen version 1.

15.2.  Informative References

   [ARGON2]   Biryukov, A., Dinu, D., Khovratovich, D., and S.
              Josefsson, "Argon2 Memory-Hard Function for Password
              Hashing and Proof-of-Work Applications", RFC 9106,
              September 2021.


Appendix A.  Mandatory Algorithms Object

   The algorithms object MUST equal the following JSON value:

   {
     "payloadCipher": "AES-256-GCM",
     "passwordKdf": "ARGON2ID-1.3",
     "passwordCipher": "AES-256-GCM",
     "secretSharing": "SHAMIR-GF256-BYTEWISE",
     "hpke": "DHKEM(X25519,HKDF-SHA256)/HKDF-SHA256/AES-256-GCM",
     "canonicalJson": "RFC8785"
   }


Appendix B.  Normative Artifacts

   The frozen schema identifiers remain under gandalfwallet.com.  A
   documentation-domain change does not change these identifiers.

   Backup envelope schema identifier:
   https://gandalfwallet.com/protocol/wallet-backup-v1.schema.json

   Custodian key schema identifier:
   https://gandalfwallet.com/protocol/wallet-custodian-key-v1.schema.json

   Recovery-share schema identifier:
   https://gandalfwallet.com/protocol/wallet-recovery-share-v1.schema.json

   Repository source, schemas, vectors, and reference implementation:
   https://github.com/lmrrcc/gandalf/tree/main/gandalf-wallet


Authors' Address

   Gandalf Contributors
   https://github.com/lmrrcc/gandalf