feat: add --password to soul export for AES-256-GCM encryption at rest#294
feat: add --password to soul export for AES-256-GCM encryption at rest#294sshekhar563 wants to merge 4 commits into
--password to soul export for AES-256-GCM encryption at rest#294Conversation
- Changed --password to be a prompt-only flag (is_flag=True) so passwords aren't passed inline and leaked to shell history - Tests now use interactive prompts for export - Added warning when password is used with non-soul format - Added missing cryptography dependency check
prakashUXtech
left a comment
There was a problem hiding this comment.
The export side here is well built. Making --password a flag with a hidden, confirmed prompt means the password never lands in shell history or ps, you route through the existing AES-256-GCM path instead of reimplementing crypto, and you handle the non-soul format and the cancelled-confirmation cases cleanly. The care shows.
What blocks merge is that this is encryption with no way back in from the CLI. After soul export --password, no soul command can open the resulting file, and the read commands (unpack, inspect, init --from-file) hit the encrypted container and raise a raw traceback instead of a clear message. So a user who encrypts their only copy through the CLI is locked out of it through that same CLI. The library already supports the read side (Soul.awaken(password=...)), so this is mostly a matter of surfacing it.
Before merge:
- Add a prompt-based
--passwordto at leastsoul unpackandsoul inspect, wired toSoul.awaken(source, password=...), mirroring the secure prompt you already wrote on the export side. If the team would rather split that into a follow-up, let's track it explicitly, and then item 2 still needs to land now. - Catch
SoulEncryptedError/SoulDecryptionErrorin the read commands and print a friendly "this soul is encrypted, pass --password" message instead of a traceback. - A CLI round-trip test would make this solid:
export --password, then read it back with the password and assert a memory survived, plus a wrong-password case giving a clean error. The current round-trip test asserts the name only and goes through the library rather than the CLI. - The issue also asked for a short
docs/encryption.md. Adding that (or a cli-reference section) would round it out.
To be clear, the part that's here is good work, and the secure prompt in particular shows real care. The ask is just to finish the loop: a soul you can lock, you should be able to unlock from the same CLI.
…, docs (qbtrix#294 review) Addresses all 4 review items from prakashUXtech: 1. Added --password flag to soul inspect and soul unpack so encrypted souls can be decrypted from the CLI (mirrors the export --password flow). 2. Added _awaken_or_fail() helper that catches SoulEncryptedError and SoulDecryptionError, printing friendly messages instead of tracebacks. All 47 Soul.awaken() calls in the CLI now route through this helper. 3. Added TestCLIRoundTrip with 4 integration tests: - export --password -> inspect --password (correct password) - export --password -> unpack --password (correct password) - inspect encrypted without --password (friendly hint) - inspect --password with wrong password (friendly error) 4. Added docs/encryption.md covering AES-256-GCM spec, CLI usage, Python API, exception handling, and archive format details. Updated docs/cli-reference.md with --password option for inspect.
prakashUXtech
left a comment
There was a problem hiding this comment.
The write-only problem is solved. soul inspect --password and soul unpack --password now decrypt through Soul.awaken(password=...), a wrong or missing password gives a clean message instead of a traceback, and the new docs/encryption.md is a good addition. The password hygiene is solid on both sides (hidden prompts, no inline value that could land in shell history). This is a real step up from last round.
One thing before merge, and it maps directly to the issue's acceptance criterion. The round-trip tests currently assert the soul's name survives, but the name lives in the plaintext manifest, so a bug that dropped the encrypted memory tiers during export or awaken would still pass. Could you add an assertion that a memory survives the encrypted round-trip? You already store one ("Secret memory" / "The secret code is 42"), so asserting restored.memory_count > 0 (or recalling it) is a one-liner, and inspect even prints a memory count the CLI test could check.
Optional, non-blocking: a line in encryption.md noting the manifest (name, DID, role) stays readable by design, so someone encrypting for privacy knows the memories are protected but the identity header is not. And the main.py top-of-file comment could use an (#294) Updated line.
Really close now.
…vacy note, header 1. Assert encrypted memory tiers survive round-trip (not just the plaintext manifest name) 2. Add privacy note to encryption.md: manifest (name/DID/role) stays readable by design 3. Add Updated: 2026-07-20 (qbtrix#294) header to cli/main.py
prakashUXtech
left a comment
There was a problem hiding this comment.
The memory-survival assertion landed, and it's stronger than I asked for. test_awaken_with_correct_password stores "Secret memory", does the encrypted round-trip, and asserts the exact content comes back (any("Secret memory" in f.content ...)), so a soul that silently dropped its encrypted tiers would fail even if it had default facts. Both optional asks are in too: the encryption.md note that the manifest (name, DID, role) stays readable by design, and the (#294) header line. The decrypt path is unregressed and actually improved, since friendly errors now cover all the read commands.
Approving. One small non-blocking follow-up for later: the mutating commands (remember, note, forget) now hint "pass --password" on an encrypted soul, but they don't expose a --password flag yet, so the hint points at an option that isn't there. In-place editing of an encrypted archive is outside this PR's scope, so it's a future item, not a change here. Nice work.
|
Heads-up: #294 now conflicts with dev because #299 landed first and both touch |
Description
This PR implements Phase 2 of the encryption feature by wiring the
--passwordflag through thesoul exportCLI command. The underlying AES-256-GCM encryption and scrypt key derivation logic was already implemented in theruntime/export/crypto.pylayer; this PR surfaces it securely to the user.Key Changes
--passwordoption is implemented as a prompt-only flag (is_flag=True). This prevents users from passing passwords inline (e.g.--password mysecret), ensuring passwords do not leak into shell history or process listings.cryptographypackage is missing.manifest.jsonfile remains unencrypted so readers can inspect metadata and see the"encrypted": trueflag without needing the password.docs/cli-reference.mdwith the new option and an example of its interactive usage.tests/test_cli/test_export_password.pywith 9 tests covering archive structure, interactive prompt simulation, mismatched confirmation handling, format warnings, and async encryption/decryption round-trips.Verification
ruff checkandruff format --checkpass.tests/test_cli/test_export_password.py.