Summary
Delegate passphrases are currently stored in plain text in the node config file. This issue tracks adding support for operator-password-encrypted passphrases so that a compromised config file does not expose forging keys.
Motivation
A node operator may run on a shared or remotely managed host. If the config file is leaked (backup, misconfigured permissions, supply-chain tooling), all delegate passphrases are immediately exposed. Encrypting them at rest significantly raises the bar for credential theft while keeping the operational flow simple: one password unlocks all delegates on startup.
Detailed description
Proposed behavior
- The operator encrypts each delegate passphrase with a single operator-chosen password before writing it to the config.
- On node startup, the node detects that a passphrase is encrypted and prompts the operator for the password (interactive) or reads it from a designated environment variable / startup flag (non-interactive / service mode).
- The password is kept in process memory only for the duration of the decryption step and is never written to disk, logs, or any storage.
- If the node is restarted, the password must be supplied again.
- When multiple delegate passphrases are present in the config (multi-delegate host), all of them are encrypted with the same single password, so the operator only enters it once per startup.
- Backward compatibility: until a separate deprecation notice is issued, the node continues to accept plain-text passphrases in the config without any migration requirement.
Encryption design sketch
- Use a well-established symmetric encryption scheme (e.g., AES-256-GCM) with a key derived from the operator password via a memory-hard KDF (e.g., scrypt or Argon2id).
- Store the encrypted value as a self-describing string in the config (e.g., a prefixed base64 blob that includes the algorithm identifier, KDF parameters, salt, IV, and ciphertext), so the node can distinguish an encrypted passphrase from a plain-text one reliably.
- The plain/encrypted detection must be deterministic and not rely on heuristics (length, character set, etc.).
Operator workflow
- Run an included CLI helper (or
npm run script) to encrypt a passphrase interactively and receive the encrypted string.
- Replace the plain-text passphrase in
config.json with the encrypted string.
- Start (or restart) the node; it detects the encrypted format and prompts for the password.
Security constraints
- Never log the password, the decrypted passphrase, or any intermediate key material.
- Protect against trivial brute-force by choosing adequate KDF parameters.
- Follow the existing security rules: no weakening of signature verification, no new unvalidated inputs.
Alternatives
- Vault / secrets manager integration: more powerful but adds a hard external dependency and complexity for small independent operators — at odds with the project's lightweight-node goal.
- OS-level keychain / credential store: platform-specific; complicates headless and Docker deployments.
- Environment variable only (no encryption): still leaves the secret exposed in process tables and shell history; plain env var does not solve the at-rest config leak.
Proposed technical implementation
- Add a CLI helper (e.g.,
tools/encrypt-passphrase.js) that takes a plain passphrase, prompts for the operator password, and outputs the encrypted config value.
- Update
helpers/config.js (and/or the startup sequence in app.js) to detect encrypted passphrases and perform decryption before the passphrase is handed to delegate / forging logic.
- Add a schema entry or convention for the encrypted format string in
schema/ and align with adamant-schema if the config shape is externally documented.
- Update
README.md and node documentation (Adamant-im/docs) with operator instructions for migration and startup.
- Add unit tests for the encrypt/decrypt helper and for the config-loading path (both plain and encrypted inputs).
Summary
Delegate passphrases are currently stored in plain text in the node config file. This issue tracks adding support for operator-password-encrypted passphrases so that a compromised config file does not expose forging keys.
Motivation
A node operator may run on a shared or remotely managed host. If the config file is leaked (backup, misconfigured permissions, supply-chain tooling), all delegate passphrases are immediately exposed. Encrypting them at rest significantly raises the bar for credential theft while keeping the operational flow simple: one password unlocks all delegates on startup.
Detailed description
Proposed behavior
Encryption design sketch
Operator workflow
npm runscript) to encrypt a passphrase interactively and receive the encrypted string.config.jsonwith the encrypted string.Security constraints
Alternatives
Proposed technical implementation
tools/encrypt-passphrase.js) that takes a plain passphrase, prompts for the operator password, and outputs the encrypted config value.helpers/config.js(and/or the startup sequence inapp.js) to detect encrypted passphrases and perform decryption before the passphrase is handed to delegate / forging logic.schema/and align withadamant-schemaif the config shape is externally documented.README.mdand node documentation (Adamant-im/docs) with operator instructions for migration and startup.