Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVFi3wvLWKaEkpDw6eLnQC
59 lines
2.7 KiB
YAML
59 lines
2.7 KiB
YAML
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
|