From c685806aeb52daf704d3e2da27063b8aef922ace Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Thu, 20 Aug 2026 13:55:40 -0400 Subject: [PATCH] v0.0.15 - Updated install_signer.sh to pull from signer repo (was n_signer); installs signer + signer-client binaries --- Cargo.lock | 2 +- Cargo.toml | 2 +- install_signer.sh | 287 ++++++++++++++++++++++++++++++++++++++++++++++ src/client/cli.rs | 1 + src/lib.rs | 2 +- 5 files changed, 291 insertions(+), 3 deletions(-) create mode 100644 install_signer.sh diff --git a/Cargo.lock b/Cargo.lock index e07530b..40ffc88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2207,7 +2207,7 @@ dependencies = [ [[package]] name = "signer" -version = "0.0.13" +version = "0.0.14" dependencies = [ "base64", "chacha20poly1305", diff --git a/Cargo.toml b/Cargo.toml index 329b616..d3a6b7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "signer" -version = "0.0.14" +version = "0.0.15" edition = "2021" license = "MIT" description = "Attended Nostr signing daemon — Rust port of n_signer" diff --git a/install_signer.sh b/install_signer.sh new file mode 100644 index 0000000..b35052d --- /dev/null +++ b/install_signer.sh @@ -0,0 +1,287 @@ +#!/usr/bin/env bash +set -euo pipefail + +# User-only installer for Qubes AppVM persistence model. +# Nothing is written to /usr, /etc, or other root-owned paths. +# +# Installs into $HOME: +# - signer -> ~/.local/bin/signer +# - signer-client -> ~/.local/bin/signer-client +# - startup helper -> ~/start_signer.sh +# +# Usage: +# bash install_signer.sh +# bash install_signer.sh --help +# +# Optional env vars: +# SIGNER_VERSION=vX.Y.Z # optional override; default is latest release tag +# SIGNER_GITEA_TOKEN= # if signer release assets are private +# SIGNER_BINARY_URL= +# SIGNER_CLIENT_BINARY_URL= + +SIGNER_VERSION="${SIGNER_VERSION:-}" + +PREFIX_BIN="${HOME}/.local/bin" + +log() { printf "\033[1;34m[INFO]\033[0m %s\n" "$*"; } +warn() { printf "\033[1;33m[WARN]\033[0m %s\n" "$*"; } +err() { printf "\033[1;31m[ERR ]\033[0m %s\n" "$*"; } + +show_help() { + cat < # required if signer release assets are private + SIGNER_BINARY_URL= + SIGNER_CLIENT_BINARY_URL= + +Install paths: + ~/.local/bin/signer + ~/.local/bin/signer-client + ~/start_signer.sh +EOF +} + +require_cmd() { + command -v "$1" >/dev/null 2>&1 || { + err "Missing command: $1" + exit 1 + } +} + +install_runtime_deps() { + if command -v apt-get >/dev/null 2>&1; then + log "Installing runtime dependencies via apt" + sudo apt-get update + sudo apt-get install -y ca-certificates curl jq + elif command -v dnf >/dev/null 2>&1; then + log "Installing runtime dependencies via dnf" + sudo dnf install -y ca-certificates curl jq + else + err "Unsupported distro: need apt-get or dnf to install runtime dependencies" + exit 1 + fi +} + +prepare_dirs() { + mkdir -p "${PREFIX_BIN}" +} + +resolve_signer_version() { + local headers=() + local latest_tag="" + + if [[ -n "${SIGNER_VERSION}" ]]; then + return 0 + fi + + if [[ -n "${SIGNER_GITEA_TOKEN:-}" ]]; then + headers=(-H "Authorization: token ${SIGNER_GITEA_TOKEN}") + fi + + latest_tag="$(curl -fsSL "${headers[@]}" "https://git.laantungir.net/api/v1/repos/laantungir/signer/releases" \ + | jq -r '.[0].tag_name // empty' || true)" + + if [[ -z "${latest_tag}" ]]; then + err "Could not resolve latest signer release tag from API." + err "Set SIGNER_VERSION explicitly (e.g. SIGNER_VERSION=v0.0.14)." + exit 1 + fi + + SIGNER_VERSION="${latest_tag}" +} + +# Resolve a release asset URL by asset name suffix. +# $1 = asset name to match exactly (e.g. "signer" or "signer-client") +download_signer_asset_url() { + local asset_name="$1" + local headers=() + local api_tag_url="https://git.laantungir.net/api/v1/repos/laantungir/signer/releases/tags/${SIGNER_VERSION}" + + if [[ -n "${SIGNER_GITEA_TOKEN:-}" ]]; then + headers=(-H "Authorization: token ${SIGNER_GITEA_TOKEN}") + fi + + curl -fsSL "${headers[@]}" "${api_tag_url}" \ + | jq -r '.assets[]?.browser_download_url // empty' \ + | grep -E "/${asset_name}$" \ + | head -n1 || true +} + +verify_installed_version() { + local expected="$1" + local bin_path="$2" + local got_line="" + local got_ver="" + + if [[ ! -x "${bin_path}" ]]; then + err "Installed binary missing: ${bin_path}" + exit 1 + fi + + got_line="$("${bin_path}" --version 2>/dev/null || true)" + got_ver="$(printf '%s\n' "${got_line}" | awk '{print $2}')" + + if [[ -z "${got_ver}" ]]; then + err "Could not determine installed signer version from: ${got_line}" + exit 1 + fi + + if [[ "${got_ver}" != "${expected}" ]]; then + err "Downloaded binary version mismatch: expected ${expected}, got ${got_ver}" + err "Release asset appears stale or mislabeled." + err "Use SIGNER_BINARY_URL to pin a known-good binary, or wait for a rebuilt release artifact." + exit 1 + fi +} + +# Download a single binary asset. +# $1 = asset name (for URL resolution fallback) +# $2 = override URL env var name (e.g. SIGNER_BINARY_URL) +# $3 = output path +download_binary() { + local asset_name="$1" + local override_env="$2" + local out_path="$3" + local asset_url="" + + # Resolve override from env var + eval "asset_url=\"\${${override_env}:-}\"" + + if [[ -z "${asset_url}" ]]; then + asset_url="$(download_signer_asset_url "${asset_name}")" + fi + + if [[ -z "${asset_url}" ]]; then + err "Could not find downloadable ${asset_name} release binary for ${SIGNER_VERSION}." + err "Provide ${override_env} or SIGNER_GITEA_TOKEN so the release asset can be resolved." + exit 1 + fi + + log "Using ${asset_name} binary URL: ${asset_url}" + if [[ -n "${SIGNER_GITEA_TOKEN:-}" ]]; then + curl -fL -H "Authorization: token ${SIGNER_GITEA_TOKEN}" -o "${out_path}" "${asset_url}" + else + curl -fL -o "${out_path}" "${asset_url}" + fi + chmod 0755 "${out_path}" +} + +install_signer() { + local release_page="" + + resolve_signer_version + release_page="https://git.laantungir.net/laantungir/signer/releases/tag/${SIGNER_VERSION}" + + log "Installing signer ${SIGNER_VERSION}" + log "Release page: ${release_page}" + + # Install the signer daemon binary + download_binary "signer" "SIGNER_BINARY_URL" "${PREFIX_BIN}/signer" + verify_installed_version "${SIGNER_VERSION}" "${PREFIX_BIN}/signer" + log "Installed ${PREFIX_BIN}/signer from release binary" + + # Install the signer-client CLI binary (best-effort: older releases may not have it) + if [[ -z "${SIGNER_CLIENT_BINARY_URL:-}" ]] && ! download_signer_asset_url "signer-client" >/dev/null 2>&1; then + warn "No signer-client asset found for ${SIGNER_VERSION}; skipping client install." + else + download_binary "signer-client" "SIGNER_CLIENT_BINARY_URL" "${PREFIX_BIN}/signer-client" + log "Installed ${PREFIX_BIN}/signer-client from release binary" + fi +} + +write_signer_start_script() { + local script_path="${HOME}/start_signer.sh" + + cat >"${script_path}" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +export PATH="$HOME/.local/bin:$PATH" +LISTEN_TARGET="${SIGNER_LISTEN_TARGET:-tcp:[::]:8080}" + +echo "=== signer startup ===" +echo "listen target: ${LISTEN_TARGET}" + +# Optional: print current FIPS identity info if fipsctl is available. +if command -v fipsctl >/dev/null 2>&1; then + if fipsctl show status >/dev/null 2>&1; then + STATUS_JSON="$(fipsctl show status)" + elif sudo -n fipsctl show status >/dev/null 2>&1; then + STATUS_JSON="$(sudo -n fipsctl show status)" + else + STATUS_JSON="" + fi + + if [[ -n "${STATUS_JSON}" ]]; then + FIPS_IPV6="$(printf '%s\n' "${STATUS_JSON}" | sed -n 's/.*"ipv6_addr": "\([^"]*\)".*/\1/p')" + FIPS_NPUB="$(printf '%s\n' "${STATUS_JSON}" | sed -n 's/.*"npub": "\([^"]*\)".*/\1/p')" + LISTEN_PORT="$(printf '%s\n' "${LISTEN_TARGET}" | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')" + [[ -n "${FIPS_IPV6}" ]] && echo "fips ipv6: ${FIPS_IPV6}" + [[ -n "${FIPS_NPUB}" ]] && echo "fips npub: ${FIPS_NPUB}" + if [[ -n "${FIPS_NPUB}" && -n "${LISTEN_PORT}" ]]; then + echo "fips address: http://${FIPS_NPUB}.fips:${LISTEN_PORT}" + fi + else + echo "fips status: unavailable (run as user in fips group or with sudo)" + fi +fi + +echo +echo "Starting signer..." +echo "On first remote request, approve in prompt with [y] or [a]." +exec "$HOME/.local/bin/signer" --listen "${LISTEN_TARGET}" +EOF + + chmod 0755 "${script_path}" + log "Wrote ${script_path}" +} + +post_checks() { + export PATH="${PREFIX_BIN}:${PATH}" + + log "Running post-install checks" + require_cmd signer + signer --version || true + + if [[ -x "${PREFIX_BIN}/signer-client" ]]; then + signer-client --version || true + fi + + log "User binaries installed in: ${PREFIX_BIN}" + log "If needed, add to shell PATH: export PATH=\"${PREFIX_BIN}:\$PATH\"" +} + +main() { + if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + show_help + exit 0 + fi + + if [[ $# -gt 0 ]]; then + err "Unknown option: $1" + show_help + exit 1 + fi + + install_runtime_deps + prepare_dirs + install_signer + write_signer_start_script + post_checks + + log "Completed user-only install of signer" + log "Start signer with: ~/start_signer.sh" +} + +main "$@" diff --git a/src/client/cli.rs b/src/client/cli.rs index d9f32a3..536fdd8 100644 --- a/src/client/cli.rs +++ b/src/client/cli.rs @@ -39,6 +39,7 @@ use clap::{Parser, Subcommand}; signer-client --role main --path \"m/44'/1237'/0'/0/0\" nip44-decrypt # Derive an HMAC digest (secp256k1) + signer-client --algorithm secp256k1 --index 0 derive testdata # Raw JSON-RPC passthrough diff --git a/src/lib.rs b/src/lib.rs index 21c6389..f3ad03b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,4 +31,4 @@ pub mod error; pub use error::SignerError; /// Version string (matches C NSIGNER_VERSION). -pub const VERSION: &str = "v0.0.14"; +pub const VERSION: &str = "v0.0.15";