diff --git a/README.md b/README.md index eba7ce1..276aa99 100644 --- a/README.md +++ b/README.md @@ -500,7 +500,7 @@ The wizard presents a **preset menu** of 10 options covering the common role typ ``` Wizard preset menu: 1. Standard Nostr (NIP-06): secp256k1, m/44'/1237'/0'/0/0 - 2. Standard Nostr hardened range: secp256k1, m/44'/1237'/*'/0'/0' + 2. Standard Nostr range: secp256k1, m/44'/1237'/*'/0/0 3. Nostr agent range (hardened): secp256k1, m/44'/1237'/*'/1'/0' 4. SSH role: ed25519, m/44'/102001'/0'/0'/0' 5. Age/x25519 role: x25519, m/44'/102002'/0'/0'/0' diff --git a/build_static.sh b/build_static.sh index ba8d89f..91b0082 100755 --- a/build_static.sh +++ b/build_static.sh @@ -126,6 +126,10 @@ if [ "$ARCH" != "$HOST_ARCH" ]; then fi echo "[1/3] Building builder stage from project root context" +# Remove previous builder image to avoid dangling images piling up +# across repeated builds (each rebuild untagges the old image, leaving ~422MB +# of garbage per build otherwise). +docker rmi "$IMAGE_TAG" >/dev/null 2>&1 || true docker buildx build \ --platform "$PLATFORM" \ --target builder \ @@ -168,3 +172,7 @@ echo "" echo "Build complete:" echo " $BUILD_DIR/$OUTPUT_NAME" echo " $BUILD_DIR/$CLIENT_NAME" + +# Prune stale build cache older than 24h to prevent unbounded cache growth +# from repeated buildx builds. Recent layers are kept for fast rebuilds. +docker builder prune -af --filter "until=24h" >/dev/null 2>&1 || true diff --git a/documents/nsigner_menus.md b/documents/nsigner_menus.md index 2401021..e59d010 100644 --- a/documents/nsigner_menus.md +++ b/documents/nsigner_menus.md @@ -53,7 +53,7 @@ Default is E; you can also paste full mnemonic here. ``` Define a role: 1. Standard Nostr (NIP-06): secp256k1, m/44'/1237'/0'/0/0 - 2. Standard Nostr hardened range: secp256k1, m/44'/1237'/*'/0'/0' + 2. Standard Nostr range: secp256k1, m/44'/1237'/*'/0/0 3. Nostr agent range (hardened): secp256k1, m/44'/1237'/*'/1'/0' 4. SSH role: ed25519, m/44'/102001'/0'/0'/0' 5. Age/x25519 role: x25519, m/44'/102002'/0'/0'/0' @@ -70,7 +70,7 @@ Define a role: | Choice | Default name | Default path | Curve | Purpose | |--------|-------------|-------------|-------|---------| | 1 | `main` | `m/44'/1237'/0'/0/0` | secp256k1 | nostr | -| 2 | `nostr_hardened` | `m/44'/1237'/*'/0'/0'` | secp256k1 | nostr | +| 2 | `nostr_range` | `m/44'/1237'/*'/0/0` | secp256k1 | nostr | | 3 | `nostr_agent` | `m/44'/1237'/*'/1'/0'` | secp256k1 | nostr | | 4 | `ssh` | `m/44'/102001'/0'/0'/0'` | ed25519 | ssh | | 5 | `age` | `m/44'/102002'/0'/0'/0'` | x25519 | age | diff --git a/src/main.c b/src/main.c index b793e00..ac5be73 100644 --- a/src/main.c +++ b/src/main.c @@ -813,8 +813,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 1 -#define NSIGNER_VERSION_PATCH 16 -#define NSIGNER_VERSION "v0.1.16" +#define NSIGNER_VERSION_PATCH 17 +#define NSIGNER_VERSION "v0.1.17" /* NSIGNER_HEADERLESS_DECLS_END */ @@ -989,11 +989,17 @@ static int read_line_stdin(char *buf, size_t buf_sz) { * * Only works when stdin is a TTY. Falls back to read_line_stdin if not a TTY * (in which case prefill is ignored). + * + * `prompt` is the label text printed before the editable field (e.g. + * " Role name [main]: "). It is reprinted on every redraw so that the + * prompt does not disappear when the user starts editing. */ -static int read_line_editable(char *buf, size_t buf_sz, const char *prefill) { +static int read_line_editable(char *buf, size_t buf_sz, const char *prefill, + const char *prompt) { struct termios old_term, new_term; size_t len = 0; /* current text length */ size_t pos = 0; /* cursor position (0..len) */ + size_t prompt_len = 0;/* length of prompt string (for cursor math) */ int fd = STDIN_FILENO; int was_raw = 0; @@ -1001,6 +1007,10 @@ static int read_line_editable(char *buf, size_t buf_sz, const char *prefill) { return -1; } + if (prompt != NULL) { + prompt_len = strlen(prompt); + } + /* If not a TTY, fall back to plain fgets */ if (!isatty(fd)) { return read_line_stdin(buf, buf_sz); @@ -1028,11 +1038,14 @@ static int read_line_editable(char *buf, size_t buf_sz, const char *prefill) { } } - /* Draw the initial pre-filled text */ + /* Draw the initial prompt + pre-filled text */ + if (prompt != NULL) { + fputs(prompt, stdout); + } if (len > 0) { fputs(buf, stdout); - fflush(stdout); } + fflush(stdout); for (;;) { char ch; @@ -1060,6 +1073,7 @@ static int read_line_editable(char *buf, size_t buf_sz, const char *prefill) { buf[len] = '\0'; /* Redraw: move to start of field, clear line, redraw, reposition */ fputs("\r\033[K", stdout); /* CR + clear to end of line */ + if (prompt != NULL) fputs(prompt, stdout); fputs(buf, stdout); if (pos < len) { /* Move cursor left to pos */ @@ -1115,6 +1129,7 @@ static int read_line_editable(char *buf, size_t buf_sz, const char *prefill) { len--; buf[len] = '\0'; fputs("\r\033[K", stdout); + if (prompt != NULL) fputs(prompt, stdout); fputs(buf, stdout); if (pos < len) { printf("\033[%zuD", len - pos); @@ -1141,6 +1156,7 @@ static int read_line_editable(char *buf, size_t buf_sz, const char *prefill) { /* Ctrl-U — clear entire line */ if (pos > 0) { fputs("\r\033[K", stdout); + if (prompt != NULL) fputs(prompt, stdout); len = 0; pos = 0; buf[0] = '\0'; @@ -1159,6 +1175,7 @@ static int read_line_editable(char *buf, size_t buf_sz, const char *prefill) { buf[len] = '\0'; /* Redraw from cursor position */ fputs("\r\033[K", stdout); + if (prompt != NULL) fputs(prompt, stdout); fputs(buf, stdout); pos++; if (pos < len) { @@ -2014,7 +2031,7 @@ static int prompt_named_path_roles(role_table_t *role_table) { tui_render_content_screen(NULL, "Define a role — bind a role name to a derivation path template"); printf("Define a role:\n"); printf(" 1. Standard Nostr (NIP-06): secp256k1, m/44'/1237'/0'/0/0\n"); - printf(" 2. Standard Nostr hardened range: secp256k1, m/44'/1237'/*'/0'/0'\n"); + printf(" 2. Standard Nostr range: secp256k1, m/44'/1237'/*'/0/0\n"); printf(" 3. Nostr agent range (hardened): secp256k1, m/44'/1237'/*'/1'/0'\n"); printf(" 4. SSH role: ed25519, m/44'/102001'/0'/0'/0'\n"); printf(" 5. Age/x25519 role: x25519, m/44'/102002'/0'/0'/0'\n"); @@ -2050,8 +2067,8 @@ static int prompt_named_path_roles(role_table_t *role_table) { switch (choice) { case 2: - default_name = "nostr_hardened"; - default_path = "m/44'/1237'/*'/0'/0'"; + default_name = "nostr_range"; + default_path = "m/44'/1237'/*'/0/0"; break; case 3: default_name = "nostr_agent"; @@ -2107,10 +2124,12 @@ static int prompt_named_path_roles(role_table_t *role_table) { /* Role name */ char role_name[ROLE_NAME_MAX]; - printf(" Role name [%s]: ", default_name); - fflush(stdout); - if (read_line_editable(role_name, sizeof(role_name), default_name) != 0) { - return (roles_created > 0) ? 0 : -1; + { + char prompt_buf[128]; + snprintf(prompt_buf, sizeof(prompt_buf), " Role name [%s]: ", default_name); + if (read_line_editable(role_name, sizeof(role_name), default_name, prompt_buf) != 0) { + return (roles_created > 0) ? 0 : -1; + } } { size_t len = strlen(role_name); @@ -2288,10 +2307,12 @@ static int prompt_named_path_roles(role_table_t *role_table) { /* Path template — only prompt for custom path; presets use the default */ char path_token[ROLE_PATH_MAX]; if (choice == 10) { - printf(" Path template [%s]:\n ", default_path); - fflush(stdout); - if (read_line_editable(path_token, sizeof(path_token), default_path) != 0) { - return (roles_created > 0) ? 0 : -1; + { + char prompt_buf[256]; + snprintf(prompt_buf, sizeof(prompt_buf), " Path template [%s]: ", default_path); + if (read_line_editable(path_token, sizeof(path_token), default_path, prompt_buf) != 0) { + return (roles_created > 0) ? 0 : -1; + } } { size_t len = strlen(path_token);