Update provisioning URL and streamline SSH key delivery

Fixes the hardcoded 'https://' in the stub provisioner's lnbitsUrl to 'http://' and implements an atomic, first-retrieval SSH private key delivery mechanism.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 2f0c982b-02f6-4381-9fc4-34f489842999
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/sPDHkg8
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
alexpaynex
2026-03-18 19:10:30 +00:00
parent 2cab3ef907
commit 2245be0eaf
2 changed files with 23 additions and 9 deletions

View File

@@ -497,7 +497,7 @@ export class ProvisionerService {
dropletId: fakeDropletId,
nodeIp: "198.51.100.42",
tailscaleHostname: `timmy-node-${jobId.slice(0, 8)}.tail1234.ts.net`,
lnbitsUrl: `https://timmy-node-${jobId.slice(0, 8)}.tail1234.ts.net`,
lnbitsUrl: `http://timmy-node-${jobId.slice(0, 8)}.tail1234.ts.net:3000`,
sshPrivateKey: privateKey,
updatedAt: new Date(),
})
@@ -568,8 +568,10 @@ export class ProvisionerService {
? `timmy-node-${jobId.slice(0, 8)}.${this.tsTailnet}.ts.net`
: null;
// LNbits listens on port 3000 (HTTP). Tailscale encrypts the link at the
// network layer, so http:// is correct — no TLS termination on the service.
const lnbitsUrl = tailscaleHostname
? `https://${tailscaleHostname}`
? `http://${tailscaleHostname}:3000`
: nodeIp
? `http://${nodeIp}:3000`
: null;

View File

@@ -144,17 +144,29 @@ router.get("/bootstrap/:id", async (req: Request, res: Response) => {
break;
case "ready": {
const keyNote = job.sshKeyDelivered
? "SSH private key was delivered on first retrieval — check your records"
: null;
// Atomic one-time SSH key delivery: only the request that wins the
// guarded UPDATE (WHERE ssh_key_delivered = false) delivers the key.
// Concurrent first-reads both see delivered=false in the pre-fetched
// job, but only one UPDATE matches — the other gets 0 rows and falls
// back to the "already delivered" note.
let sshPrivateKey: string | null = null;
let keyNote: string | null = null;
// Deliver SSH key on first retrieval, then clear it from DB
const sshPrivateKey = job.sshKeyDelivered ? null : job.sshPrivateKey;
if (!job.sshKeyDelivered && job.sshPrivateKey) {
await db
const won = await db
.update(bootstrapJobs)
.set({ sshKeyDelivered: true, sshPrivateKey: null, updatedAt: new Date() })
.where(eq(bootstrapJobs.id, job.id));
.where(and(eq(bootstrapJobs.id, job.id), eq(bootstrapJobs.sshKeyDelivered, false)))
.returning({ id: bootstrapJobs.id });
if (won.length > 0) {
// This request won the delivery race — return the key we pre-read.
sshPrivateKey = job.sshPrivateKey;
} else {
keyNote = "SSH private key was delivered on a concurrent request — check your records";
}
} else {
keyNote = "SSH private key was delivered on first retrieval — check your records";
}
res.json({