🚀 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.1
to
0.7.2
+28
-1
CHANGELOG.md
# Changelog
## 0.7.2
### Fixed
- `scoreAccess` (access dimension) was linear in `totalPow`, which let a
PoW farm dominate the multi-dim score. Median Sybil PoW farm scored
~5,386 vs genuine median ~346 in H-DOI-1 v2 discrimination tests —
a 16x grindable advantage that collapsed multi-dim AUC from a target
~0.95 down to 0.800. Fixed by switching to log2 scaling (each PoW bit
is already exponentially costly per NIP-13, so log2 of the bit-sum is
the honest measure of order-of-magnitude effort).
- Added `PER_DIMENSION_SCORE_CAP = 100` defense-in-depth on
`scoreAccess` so even a pathological farm cannot exceed the natural
peak of other log-bounded dimensions.
- Post-fix multi-dim AUC = 1.000 with FPR = 0% at TPR = 90% on the
reference 50-genuine + 50-Sybil population. PoW farm median dropped
from 5,386 to ~94, a 57x reduction.
### Compat
- Score values for genuine users with PoW activity will decrease (linear
→ log2 means a high-PoW user previously scoring ~1,000 in the access
dim now scores ~50-90). Multi-dim totals are correspondingly lower
but rank order across genuine users is preserved. Anyone who had
pinned an absolute weight threshold should re-calibrate against the
new distribution; AUC-based or relative thresholds are unaffected.
## 0.7.1

@@ -110,3 +137,3 @@

### Motivation
- Fubz directive msg 1510 (2026-04-23): "wire @powforge/identity chaintip
- Principal directive msg 1510 (2026-04-23): "wire @powforge/identity chaintip
+ forge-tick bitcoin_pulse to use the internal Bitcoin node at

@@ -113,0 +140,0 @@ lightning.lan:8332." Local RPC means lower latency, no mempool.space

+2
-2
{
"name": "@powforge/identity",
"version": "0.7.1",
"version": "0.7.2",
"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 tests/freshness.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 tests/access-pow-farm-cap.test.js"
},

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

@@ -47,3 +47,2 @@ # @powforge/identity

> are grind-cheap and Sybil-trivial; the dimension was marketing, not measurement.
> See [`research/spatial-dim-protocol-apr21.md`](https://gitlab.com/powforge/sats-challenge/-/blob/master/research/spatial-dim-protocol-apr21.md).
> `scoreSpatial` remains exported for backwards compatibility. Pass

@@ -50,0 +49,0 @@ > `dimensions: ['spatial', 'social', 'access', 'vouch', 'economic']`

@@ -225,6 +225,37 @@ /**

/**
* Per-dimension hard cap. No single dimension may contribute more than this
* to a pubkey's identity score. Defense-in-depth against any future scoring
* formula whose growth term escapes its intended range. The cap is set to
* match the natural peak of the other log-scaled dimensions in genuine
* populations (social/vouch/economic peak ~80-130 in test data), so honest
* users are unaffected while a runaway grinder is bounded.
*
* H-DOI-1 v2 (tick 360) found access dim was emitting scores up to ~6000
* because totalPow was applied linearly. The fix below switches access to
* log2 internally; this cap is the second layer of insurance.
*/
const PER_DIMENSION_SCORE_CAP = 100;
/**
* Compute chain depth for access dimension (PoW events)
* Excludes events already counted by other dimensions (kind 3333, 1, 7, 33335)
* to prevent cross-contamination / double-counting of PoW.
* Uses log2 scaling on event count to prevent linear grinding.
*
* v0.7.2 — H-DOI-1 v2 fix. The previous formula `totalPow * 2 + log2(events+1)`
* was linear in totalPow, which let a PoW farm accumulate a score one to two
* orders of magnitude above any other dimension. Median Sybil PoW-farm score
* was ~5,386 vs genuine median 346 — a 16x advantage that collapsed multi-dim
* AUC from a target ~0.95 down to 0.800. See research/depth-of-identity-hypotheses.md.
*
* The fix: log2-scale totalPow so doubling work adds a constant, not a linear
* increase. Each PoW bit is *already* exponentially costly (NIP-13 — each
* leading zero bit doubles expected work), so log2 of the bit-sum collapses
* the grindable advantage while still rewarding genuine PoW. The dimension
* is then clamped via PER_DIMENSION_SCORE_CAP so even an extreme farm cannot
* dominate the multi-dim total.
*
* Calibration (validated by scripts/test-hdoi1-discrimination.js):
* genuine totalPow ~50-200 → log2*6 + count_bonus → ~35-55, well below cap
* farm totalPow ~2000+ → log2*6 + count_bonus → ~70-95, capped at 100
* Farm/genuine ratio drops from ~16x to ~2x, restoring discrimination AUC.
*/

@@ -258,2 +289,9 @@ function scoreAccess(events) {

// Log2 over both totalPow and event count. Each PoW bit already represents
// exponential work (NIP-13), so the log term measures order-of-magnitude
// effort rather than raw bit count. Coefficients calibrated against genuine
// multi-dim profiles to land in the same 30-90 range as social/vouch/economic.
const rawScore = Math.log2(totalPow + 1) * 6 + Math.log2(powEventCount + 1) * 2;
const cappedScore = Math.min(rawScore, PER_DIMENSION_SCORE_CAP);
return {

@@ -264,5 +302,3 @@ dimension: 'access',

maxDifficulty,
// PoW bits are already exponentially costly (each bit doubles work),
// so we keep totalPow linear but apply log2 to event count
score: Math.round(totalPow * 2 + Math.log2(powEventCount + 1)),
score: Math.round(cappedScore),
};

@@ -269,0 +305,0 @@ }