Back to Body Journal
SOURCE / PINNED RELEASE

Made of little things.

Body Journal

Release
e8049f8da4e2…
Author-recorded commit
61c04bf0e8aa…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6c4554844c7

Archive hash verified: 4369d778da046a8b…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/report-identity.ts
import { schnorr } from '@noble/curves/secp256k1.js';
import { bech32 } from '@scure/base';
export const toHex = (bytes: Uint8Array) => Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
export const canonical = (value: unknown): string => JSON.stringify(value, (_key, v) => v && typeof v === 'object' && !Array.isArray(v) ? Object.fromEntries(Object.keys(v).sort().map(k => [k, v[k]])) : v);
export async function digest(value: unknown) { return toHex(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonical(value))))); }
export function parsePrivateKey(input: string): Uint8Array {
  try {
    const text = input.trim().replace(/^nostr:/i, '');
    let key: Uint8Array;
    if (/^[a-f0-9]{64}$/i.test(text)) key = Uint8Array.from(text.match(/../g)!, b => parseInt(b,16));
    else { const data = bech32.decode(text as `${string}1${string}`, 128); if (data.prefix !== 'nsec') throw new Error(); key = new Uint8Array(bech32.fromWords(data.words)); }
    schnorr.getPublicKey(key); return key;
  } catch { throw new Error('Enter a valid private nsec or 64-character hex key. A public key cannot restore reports.'); }
}
export const createPrivateKey = () => schnorr.utils.randomSecretKey();
export const privateText = (key: Uint8Array) => bech32.encode('nsec', bech32.toWords(key));
export const publicKey = (key: Uint8Array) => toHex(schnorr.getPublicKey(key));
export const fingerprint = (key: Uint8Array) => { const p = publicKey(key); return p.slice(0,8) + '…' + p.slice(-8); };
export type Challenge = { service: string; pubkey: string; nonce: string; action: string; digest: string; expiresAt: number };
export async function signChallenge(key: Uint8Array, challenge: Challenge, service: string, action: string, args: unknown) {
  if (challenge.service !== service || challenge.pubkey !== publicKey(key) || challenge.action !== action || challenge.digest !== await digest(args) || !/^[a-f0-9]{64}$/.test(challenge.nonce) || !Number.isSafeInteger(challenge.expiresAt) || challenge.expiresAt <= Date.now() || challenge.expiresAt > Date.now() + 180000) throw new Error('The service returned an invalid identity challenge. No proof was sent.');
  const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(['ai-doc-identity/v1', challenge.service, challenge.pubkey, challenge.nonce, challenge.action, challenge.digest, challenge.expiresAt])));
  return toHex(schnorr.sign(new Uint8Array(bytes), key));
}