feat: add Timmy Nostr keygen script + operator setup docs
Some checks failed
CI / Typecheck & Lint (pull_request) Failing after 0s

- Add scripts/generate-timmy-nsec.sh wrapper and TypeScript keygen script
  that outputs nsec/npub and a ready-to-paste export line
- Add OPERATOR.md with persistent identity setup instructions
- Document TIMMY_NOSTR_NSEC in replit.md secrets table
- Startup log already uses INFO (persisted) vs WARN (ephemeral)

Fixes #48

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alexander Whitestone
2026-03-22 21:50:11 -04:00
parent 6af27d4cdc
commit 247e796af3
5 changed files with 96 additions and 0 deletions

45
OPERATOR.md Normal file
View File

@@ -0,0 +1,45 @@
# Operator Setup Guide
## Timmy's Nostr Identity
Timmy uses a secp256k1 Nostr keypair for his on-chain identity. Without a
persisted key, Timmy generates a new ephemeral identity on every restart —
losing continuity with anyone who knew his previous `npub`.
### One-time setup
1. **Generate a keypair:**
```bash
bash scripts/generate-timmy-nsec.sh
```
This prints the `nsec1...` (private key), `npub1...` (public key), and an
`export` line you can copy-paste.
2. **Set the environment variable** in your deployment environment:
- **Replit:** Add `TIMMY_NOSTR_NSEC` in the Secrets tab (padlock icon).
- **VPS (systemd):** Add to `/opt/timmy-tower/.env`:
```
TIMMY_NOSTR_NSEC=nsec1...
```
- **Local dev:** Export in your shell or add to a `.env` file:
```bash
export TIMMY_NOSTR_NSEC="nsec1..."
```
3. **Restart the API server.** On startup you should see an `INFO` log:
```
timmy-identity INFO timmy identity loaded from env { npub: "npub1..." }
```
If you see a `WARN` instead, the env var is missing or malformed.
### Security
- The `nsec` is a private key — treat it like a password.
- Never commit it to version control.
- Never log it or expose it in API responses.
- If compromised, generate a new keypair and update all references to the old `npub`.

View File

@@ -67,6 +67,7 @@ Every package extends `tsconfig.base.json` which sets `composite: true`. The roo
|---|---|---|
| `LNBITS_URL` | Base URL of your LNbits instance | `https://legend.lnbits.com` |
| `LNBITS_API_KEY` | Invoice/Admin API key from your LNbits wallet | `a3f...` |
| `TIMMY_NOSTR_NSEC` | Timmy's persistent Nostr private key (run `bash scripts/generate-timmy-nsec.sh` to generate) | `nsec1...` |
> **Note:** If `LNBITS_URL` and `LNBITS_API_KEY` are absent, `LNbitsService` automatically runs in **stub mode** — invoices are simulated in-memory and can be marked paid via `svc.stubMarkPaid(hash)`. This is intentional for development without a Lightning node.

18
scripts/generate-timmy-nsec.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# =============================================================================
# Generate a fresh Nostr keypair for Timmy's persistent identity.
#
# Usage:
# bash scripts/generate-timmy-nsec.sh
#
# Requires: pnpm, Node.js, nostr-tools (installed via pnpm install)
#
# Output: nsec1... private key, npub1... public key, and an export line
# to copy-paste into your deployment environment.
# =============================================================================
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
exec pnpm --filter @workspace/scripts run generate-timmy-nsec

View File

@@ -7,8 +7,12 @@
"hello": "tsx ./src/hello.ts",
"timmy-watch": "tsx ./src/timmy-watch.ts",
"timmy-report": "tsx ./src/timmy-report.ts",
"generate-timmy-nsec": "tsx ./src/generate-timmy-nsec.ts",
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"nostr-tools": "^2.23.3"
},
"devDependencies": {
"@types/node": "catalog:",
"tsx": "catalog:"

View File

@@ -0,0 +1,28 @@
/**
* generate-timmy-nsec.ts — Generate a fresh Nostr keypair for Timmy.
*
* Outputs the nsec (private) and npub (public) keys, plus a ready-to-paste
* export line for setting the TIMMY_NOSTR_NSEC environment variable.
*
* Usage:
* pnpm --filter @workspace/scripts run generate-timmy-nsec
* # or via the wrapper:
* bash scripts/generate-timmy-nsec.sh
*/
import { generateSecretKey, getPublicKey, nip19 } from "nostr-tools";
const sk = generateSecretKey();
const pubkeyHex = getPublicKey(sk);
const nsec = nip19.nsecEncode(sk);
const npub = nip19.npubEncode(pubkeyHex);
console.log("=== Timmy Nostr Identity ===");
console.log("");
console.log(` npub: ${npub}`);
console.log(` nsec: ${nsec}`);
console.log("");
console.log("Add this to your deployment environment:");
console.log("");
console.log(` export TIMMY_NOSTR_NSEC="${nsec}"`);
console.log("");
console.log("⚠ Keep the nsec secret — anyone with it can impersonate Timmy.");