/api/system/load-password

POST

Load the file-decryption password into RAM. While the password is loaded, encrypted (Type 10) coin files can be decrypted in memory as needed, and coin encryption operations become possible.

POST http://localhost:8080/api/system/load-password
⚠️ Renamed endpoint

This endpoint replaces /api/system/login. The old URL is no longer registered. Its counterpart, /api/system/logout, is replaced by /api/system/shutdown, which deletes the password from RAM and shuts the core down.

Description

/api/system/load-password accepts a password in a form-encoded POST body and holds the derived key in RAM. It is deliberately not called "login": no account exists, and nothing is stored on disk. The password is the only thing standing between the encrypted coin files and their authenticity numbers — the server keeps it (as a derived key) in memory until /api/system/shutdown is called or the process exits.

How the password is verified: encrypted Type 10 coin files intentionally contain no password check of any kind — nothing on disk can prove a password right or wrong. The core verifies a candidate password by decrypting one coin in memory and asking the RAIDA whether the resulting authenticity numbers are genuine, using the Detect Hash command (Group 1, Code 12): each RAIDA receives a SHA-256 hash bound to its own challenge, never the ANs themselves, so a wrong guess exposes nothing. If the RAIDA quorum rejects the hash, the response is a definitive 401 bad password. This requires network access; servers that do not support Detect Hash yet are handled by an encrypted-detect fallback automatically.

⚠️ Security Notes
  • POST only. GET requests are rejected with 405 — a password in a URL would be written to Client_Data/main.log and to any proxy or browser history logs.
  • Lost password = lost coins. Once files are encrypted, there is no recovery if the password is forgotten. The GUI must warn the user and require the password to be typed twice when encrypting for the first time.
  • UTF-8 byte exactness. The key is derived from the raw bytes the client sends. Encode non-ASCII characters as UTF-8 (form-encode with --data-urlencode / FormData) so every client derives the same key.
  • Use HTTPS in any deployment that is not loopback-only.

Client Flow — what to call, in what order

  1. Call GET /api/system/encryption-status at startup, before showing the dashboard.
    • If encrypted files exist and no key is loaded, prompt for the password before the dashboard appears.
    • If no encrypted files exist, no prompt is needed. The wallet is decrypted.
  2. Send the password with POST /api/system/load-password.
  3. On 401 bad password, tell the user the password is wrong and let them retry — this verdict is definitive (a RAIDA quorum rejected it). On 503, the verdict is inconclusive (network/scan trouble): keep the wallet read-only and retry; do NOT tell the user the password was wrong. On 200 with key_state: "confirmed", show the dashboard.
  4. To encrypt a decrypted wallet, call /api/system/encrypt_existing_files. To decrypt, call /api/system/decrypt_existing_files.
  5. When the user logs out, call POST /api/system/shutdown — it deletes the password from RAM and shuts the core down; the GUI should then exit too.

Parameters

Parameter Location Type Required Description
password POST body (form-encoded) string Required The user's password as raw UTF-8 bytes. Any characters are accepted. Minimum 1 byte; maximum 4096 bytes (HTTP layer ceiling).

Response

A 200 always carries key_state — the client MUST branch on it, not just on the status code.

200 — confirmed (encrypted files exist, RAIDA verified the password)

{
  "command": "load-password",
  "success": true,
  "key_set": true,
  "key_state": "confirmed",
  "raida": { "pass": 25, "fail": 0, "usable": 25 }
}

200 — establishing_ready (no encrypted files yet; password held for first encryption)

{
  "command": "load-password",
  "success": true,
  "key_set": true,
  "key_state": "establishing_ready",
  "message": "Password held for first encryption; nothing to verify against",
  "raida": { "pass": 0, "fail": 0, "usable": 0 }
}

200 — candidate (verification inconclusive; wallet stays read-only)

{
  "command": "load-password",
  "success": true,
  "key_set": true,
  "key_state": "candidate",
  "message": "Wallet is read-only until RAIDA confirmation succeeds",
  "raida": { "pass": 9, "fail": 2, "usable": 11 }
}

Response Fields

FieldTypeDescription
key_statestringconfirmed — RAIDA quorum verified the password; full read/write. candidate — could not reach a quorum verdict; wallet read-only, retry later. establishing_ready — no Type 10 files exist; password held in RAM for the first encrypt run. establishing — a first-encryption run is currently in flight.
key_setbooltrue on every 200: a password/derived key is now in RAM.
raidaobjectPer-verification RAIDA tallies: pass (servers that confirmed the ANs), fail (servers that rejected them), usable (servers that answered). Zeroes when there was nothing to verify against.

Error Responses

400 — Password missing

{
  "error": true,
  "message": "Missing required parameter: password",
  "code": 400
}

405 — GET not allowed

{
  "error": true,
  "message": "Use POST: a password in a GET URL would be written to server and proxy logs",
  "code": 405
}

401 — Bad password (definitive)

{
  "error": true,
  "message": "bad password",
  "code": 401,
  "detail": "(or coins are counterfeit)",
  "key_state": "none",
  "key_set": false,
  "raida": { "pass": 0, "fail": 25, "usable": 25 }
}

A RAIDA quorum rejected the derived authenticity numbers. The candidate key has been cleared from RAM; prompt the user to retype the password. This is the ONLY response that means "wrong password" — never infer a wrong password from a 503. (The detail field notes the one other possibility: the coin used for verification is itself counterfeit.)

503 — Inconclusive or cannot scan (NOT a wrong password)

Two messages share this code: Corrupt Type 10 coin file present; repair or remove before loading password (run copy_undecryptable to collect the damaged files) and Unable to scan wallet for encrypted files; try again (transient filesystem/scan trouble). In both cases keep the wallet locked and let the user retry — do not display "wrong password".

Example Usage

curl -X POST \
  -H "Content-Type: application/x-www-form-urlencoded; charset=utf-8" \
  --data-urlencode "password=¥CheeseCake£" \
  "http://localhost:8080/api/system/load-password"
const body = new URLSearchParams();
body.set('password', '¥CheeseCake£');

const res = await fetch('http://localhost:8080/api/system/load-password', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' },
  body: body
});
const data = await res.json();
if (res.status === 401) {
  // definitive wrong password — prompt to retype
} else if (res.status === 503) {
  // inconclusive — stay locked, offer retry; NOT a wrong password
} else if (data.key_state === 'confirmed' || data.key_state === 'establishing_ready') {
  // show the dashboard
} else if (data.key_state === 'candidate') {
  // read-only until RAIDA confirmation succeeds — retry load-password later
}
import requests

url = 'http://localhost:8080/api/system/load-password'
resp = requests.post(url, data={'password': '¥CheeseCake£'})
print(resp.json())
# {'command': 'load-password', 'success': True, 'key_set': True, ...}

Related Endpoints