/api/system/copy_undecryptable

POST

Support and diagnostics tool. Walks every .bin file in one wallet, classifies each against the confirmed session password, and copies the ones that password cannot decrypt into quarantine folders (Z/NeedsDecryption, Z/Encryption_Failed) inside the wallet. Copy only — sources are never moved, modified, or deleted, and existing quarantine copies are never overwritten.

POST http://localhost:8080/api/system/copy_undecryptable

Description

Encrypted Type 10 coin files intentionally carry no password verifier, so two failure modes are invisible to normal operation:

  • Cross-salt files — Type 10 files whose salt does not match the session's primary salt domain (typically imported or restored from a different wallet/password). Dangerous subtlety: such a file may appear to "decrypt" under the current password — the key-derivation math succeeds for any salt — but the output is garbage. The salt mismatch, not the decrypt result, is the only local signal.
  • Corrupt Type 10 files — files whose Type 10 header or CRC is damaged and cannot be parsed at all.

This endpoint makes both visible and collectable. Per file classified:

ClassificationAction
Decryptable under the session passwordCounted, left alone.
Plaintext (not Type 10)Counted, left alone.
Multi-coin fileCounted, left alone (multi-coin is export-only; not expected in wallet folders).
Cross-salt (different salt domain)Copied to Z/NeedsDecryption/; path reported.
Corrupt Type 10Copied to Z/Encryption_Failed/; path reported.

The walk covers the full coin-folder scan set of the wallet (Bank, Fracked, Limbo, Suspect, Grade, Pending, Import, Imported) and — unlike a normal wallet scan — includes files whose metadata fails to parse, so CRC-corrupt files are not silently skipped.

Copy semantics — safe by construction
  • Never automatic. Only runs when explicitly called. Nothing in the core quarantines files on its own.
  • Copy only. The source file stays exactly where it is, byte-identical.
  • Never overwrites. The destination is created exclusively; if a copy already exists it is counted as already_present and skipped. Re-running is safe and idempotent.
  • Gate re-checked per copy. If the session loses its confirmed state mid-run (concurrent logout), the remaining copies are aborted with 409.

Parameters

Parameter Type Required Description
wallet_path string Optional Wallet to scan. Must be a registered wallet. Default: the Default wallet.
dry_run bool Optional true/1/yes: classify and report, but write nothing. copied stays 0; already_present reflects a point-in-time check. Default: false.

Response

Success — 200 OK

{
  "command": "copy-undecryptable",
  "success": true,
  "dry_run": false,
  "truncated": false,
  "counts": {
    "scanned": 372,
    "decryptable": 369,
    "cross_salt": 2,
    "corrupt": 1,
    "multi": 0,
    "not_type10": 0,
    "copied": 3,
    "already_present": 0,
    "copy_errors": 0,
    "scan_errors": 0
  },
  "needs_decryption": [
    "Bank/2.0000001.bin",
    "Import/1.0000042.bin"
  ],
  "needs_decryption_truncated": false,
  "encryption_failed": [
    "Bank/5.0000007.bin"
  ],
  "encryption_failed_truncated": false
}

Response Fields

FieldTypeDescription
dry_runboolEcho of the request parameter.
truncatedbooltrue if the classification cap (2000 files) was reached before the walk finished. Re-run after resolving the reported files.
counts.scannedintNumber of .bin files classified.
counts.decryptableintType 10 files the session password decrypts.
counts.cross_saltintType 10 files from a different salt domain → Z/NeedsDecryption.
counts.corruptintUnparseable / CRC-damaged Type 10 files → Z/Encryption_Failed.
counts.multiintMulti-coin files (left alone).
counts.not_type10intPlaintext files (left alone).
counts.copiedintPhysical copies made this run (always 0 on dry_run).
counts.already_presentintQuarantine copies that already existed and were skipped.
counts.copy_errorsintCopies that failed (details in main.log).
counts.scan_errorsintFolder-walk errors (details in main.log).
needs_decryptionarrayWallet-relative paths of cross-salt files, capped at 100 entries (needs_decryption_truncated flags the cap).
encryption_failedarrayWallet-relative paths of corrupt files, capped at 100 entries (encryption_failed_truncated flags the cap).

Error Responses

409 — No confirmed password

{
  "error": true,
  "message": "Requires a confirmed password (load-password first)",
  "code": 409
}

Classification is meaningless without a reference password: "undecryptable" is always relative to the confirmed session key. Call /api/system/load-password and get key_state: "confirmed" first. The same 409 (message "Session is no longer confirmed — copies aborted") is returned if a logout lands mid-run — copies made before the abort remain.

405 — GET not allowed

Use POST.

400 — Bad wallet

Invalid wallet_path or wallet_path is not a registered wallet.

500 — Walk failed

Failed to walk wallet folders — filesystem-level failure enumerating the wallet.

Example Usage

# Preview what would be quarantined (no writes).
curl -s -X POST "http://localhost:8080/api/system/copy_undecryptable?dry_run=true"

# Real run on the Default wallet.
curl -s -X POST "http://localhost:8080/api/system/copy_undecryptable"
const res = await fetch(
  'http://localhost:8080/api/system/copy_undecryptable?dry_run=true',
  { method: 'POST' }
).then(r => r.json());

if (res.counts.cross_salt + res.counts.corrupt > 0) {
  // Show the user res.needs_decryption / res.encryption_failed,
  // then re-POST without dry_run to make the support copies.
}
import requests

base = 'http://localhost:8080'
r = requests.post(f'{base}/api/system/copy_undecryptable',
                  params={'dry_run': 'true'}).json()
print(r['counts'])
print('cross-salt:', r['needs_decryption'])
print('corrupt:', r['encryption_failed'])

Related Endpoints