🚀 Socket Launch Week Day 4:Socket MCP Adds Org Alerts, Threat Feed Review, and Package Inspection.Learn more
Sign In

@powforge/identity

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@powforge/identity - npm Package Compare versions

Comparing version
0.7.0
to
0.7.1
+24
-0
CHANGELOG.md
# Changelog
## 0.7.1
### Added
- `checkFreshness(scoreResponse, currentTipHeight)` — pure-function helper
that validates a signed DoI score's chaintip-bound freshness window
without round-tripping to the oracle. Returns `{fresh, reason,
signed_tip_height, current_tip_height, blocks_elapsed, freshness_blocks,
fresh_until_height}`. Does NOT verify the schnorr signature; pair with
the existing schnorr verify path on the caller side.
- The oracle now signs `freshness_blocks` alongside `bitcoin_tip` inside
the schnorr-covered envelope. Default 6 blocks (~60 min). Verifiers can
reject a stale score even when the signature still verifies. The oracle
also exposes a public `/verify-freshness` endpoint that does the same
check server-side for callers without the SDK.
### Compat
- Additive only. SDK callers that never reach for `checkFreshness` see
byte-identical behavior. Oracle responses keep their previous shape;
`freshness_blocks` is a new field, not a renamed one. Pre-existing
schnorr verifiers that canonicalize the full payload before checking
the signature pick the field up automatically.
## 0.7.0

@@ -4,0 +28,0 @@

+2
-2
{
"name": "@powforge/identity",
"version": "0.7.0",
"version": "0.7.1",
"description": "Depth-of-Identity SDK for Nostr. Measures accumulated irreversible work across four dimensions of irreversible work (social, access, vouch, economic). Try it live at powforge.dev/explorer.",

@@ -18,3 +18,3 @@ "keywords": [

"scripts": {
"test": "node --test tests/smoke.test.js tests/publish.test.js tests/temporal.test.js tests/zap-bolt11-dedup.test.js tests/zap-receipt-verify.test.js tests/vouch-cycle.test.js tests/indirect-vouch-cycle.test.js tests/chaintip.test.js tests/spatial-deprecation.test.js"
"test": "node --test tests/smoke.test.js tests/publish.test.js tests/temporal.test.js tests/zap-bolt11-dedup.test.js tests/zap-receipt-verify.test.js tests/vouch-cycle.test.js tests/indirect-vouch-cycle.test.js tests/chaintip.test.js tests/spatial-deprecation.test.js tests/freshness.test.js"
},

@@ -21,0 +21,0 @@ "license": "MIT",

@@ -792,2 +792,90 @@ /**

module.exports = { getIdentityDepth, queryRelay, scoreSpatial, scoreSocial, scoreAccess, scoreVouch, scoreEconomic, verifyEventFull, withinTemporalBounds, verifyZapReceipt, detectDirectCycle, detectIndirectCycle, getChaintip, signWithChaintip, resetChaintipCache };
/**
* checkFreshness — pure-function freshness validator for a signed score.
*
* Validates the chaintip-binding window on a signed DoI score WITHOUT
* round-tripping to the oracle. Assumes the schnorr signature has already
* been verified (or will be — this function does NOT verify the signature,
* only the freshness window).
*
* Inputs:
* scoreResponse the signed envelope returned by the oracle. Must carry
* `bitcoin_tip.height` (number) and `freshness_blocks`
* (number). Older oracle builds that pre-date the
* freshness cert (no `freshness_blocks` field) cannot
* be checked — this function returns fresh:false with
* reason 'no_freshness_window' so callers don't silently
* accept stale stale scores from older oracles.
* currentTipHeight the caller's own view of the current bitcoin chaintip
* height. Caller fetches this however they like
* (mempool.space, local bitcoind, getChaintip()).
*
* Returns:
* {
* fresh: bool,
* reason: string|null,
* signed_tip_height: number|null,
* current_tip_height: number,
* blocks_elapsed: number|null,
* freshness_blocks: number|null,
* fresh_until_height: number|null,
* }
*
* reason values:
* 'no_bitcoin_tip' — score has no bitcoin_tip binding (legacy)
* 'no_freshness_window' — score has tip but no freshness_blocks field
* 'future_tip' — signed_tip_height > current_tip_height
* (signer claims a height the caller cannot see;
* could be reorg, lying signer, or stale caller)
* 'stale' — gap exceeds the freshness window
* null — fresh
*
* Edge cases:
* blocks_elapsed === freshness_blocks → fresh (boundary inclusive).
* blocks_elapsed === 0 → fresh.
* signed_tip > current → fresh:false, reason:'future_tip'.
*/
function checkFreshness(scoreResponse, currentTipHeight) {
if (typeof currentTipHeight !== 'number' || !Number.isFinite(currentTipHeight) || currentTipHeight < 0) {
throw new Error('checkFreshness: currentTipHeight must be a non-negative finite number');
}
const result = {
fresh: false,
reason: null,
signed_tip_height: null,
current_tip_height: currentTipHeight,
blocks_elapsed: null,
freshness_blocks: null,
fresh_until_height: null,
};
if (!scoreResponse || typeof scoreResponse !== 'object') {
result.reason = 'no_bitcoin_tip';
return result;
}
const tip = scoreResponse.bitcoin_tip;
if (!tip || typeof tip.height !== 'number') {
result.reason = 'no_bitcoin_tip';
return result;
}
result.signed_tip_height = tip.height;
const fb = scoreResponse.freshness_blocks;
if (typeof fb !== 'number' || !Number.isFinite(fb) || fb < 1) {
result.reason = 'no_freshness_window';
return result;
}
result.freshness_blocks = fb;
result.fresh_until_height = tip.height + fb;
result.blocks_elapsed = currentTipHeight - tip.height;
if (result.blocks_elapsed < 0) {
result.reason = 'future_tip';
return result;
}
if (result.blocks_elapsed > fb) {
result.reason = 'stale';
return result;
}
result.fresh = true;
return result;
}
module.exports = { getIdentityDepth, queryRelay, scoreSpatial, scoreSocial, scoreAccess, scoreVouch, scoreEconomic, verifyEventFull, withinTemporalBounds, verifyZapReceipt, detectDirectCycle, detectIndirectCycle, getChaintip, signWithChaintip, resetChaintipCache, checkFreshness };