Skip to content

feat(init-wallet-enc): optionally create enc identity file#320

Open
distractedm1nd wants to merge 2 commits into
zcash:mainfrom
distractedm1nd:ryan-302
Open

feat(init-wallet-enc): optionally create enc identity file#320
distractedm1nd wants to merge 2 commits into
zcash:mainfrom
distractedm1nd:ryan-302

Conversation

@distractedm1nd

@distractedm1nd distractedm1nd commented Nov 5, 2025

Copy link
Copy Markdown
Contributor

Resolves #302
Resolves COR-333

Wanted to open as a suggestion for dealing with #302, and starting a discussion:

Would it not be better to integrate this behavior directly into the initialization command (with a flag), rather than adding a new command? 2 steps rather than 3 is already a big improvement from a product perspective, let alone removing the requirement of having the extra binary.

But this draft PR is just a placeholder to discuss it, just don't want to waste time in a direction that isn't useful. Obviously there would still be a lot to change+improve here.

@str4d str4d left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial feedback. Depending on how the UX ends up shaping out for configuring the different possible alternative wallet encryption mechanisms (including plugins #252), it may be better to have a dedicated CLI method for each, and remove init-wallet-encryption (instead doing what it does now inside each of the dedicated methods). But we can start by prototyping the combined UX inside init-wallet-encryption.

Comment on lines +29 to +35
let sk = age::x25519::Identity::generate();
let pk = sk.to_public();

// Q: should we also include timestamp?
writeln!(out, "# public key: {}", pk).map_err(|e| ErrorKind::Init.context(e))?;
writeln!(out, "{}", sk.to_string().expose_secret())
.map_err(|e| ErrorKind::Init.context(e))?;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, what I should do is expose the file-generator code from inside rage-keygen somewhere under age::cli_common for reuse. Then it would also benefit from translations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried to tackle this for a bit but gave up - moving the generator (where the localized strings are found) into cli_common means moving those strings over to age/i18n, and adding chrono as a dependency. Is this what you meant?

Exposing instead only the file creation might be an option? But then we don't really gain much at all from doing so.

I've updated the code now without making a PR to rage - maybe it'll help thinking about what to expose upstream.

.map_err(|e| ErrorKind::Init.context(e))?;

info!("Encryption identity file created at {}", path.display());
info!("Public key: {}", pk);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recipient is irrelevant to Zallet users.

Suggested change
info!("Public key: {}", pk);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread zallet/src/cli.rs Outdated
pub(crate) struct InitWalletEncryptionCmd {
/// Creates the encryption identity file if it does not already exist.
#[arg(short)]
pub(crate) create: bool,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this being a bool, we should take an enum with variants corresponding to both unencrypted (what is currently implemented) and passphrase-encrypted, so we cover both of the currently-documented use cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@distractedm1nd distractedm1nd marked this pull request as ready for review November 10, 2025 07:08
@true-jared true-jared requested review from nullcopy and str4d February 2, 2026 16:49
@nuttycom

nuttycom commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

@str4d can you please create an issue for the follow-up work that will be needed to support plugins?

@true-jared true-jared assigned schell and unassigned schell Mar 6, 2026
@str4d

str4d commented Mar 9, 2026

Copy link
Copy Markdown
Collaborator

I summarised what needs to happen here: #252 (comment)

For now, this PR can be reviewed and merged as-is, and we can then iterate on the UX in later alphas.

@schell schell left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only thing left is to add some unit tests to ensure this creates valid identity files and that there's no clobbering when given a path that already exists.

See my changes at distractedm1nd#1.

})?
.to_string();

generate_identity_file(&path, self.mode)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably be checking the path to ensure we're not clobbering an existing identity file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see that OutputWriter has a parameter allow_overwrite that you're setting to false to not overwrite an existing file. So we're trusting that library here, which might be fine, but I'd still prefer to have a manual check before calling generate_identity_file just to be sure.

.ok_or_else(|| {
ErrorKind::Init.context(format!(
"{} is not currently supported (not UTF-8)",
config.encryption_identity().display(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have translations strings to use here:

fl!("err-init-path-not-utf8", ...)

// TODO: Not a big fan of this, at all. Is there a better
// way to log it that will capture more attention?
warn!("Using an autogenerated passphrase: {}", p.expose_secret());
warn!("Please store this passphrase securely.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a better way to log it.

I'd like to make this case unrepresentable by requiring generation have a filepath to put the passphrase into once generated, making that action explicit. But we're dealing with an external library here, so I guess this is the best we can do for now.

We could write the passphrase to a file and warn! but they could miss that - and then they have a sensitive passphrase sitting on their filesystem...

})?
.to_string(),
)
age::IdentityFile::from_file(path.to_string())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path is already String here - we should use .clone() :)

}
Err(e) => {
return Err(ErrorKind::Init
.context(format!("Failed to read or generate passphrase: {}", e))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use translations through fl! here.

})?
.to_string();

generate_identity_file(&path, self.mode)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably want to generate the file AFTER initializing the keystore. If the mode is use-existing, and the keystore file doesn't exist, I believe this would fail with an IO error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a zallet CLI command to generate encryption-identity.txt

5 participants