v0.0.11 - Improve startup UX: print version, visible mnemonic entry, retry up to 10 attempts, and support q/x quit

This commit is contained in:
Laan Tungir
2026-05-04 07:02:40 -04:00
parent 28f50a750f
commit c5fdb1a207

View File

@@ -451,8 +451,8 @@ int socket_name_random(char *out, size_t out_len);
/* Version information (auto-updated by build/version tooling) */
#define NSIGNER_VERSION_MAJOR 0
#define NSIGNER_VERSION_MINOR 0
#define NSIGNER_VERSION_PATCH 10
#define NSIGNER_VERSION "v0.0.10"
#define NSIGNER_VERSION_PATCH 11
#define NSIGNER_VERSION "v0.0.11"
/* NSIGNER_HEADERLESS_DECLS_END */
@@ -807,7 +807,7 @@ static void render_status(const role_table_t *role_table,
}
printf("\nHotkeys\n-------\n");
printf("q quit l lock/reunlock r refresh a toggle auto-approve(prompt): %s\n",
printf("q/x quit l lock/reunlock r refresh a toggle auto-approve(prompt): %s\n",
g_auto_approve ? "ON" : "OFF");
fflush(stdout);
}
@@ -836,94 +836,101 @@ static int prompt_load_mnemonic(mnemonic_state_t *mnemonic) {
char phrase[MNEMONIC_MAX_LEN];
char phrase_copy[MNEMONIC_MAX_LEN];
char mode[MNEMONIC_MAX_LEN];
struct termios old_term;
struct termios new_term;
int have_term = 0;
int invalid_attempts = 0;
const int max_invalid_attempts = 10;
if (mnemonic == NULL) {
return -1;
}
printf("Mnemonic source: [E]nter existing or [G]enerate new (default E; you can also paste mnemonic here): ");
fflush(stdout);
if (read_line_stdin(mode, sizeof(mode)) != 0) {
fprintf(stderr, "Failed to read mnemonic source choice\n");
return -1;
}
if (strchr(mode, ' ') != NULL && mode[0] != 'g' && mode[0] != 'G') {
if (mnemonic_load(mnemonic, mode) != 0) {
fprintf(stderr, "Invalid mnemonic (must be 12/15/18/21/24 words)\n");
return -1;
}
return 0;
}
if (mode[0] == 'g' || mode[0] == 'G') {
int idx = 1;
char *ctx = NULL;
char *word;
if (mnemonic_generate(12, phrase, sizeof(phrase)) != 0) {
fprintf(stderr, "Failed to generate mnemonic\n");
while (invalid_attempts < max_invalid_attempts) {
printf("Mnemonic source: [E]nter existing or [G]enerate new (default E; you can also paste mnemonic here): ");
fflush(stdout);
if (read_line_stdin(mode, sizeof(mode)) != 0) {
fprintf(stderr, "Failed to read mnemonic source choice\n");
return -1;
}
strncpy(phrase_copy, phrase, sizeof(phrase_copy) - 1);
phrase_copy[sizeof(phrase_copy) - 1] = '\0';
printf("\nGenerated mnemonic (WRITE THIS DOWN - it will not be shown again):\n");
word = strtok_r(phrase_copy, " ", &ctx);
while (word != NULL) {
printf("%2d. %s\n", idx, word);
idx++;
word = strtok_r(NULL, " ", &ctx);
if ((mode[0] == 'q' || mode[0] == 'Q' || mode[0] == 'x' || mode[0] == 'X') && mode[1] == '\0') {
fprintf(stderr, "User requested exit.\n");
return -1;
}
if (mnemonic_load(mnemonic, phrase) != 0) {
if (strchr(mode, ' ') != NULL && mode[0] != 'g' && mode[0] != 'G') {
if (mnemonic_load(mnemonic, mode) == 0) {
return 0;
}
invalid_attempts++;
fprintf(stderr,
"Invalid mnemonic (must be 12/15/18/21/24 words). Attempts: %d/%d\n",
invalid_attempts,
max_invalid_attempts);
continue;
}
if (mode[0] == 'g' || mode[0] == 'G') {
int idx = 1;
char *ctx = NULL;
char *word;
if (mnemonic_generate(12, phrase, sizeof(phrase)) != 0) {
fprintf(stderr, "Failed to generate mnemonic\n");
return -1;
}
strncpy(phrase_copy, phrase, sizeof(phrase_copy) - 1);
phrase_copy[sizeof(phrase_copy) - 1] = '\0';
printf("\nGenerated mnemonic (WRITE THIS DOWN - it will not be shown again):\n");
word = strtok_r(phrase_copy, " ", &ctx);
while (word != NULL) {
printf("%2d. %s\n", idx, word);
idx++;
word = strtok_r(NULL, " ", &ctx);
}
if (mnemonic_load(mnemonic, phrase) != 0) {
memset(phrase, 0, sizeof(phrase));
memset(phrase_copy, 0, sizeof(phrase_copy));
fprintf(stderr, "Failed to load generated mnemonic\n");
return -1;
}
memset(phrase, 0, sizeof(phrase));
memset(phrase_copy, 0, sizeof(phrase_copy));
fprintf(stderr, "Failed to load generated mnemonic\n");
return 0;
}
printf("Enter mnemonic (12/15/18/21/24 words): ");
fflush(stdout);
if (read_line_stdin(phrase, sizeof(phrase)) != 0) {
fprintf(stderr, "Failed to read mnemonic\n");
return -1;
}
memset(phrase, 0, sizeof(phrase));
memset(phrase_copy, 0, sizeof(phrase_copy));
return 0;
}
printf("Enter mnemonic (12/15/18/21/24 words): ");
fflush(stdout);
if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &old_term) == 0) {
new_term = old_term;
new_term.c_lflag &= (tcflag_t)~ECHO;
if (tcsetattr(STDIN_FILENO, TCSANOW, &new_term) == 0) {
have_term = 1;
if ((phrase[0] == 'q' || phrase[0] == 'Q' || phrase[0] == 'x' || phrase[0] == 'X') && phrase[1] == '\0') {
memset(phrase, 0, sizeof(phrase));
fprintf(stderr, "User requested exit.\n");
return -1;
}
}
if (read_line_stdin(phrase, sizeof(phrase)) != 0) {
if (have_term) {
(void)tcsetattr(STDIN_FILENO, TCSANOW, &old_term);
if (mnemonic_load(mnemonic, phrase) == 0) {
memset(phrase, 0, sizeof(phrase));
return 0;
}
fprintf(stderr, "Failed to read mnemonic\n");
return -1;
}
if (have_term) {
(void)tcsetattr(STDIN_FILENO, TCSANOW, &old_term);
printf("\n");
}
if (mnemonic_load(mnemonic, phrase) != 0) {
memset(phrase, 0, sizeof(phrase));
fprintf(stderr, "Invalid mnemonic (must be 12/15/18/21/24 words)\n");
return -1;
invalid_attempts++;
fprintf(stderr,
"Invalid mnemonic (must be 12/15/18/21/24 words). Attempts: %d/%d\n",
invalid_attempts,
max_invalid_attempts);
}
memset(phrase, 0, sizeof(phrase));
return 0;
fprintf(stderr, "Too many invalid mnemonic attempts (%d). Exiting.\n", max_invalid_attempts);
return -1;
}
static void apply_test_overrides(policy_table_t *policy) {
@@ -1055,6 +1062,9 @@ int main(int argc, char *argv[]) {
return 1;
}
printf("nsigner %s\n", NSIGNER_VERSION);
fflush(stdout);
mnemonic_init(&mnemonic);
if (prompt_load_mnemonic(&mnemonic) != 0) {
mnemonic_unload(&mnemonic);
@@ -1207,7 +1217,7 @@ int main(int argc, char *argv[]) {
char ch = '\0';
if (read(STDIN_FILENO, &ch, 1) > 0) {
ch = (char)tolower((unsigned char)ch);
if (ch == 'q') {
if (ch == 'q' || ch == 'x') {
g_running = 0;
} else if (ch == 'r') {
render_status(&role_table, &mnemonic, derived_count, socket_name);