Files
wisp/scripts/gen-keystore.sh
NilsBriggen 9f42ee2460
CI / test (push) Has been cancelled
CI / deploy-web (push) Has been cancelled
CI / build-apk (push) Has been cancelled
Build Wisp: on-device transcription studio (web + native, one codebase)
Private, offline speech-to-text that runs Whisper on the user's own device —
free, no account, no per-minute fees. Replaces Otter.ai / Rev.

- Pure, tested engine: chunking, overlap timestamp-stitching, exports
  (SRT/VTT/TXT/MD/JSON), WAV codec, resampler, job queue, model catalog (142 tests).
- Platform-abstracted TranscriptionEngine: transformers.js on web (loaded from
  CDN at runtime to dodge Metro's onnxruntime-web bundling limits), whisper.rn
  on native. Shared pipeline orchestrates decode -> chunk -> transcribe -> stitch.
- Cross-platform StorageRepo (Dexie web / expo-sqlite native), Zod-validated.
- UI: library + search, import, live-progress transcription, synced click-to-seek
  editor, multi-format export; model picker + privacy in settings.
- Web ships as a single-page PWA with COOP/COEP isolation for threaded WASM;
  Docker (nginx) image + Traefik compose for wisp.briggen.dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 17:54:21 +02:00

121 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# scripts/gen-keystore.sh
# ---------------------------------------------------------------------------
# Generate a NEW dedicated release keystore for Wisp, then print the exact
# commands to base64-encode it and the list of Gitea Actions secrets to set.
#
# Run this ONCE on a trusted machine. The resulting keystore is the SINGLE
# source of truth for signing every future Wisp APK — if you lose it (or its
# passwords), you cannot ship an update that upgrades an already-installed APK
# (signature mismatch). BACK IT UP somewhere safe (password manager / offline).
#
# This script does NOT touch CI, does NOT commit anything, and does NOT generate
# a "real" production key for you silently — YOU run it, YOU keep the output.
#
# Requires: keytool (ships with any JDK).
#
# Usage:
# ./scripts/gen-keystore.sh # interactive password prompts
# STORE_PASS=... KEY_PASS=... ./scripts/gen-keystore.sh # non-interactive
# ---------------------------------------------------------------------------
set -euo pipefail
# --- Tunables (override via env) --------------------------------------------
# Output keystore path. Default lands beside the script, OUTSIDE the app tree so
# you don't accidentally commit it (.gitignore already excludes *.jks/*.keystore,
# but we keep it out of the way regardless).
KEYSTORE_OUT="${KEYSTORE_OUT:-./wisp-release.keystore}"
# Key alias. MUST match ANDROID_KEY_ALIAS in CI. scripts/ci-android-sign.sh and
# the docs assume the alias 'wisp'.
KEY_ALIAS="${KEY_ALIAS:-wisp}"
# RSA 2048, valid for 10000 days (~27 years) — Google Play's minimum validity
# recommendation for upload/app-signing keys is well within this.
KEY_ALG="RSA"
KEY_SIZE="2048"
VALIDITY_DAYS="10000"
# Distinguished Name. Edit to taste; only CN is really meaningful for an app key.
DNAME="${DNAME:-CN=Wisp, OU=Wisp, O=briggen.dev, L=, ST=, C=DE}"
log() { printf '[gen-keystore] %s\n' "$*"; }
fail() { printf '[gen-keystore] ERROR: %s\n' "$*" >&2; exit 1; }
# --- Preconditions ----------------------------------------------------------
command -v keytool >/dev/null 2>&1 || fail "keytool not found. Install a JDK (e.g. JDK 17)."
if [[ -e "${KEYSTORE_OUT}" ]]; then
fail "Refusing to overwrite existing keystore at ${KEYSTORE_OUT}. Move/delete it first."
fi
# --- Collect passwords ------------------------------------------------------
# Allow non-interactive use via env (STORE_PASS / KEY_PASS); otherwise prompt.
# We do NOT echo passwords. For most setups STORE_PASS and KEY_PASS can be equal.
STORE_PASS="${STORE_PASS:-}"
KEY_PASS="${KEY_PASS:-}"
if [[ -z "${STORE_PASS}" ]]; then
read -r -s -p "Enter NEW keystore (store) password: " STORE_PASS; echo
read -r -s -p "Confirm keystore password: " STORE_PASS_CONFIRM; echo
[[ "${STORE_PASS}" == "${STORE_PASS_CONFIRM}" ]] || fail "Store passwords do not match."
fi
[[ "${#STORE_PASS}" -ge 6 ]] || fail "Keystore password must be at least 6 characters (keytool requirement)."
if [[ -z "${KEY_PASS}" ]]; then
read -r -s -p "Enter NEW key (alias) password [blank = same as store]: " KEY_PASS; echo
KEY_PASS="${KEY_PASS:-$STORE_PASS}"
fi
# --- Generate the keystore --------------------------------------------------
# -storetype PKCS12 is the modern default (JKS is deprecated). keytool will emit
# a PKCS12 keystore; that's fully supported by Gradle/apksigner.
log "Generating ${KEY_ALG} ${KEY_SIZE} key, alias '${KEY_ALIAS}', valid ${VALIDITY_DAYS} days..."
keytool -genkeypair \
-keystore "${KEYSTORE_OUT}" \
-storetype PKCS12 \
-alias "${KEY_ALIAS}" \
-keyalg "${KEY_ALG}" \
-keysize "${KEY_SIZE}" \
-validity "${VALIDITY_DAYS}" \
-dname "${DNAME}" \
-storepass "${STORE_PASS}" \
-keypass "${KEY_PASS}"
log "Keystore created: ${KEYSTORE_OUT}"
echo
# --- Print follow-up instructions -------------------------------------------
# Show the EXACT base64 command and the secrets to configure in Gitea. We print
# the base64 command (not the base64 itself, and never the passwords) so the
# human runs it themselves and pastes into the Gitea secrets UI.
cat <<EOF
=============================================================================
NEXT STEPS — configure Gitea Actions secrets for the build-apk job
=============================================================================
1) Base64-encode the keystore (single line, no wrapping) and copy it:
# Linux:
base64 -w0 "${KEYSTORE_OUT}"
# macOS (no -w flag; -b0 disables wrapping):
base64 -b0 "${KEYSTORE_OUT}"
Copy the entire single-line output — that is ANDROID_KEYSTORE_BASE64.
2) In Gitea -> the Wisp repo -> Settings -> Actions -> Secrets, add:
ANDROID_KEYSTORE_BASE64 = <the base64 string from step 1>
ANDROID_KEYSTORE_PASSWORD = <the store password you just chose>
ANDROID_KEY_ALIAS = ${KEY_ALIAS}
ANDROID_KEY_PASSWORD = <the key password you just chose>
(These names match scripts/ci-android-sign.sh and .gitea/workflows/ci.yml.)
3) BACK UP "${KEYSTORE_OUT}" and BOTH passwords in a safe place (password
manager / offline). If lost, you can never update an already-installed APK.
4) Do NOT commit the keystore. (.gitignore already excludes *.keystore/*.jks.)
=============================================================================
EOF