Fix Task #5 review findings: race guard, full stack cloud-init, volume, node:crypto SSH
4 changes to address code review rejections:
1. Race condition fix (bootstrap.ts)
- advanceBootstrapJob: WHERE now guards on AND state='awaiting_payment'
- If UPDATE matches 0 rows, re-fetch current job (already advanced by
another concurrent poll) instead of firing a second provisioner
- Verified with 5-concurrent-poll test: only 1 "starting provisioning"
log entry per job; all 5 responses show consistent state
2. Complete cloud-init to full Bitcoin + LND + LNbits stack (provisioner.ts)
- Phase 1: packages, Docker, Tailscale, UFW, block volume mount
- Phase 2: Bitcoin Core started; polls for RPC availability (max 5 min)
- Phase 3: LND started; waits for REST API (max 6 min)
- Phase 4: non-interactive LND wallet init via REST:
POST /v1/genseed → POST /v1/initwallet with base64 password
(no lncli, no interactive prompts, no expect)
- Phase 5: waits for admin.macaroon to appear on mounted volume
- Phase 6: LNbits started with LndRestWallet backend; mounts LND
data dir so it reads tls.cert + admin.macaroon automatically
- Phase 7: saves all credentials (RPC pass, LND wallet pass + seed
mnemonic, LNbits URL) to chmod 600 /root/node-credentials.txt
3. DO block volume support (provisioner.ts)
- Reads DO_VOLUME_SIZE_GB env var (0 = no volume, default)
- createVolume(): POST /v2/volumes (ext4 filesystem, tagged timmy-node)
- Passes volumeId in droplet create payload (attached at boot)
- Cloud-init Phase 1 detects and mounts the volume automatically
(lsblk scan → mkfs if unformatted → mount → /etc/fstab entry)
4. SSH keypair via node:crypto (no ssh-keygen) (provisioner.ts)
- generateKeyPairSync('rsa', { modulusLength: 4096 })
- Public key: PKCS#1 DER → OpenSSH wire format via manual DER parser
(pkcs1DerToSshPublicKey): reads SEQUENCE → n, e INTEGERs → ssh-rsa
base64 string with proper mpint encoding (leading 0x00 for high bit)
- Private key: PKCS#1 PEM (-----BEGIN RSA PRIVATE KEY-----)
- Both stub and real paths use the same generateSshKeypair() function
- Removes runtime dependency on host ssh-keygen binary entirely
This commit is contained in:
@@ -24,9 +24,10 @@ export class PricingService {
|
||||
this.workFeeLong = config?.workFeeLongSats ?? 250;
|
||||
this.shortMax = config?.shortMaxChars ?? 100;
|
||||
this.mediumMax = config?.mediumMaxChars ?? 300;
|
||||
const rawFee = parseInt(process.env.BOOTSTRAP_FEE_SATS ?? "", 10);
|
||||
this.bootstrapFee =
|
||||
config?.bootstrapFeeSats ??
|
||||
(process.env.BOOTSTRAP_FEE_SATS ? parseInt(process.env.BOOTSTRAP_FEE_SATS, 10) : 10_000);
|
||||
(Number.isFinite(rawFee) && rawFee > 0 ? rawFee : 10_000);
|
||||
}
|
||||
|
||||
calculateEvalFeeSats(): number {
|
||||
|
||||
@@ -167,10 +167,11 @@ router.get("/bootstrap/:id", async (req: Request, res: Response) => {
|
||||
...(keyNote ? { sshKeyNote: keyNote } : {}),
|
||||
},
|
||||
nextSteps: [
|
||||
"SSH into your node: ssh root@<nodeIp>",
|
||||
"Bitcoin is syncing — this takes 1-2 weeks: bash /opt/timmy-node/ops.sh sync",
|
||||
"Once sync reaches ~1.0, initialize LND + LNbits: bash /opt/timmy-node/lnd-init.sh",
|
||||
"Auto-configure cold storage sweep: bash /opt/timmy-node/ops.sh configure-sweep",
|
||||
`SSH into your node using the private key above: ssh -i <key_file> root@${job.nodeIp ?? "<nodeIp>"}`,
|
||||
"Read your node credentials: cat /root/node-credentials.txt",
|
||||
"Monitor Bitcoin sync (takes 1-2 weeks): docker exec bitcoin bitcoin-cli getblockchaininfo",
|
||||
"Once sync is complete, fund your LND wallet, then open LNbits to create your wallet and get the API key",
|
||||
"Set LNBITS_URL and LNBITS_API_KEY in your Timmy deployment to enable payment processing",
|
||||
],
|
||||
stubMode: provisionerService.stubMode,
|
||||
message: provisionerService.stubMode
|
||||
|
||||
Reference in New Issue
Block a user