@percher/shared 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pow.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Proof-of-work for the headless anonymous-deploy gate (FUTURE23 Spår A
3
+ * hardening). A solution is any string such that
4
+ * `sha256(`${nonce}:${solution}`)` has at least `difficulty` leading zero
5
+ * bits. Lives in @percher/shared so the API verifies with the EXACT algorithm
6
+ * the CLI/MCP client solves — a drift between the two would silently reject
7
+ * every legitimate publish or accept unsolved ones.
8
+ *
9
+ * A browser CAPTCHA can't gate this endpoint: the caller is a headless agent
10
+ * or CLI. PoW is the headless equivalent — the client spends CPU instead of a
11
+ * human solving a puzzle, so a botnet pays a real cost per app regardless of
12
+ * how many IPs it controls.
13
+ */
14
+ export declare const POW_ALGORITHM = "sha256-leading-zero-bits";
15
+ /** Count leading zero bits in a hex digest. */
16
+ export declare function leadingZeroBits(hexDigest: string): number;
17
+ /** True if `solution` meets `difficulty` for `nonce`. difficulty<=0 is a no-op. */
18
+ export declare function verifyPow(nonce: string, solution: string, difficulty: number): boolean;
19
+ /**
20
+ * Brute-force a solution. Returns the first hit, or null if `maxIterations` is
21
+ * exhausted (guards against a pathological difficulty hanging the client).
22
+ */
23
+ export declare function solvePow(nonce: string, difficulty: number, maxIterations?: number): string | null;
24
+ //# sourceMappingURL=pow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pow.d.ts","sourceRoot":"","sources":["../src/pow.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,6BAA6B,CAAC;AAMxD,+CAA+C;AAC/C,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAazD;AAED,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAItF;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,aAAa,SAAa,GACzB,MAAM,GAAG,IAAI,CAOf"}
package/dist/pow.js ADDED
@@ -0,0 +1,56 @@
1
+ import { createHash } from "node:crypto";
2
+ /**
3
+ * Proof-of-work for the headless anonymous-deploy gate (FUTURE23 Spår A
4
+ * hardening). A solution is any string such that
5
+ * `sha256(`${nonce}:${solution}`)` has at least `difficulty` leading zero
6
+ * bits. Lives in @percher/shared so the API verifies with the EXACT algorithm
7
+ * the CLI/MCP client solves — a drift between the two would silently reject
8
+ * every legitimate publish or accept unsolved ones.
9
+ *
10
+ * A browser CAPTCHA can't gate this endpoint: the caller is a headless agent
11
+ * or CLI. PoW is the headless equivalent — the client spends CPU instead of a
12
+ * human solving a puzzle, so a botnet pays a real cost per app regardless of
13
+ * how many IPs it controls.
14
+ */
15
+ export const POW_ALGORITHM = "sha256-leading-zero-bits";
16
+ function digest(nonce, solution) {
17
+ return createHash("sha256").update(`${nonce}:${solution}`).digest("hex");
18
+ }
19
+ /** Count leading zero bits in a hex digest. */
20
+ export function leadingZeroBits(hexDigest) {
21
+ let bits = 0;
22
+ for (const ch of hexDigest) {
23
+ const nibble = Number.parseInt(ch, 16);
24
+ if (nibble === 0) {
25
+ bits += 4;
26
+ continue;
27
+ }
28
+ // clz32(n) - 28 = leading zeros of a 4-bit value (1..15).
29
+ bits += Math.clz32(nibble) - 28;
30
+ break;
31
+ }
32
+ return bits;
33
+ }
34
+ /** True if `solution` meets `difficulty` for `nonce`. difficulty<=0 is a no-op. */
35
+ export function verifyPow(nonce, solution, difficulty) {
36
+ if (difficulty <= 0)
37
+ return true;
38
+ if (!solution)
39
+ return false;
40
+ return leadingZeroBits(digest(nonce, solution)) >= difficulty;
41
+ }
42
+ /**
43
+ * Brute-force a solution. Returns the first hit, or null if `maxIterations` is
44
+ * exhausted (guards against a pathological difficulty hanging the client).
45
+ */
46
+ export function solvePow(nonce, difficulty, maxIterations = 50_000_000) {
47
+ if (difficulty <= 0)
48
+ return "0";
49
+ for (let i = 0; i < maxIterations; i++) {
50
+ const solution = i.toString(36);
51
+ if (leadingZeroBits(digest(nonce, solution)) >= difficulty)
52
+ return solution;
53
+ }
54
+ return null;
55
+ }
56
+ //# sourceMappingURL=pow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pow.js","sourceRoot":"","sources":["../src/pow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAExD,SAAS,MAAM,CAAC,KAAa,EAAE,QAAgB;IAC7C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;YACjB,IAAI,IAAI,CAAC,CAAC;YACV,SAAS;QACX,CAAC;QACD,0DAA0D;QAC1D,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM;IACR,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,QAAgB,EAAE,UAAkB;IAC3E,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,OAAO,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,IAAI,UAAU,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,KAAa,EACb,UAAkB,EAClB,aAAa,GAAG,UAAU;IAE1B,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,IAAI,UAAU;YAAE,OAAO,QAAQ,CAAC;IAC9E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percher/shared",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Shared constants and contracts (timeouts, etc.) used across the Percher CLI, API, MCP, and worker. Internal — values change in lockstep across packages.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,6 +25,11 @@
25
25
  "types": "./dist/platform-env.d.ts",
26
26
  "import": "./dist/platform-env.js",
27
27
  "default": "./dist/platform-env.js"
28
+ },
29
+ "./pow": {
30
+ "types": "./dist/pow.d.ts",
31
+ "import": "./dist/pow.js",
32
+ "default": "./dist/pow.js"
28
33
  }
29
34
  },
30
35
  "files": [