@powforge/attest-client
v0.2.0
Published
Client for attest.powforge.dev — PoW-gated Schnorr DLC oracle. Dead man's switch + bug bounty auto-release. dlcspecs-compatible OracleAnnouncement / OracleAttestation TLV bytes for dlcdevkit and rust-dlc integration.
Maintainers
Readme
@powforge/attest-client
Node.js client for attest.powforge.dev — a PoW-gated Schnorr DLC oracle for Bitcoin bug-bounty auto-release.
Wraps the six public REST endpoints, vendors a SHA-256 proof-of-work solver, and exposes pollStatus for the PENDING → RELEASED transition. Zero runtime dependencies. Node 18+.
Install
npm install @powforge/attest-clientQuick start
const { AttestClient } = require('@powforge/attest-client');
const client = new AttestClient();
const bounty = await client.createBounty({
title: 'Fix issue #42',
condition: { type: 'github_pr_merged', repo: 'me/repo', ref: 42 },
amount_sats: 1000,
});
console.log('Bounty registered:', bounty.bounty_id);
const released = await client.pollStatus(bounty.bounty_id, { intervalMs: 30_000 });
console.log('Released:', released.attestation);createBounty auto-mines the PoW (difficulty pulled from /api/v1/info, currently 18 bits). pollStatus returns when the GitHub condition is met and the oracle signs a RELEASED attestation. The attestation is a BIP-340 Schnorr signature over sha256("RELEASED" || bounty_id), verifiable against oracle_pubkey from /api/v1/info.
API
new AttestClient(options?)
| Option | Type | Default |
|---|---|---|
| baseUrl | string | "https://attest.powforge.dev" |
| fetchImpl | function | globalThis.fetch |
| minerImpl | function | inline SHA-256 loop |
| userAgent | string | "@powforge/attest-client/0.1.0" |
Methods
| Method | Returns |
|---|---|
| getInfo() | service manifest with oracle_pubkey, pow |
| getChallenge() | { challenge, difficulty, expires_unix } |
| mine(challenge, difficulty, opts?) | nonce string |
| createBounty(opts) | { bounty_id, state, announcement, ... } |
| registerBounty(opts) | alias for createBounty |
| getBounty(id) | bounty record |
| getBountyStatus(id) | { state, ... } — triggers GitHub check + release if condition met |
| getAnnouncement(id) | DLC adaptor data: oracle_pubkey, nonce_pubkey, outcome_hash |
| pollStatus(id, opts?) | resolves when state is RELEASED |
createBounty(opts)
await client.createBounty({
title: 'optional title',
condition: { type: 'github_pr_merged', repo: 'owner/repo', ref: 42 },
amount_sats: 1000, // optional metadata
pow_challenge: 'hex', // optional pre-solved override
pow_nonce: 'string', // optional pre-solved override
onProgress: ({ iters, elapsedMs }) => {}, // mining progress
signal: abortController.signal, // cancel mining
});Condition types: 'github_pr_merged', 'github_issue_closed'.
pollStatus(id, opts)
await client.pollStatus(bountyId, {
intervalMs: 30_000, // default 30s
maxAttempts: 120, // default 120 (~1h at 30s)
signal: abortController.signal,
onTick: (state) => console.log('tick:', state),
});Throws if maxAttempts exhausted or signal aborts.
DLC integration
The /announcement endpoint returns the oracle's pre-committed nonce point (R) and outcome hash. Counterparties can compute the DLC adaptor point T = R + H(R || P || outcome)·P at registration time and sign CET adaptors before the GitHub event occurs. Settlement is trustless: the oracle's Schnorr s scalar (revealed in the attestation) is the discrete log of T.
See the rust-dlc reference example for a full end-to-end counterparty flow.
Errors
All methods throw a descriptive Error on non-2xx responses. The error carries:
err.status— HTTP status codeerr.body— parsed JSON response body
try {
await client.createBounty({ /* missing pow */ });
} catch (err) {
if (err.status === 402) console.error('PoW required:', err.body.reason);
}License
MIT
