From e9627ecc8d1da0d7d8c364accfbb89b9b571df8a Mon Sep 17 00:00:00 2001 From: teahubxyz Date: Sun, 25 Oct 2026 21:42:19 +0000 Subject: [PATCH] ops: assign well-known house handles, signed with the factory key (SNAIL_KNOWN) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VVFi3wvLWKaEkpDw6eLnQC --- .gitea/workflows/wk-house.yml | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .gitea/workflows/wk-house.yml diff --git a/.gitea/workflows/wk-house.yml b/.gitea/workflows/wk-house.yml new file mode 100644 index 0000000..d1af5a9 --- /dev/null +++ b/.gitea/workflows/wk-house.yml @@ -0,0 +1,58 @@ +name: wk-house +# Assign well-known.online "house" handles to a pubkey (default: the factory key) via POST /house, +# signed with the factory nsec (SNAIL_KNOWN). The key never appears in logs. +on: + workflow_dispatch: + inputs: + names: + description: comma-separated names + required: true + og: + description: list in og.well-known.online (free inbox) + required: true + default: 'true' + pubkey: + description: owner pubkey (hex or npub); empty = the factory key + required: false + default: '' +jobs: + house: + runs-on: ubuntu-latest + steps: + - name: assign + env: + NSEC: ${{ secrets.SNAIL_KNOWN }} + NAMES: ${{ inputs.names }} + OG: ${{ inputs.og }} + OWNER: ${{ inputs.pubkey }} + run: | + mkdir -p /tmp/h && cd /tmp/h + npm init -y >/dev/null && npm i --silent @noble/curves@1.9.7 >/dev/null + cat > h.mjs <<'EOF' + import { schnorr } from '@noble/curves/secp256k1'; + import { createHash } from 'node:crypto'; + const CH = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; + function nsecToHex(s) { + s = s.trim().toLowerCase(); + if (/^[0-9a-f]{64}$/.test(s)) return s; + const d = [...s.slice(s.lastIndexOf('1') + 1)].map((c) => CH.indexOf(c)).slice(0, -6); + let acc = 0, bits = 0; const out = []; + for (const v of d) { acc = (acc << 5) | v; bits += 5; while (bits >= 8) { bits -= 8; out.push((acc >> bits) & 255); } } + return Buffer.from(out).toString('hex'); + } + const sk = nsecToHex(process.env.NSEC); + const pk = Buffer.from(schnorr.getPublicKey(sk)).toString('hex'); + console.log('signer', pk); + let i = 0; + for (const name of process.env.NAMES.split(',').map((s) => s.trim()).filter(Boolean)) { + const ev = { pubkey: pk, created_at: Math.floor(Date.now() / 1000), kind: 27235, content: String(i++), + tags: [['u', 'https://well-known.online/house'], ['method', 'POST'], ['name', name]] }; + ev.id = createHash('sha256').update(JSON.stringify([0, ev.pubkey, ev.created_at, ev.kind, ev.tags, ev.content])).digest('hex'); + ev.sig = Buffer.from(schnorr.sign(ev.id, sk)).toString('hex'); + const body = { event: ev, og: process.env.OG === 'true', ...(process.env.OWNER ? { pubkey: process.env.OWNER } : {}) }; + const r = await fetch('https://well-known.online/house', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) }); + console.log(name, r.status, await r.text()); + if (r.status >= 300) process.exitCode = 1; + } + EOF + node h.mjs