@oleary-labs/signet-sdk 0.1.0

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.
Files changed (95) hide show
  1. package/dist/admin.d.ts +38 -0
  2. package/dist/admin.d.ts.map +1 -0
  3. package/dist/admin.js +112 -0
  4. package/dist/admin.js.map +1 -0
  5. package/dist/authkey-session.d.ts +64 -0
  6. package/dist/authkey-session.d.ts.map +1 -0
  7. package/dist/authkey-session.js +164 -0
  8. package/dist/authkey-session.js.map +1 -0
  9. package/dist/bootstrap.d.ts +30 -0
  10. package/dist/bootstrap.d.ts.map +1 -0
  11. package/dist/bootstrap.js +60 -0
  12. package/dist/bootstrap.js.map +1 -0
  13. package/dist/bundler.d.ts +85 -0
  14. package/dist/bundler.d.ts.map +1 -0
  15. package/dist/bundler.js +160 -0
  16. package/dist/bundler.js.map +1 -0
  17. package/dist/delegate.d.ts +57 -0
  18. package/dist/delegate.d.ts.map +1 -0
  19. package/dist/delegate.js +111 -0
  20. package/dist/delegate.js.map +1 -0
  21. package/dist/frostVerify.d.ts +23 -0
  22. package/dist/frostVerify.d.ts.map +1 -0
  23. package/dist/frostVerify.js +69 -0
  24. package/dist/frostVerify.js.map +1 -0
  25. package/dist/index.d.ts +32 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +38 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/jwks.d.ts +28 -0
  30. package/dist/jwks.d.ts.map +1 -0
  31. package/dist/jwks.js +81 -0
  32. package/dist/jwks.js.map +1 -0
  33. package/dist/jwt.d.ts +27 -0
  34. package/dist/jwt.d.ts.map +1 -0
  35. package/dist/jwt.js +50 -0
  36. package/dist/jwt.js.map +1 -0
  37. package/dist/keygen.d.ts +26 -0
  38. package/dist/keygen.d.ts.map +1 -0
  39. package/dist/keygen.js +60 -0
  40. package/dist/keygen.js.map +1 -0
  41. package/dist/oauth.d.ts +34 -0
  42. package/dist/oauth.d.ts.map +1 -0
  43. package/dist/oauth.js +119 -0
  44. package/dist/oauth.js.map +1 -0
  45. package/dist/request.d.ts +42 -0
  46. package/dist/request.d.ts.map +1 -0
  47. package/dist/request.js +115 -0
  48. package/dist/request.js.map +1 -0
  49. package/dist/scopedSign.d.ts +82 -0
  50. package/dist/scopedSign.d.ts.map +1 -0
  51. package/dist/scopedSign.js +130 -0
  52. package/dist/scopedSign.js.map +1 -0
  53. package/dist/server-prover.d.ts +29 -0
  54. package/dist/server-prover.d.ts.map +1 -0
  55. package/dist/server-prover.js +54 -0
  56. package/dist/server-prover.js.map +1 -0
  57. package/dist/session.d.ts +14 -0
  58. package/dist/session.d.ts.map +1 -0
  59. package/dist/session.js +29 -0
  60. package/dist/session.js.map +1 -0
  61. package/dist/types.d.ts +56 -0
  62. package/dist/types.d.ts.map +1 -0
  63. package/dist/types.js +5 -0
  64. package/dist/types.js.map +1 -0
  65. package/dist/userop.d.ts +104 -0
  66. package/dist/userop.d.ts.map +1 -0
  67. package/dist/userop.js +212 -0
  68. package/dist/userop.js.map +1 -0
  69. package/dist/x402.d.ts +127 -0
  70. package/dist/x402.d.ts.map +1 -0
  71. package/dist/x402.js +167 -0
  72. package/dist/x402.js.map +1 -0
  73. package/package.json +64 -0
  74. package/src/admin.ts +178 -0
  75. package/src/authkey-session.ts +241 -0
  76. package/src/bootstrap.ts +106 -0
  77. package/src/bundler.ts +256 -0
  78. package/src/delegate.ts +163 -0
  79. package/src/frostVerify.ts +79 -0
  80. package/src/generate-inputs.ts +158 -0
  81. package/src/index.ts +43 -0
  82. package/src/jwks.ts +92 -0
  83. package/src/jwt.ts +74 -0
  84. package/src/keygen.ts +89 -0
  85. package/src/oauth.ts +157 -0
  86. package/src/partial-sha.ts +99 -0
  87. package/src/proof.ts +99 -0
  88. package/src/request.ts +174 -0
  89. package/src/scopedSign.ts +184 -0
  90. package/src/server-prover.ts +76 -0
  91. package/src/session.ts +33 -0
  92. package/src/types.ts +63 -0
  93. package/src/userop.ts +368 -0
  94. package/src/witness.ts +132 -0
  95. package/src/x402.ts +275 -0
package/src/request.ts ADDED
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Session-authenticated request signing.
3
+ *
4
+ * After auth, every keygen/sign request must include a signature
5
+ * over the canonical request hash, produced with the session private key.
6
+ *
7
+ * Canonical hash: SHA256(groupID ":" keyID ":" nonce ":" timestamp_8BE [":" messageHash])
8
+ * Signature: 64-byte [R || S] secp256k1 ECDSA (no recovery byte)
9
+ */
10
+
11
+ import type { SessionKeypair, IdTokenClaims } from "./types";
12
+ import { bytesToHex } from "./session";
13
+
14
+ /** Signed request ready to POST to a node. */
15
+ export interface SignedRequest {
16
+ group_id: string;
17
+ key_suffix?: string;
18
+ session_pub: string;
19
+ request_sig: string;
20
+ nonce: string;
21
+ timestamp: number;
22
+ }
23
+
24
+ /** Signed request with message_hash for /v1/sign. */
25
+ export interface SignedSignRequest extends SignedRequest {
26
+ message_hash: string;
27
+ }
28
+
29
+ /**
30
+ * Derive the key ID that the node will resolve for this session.
31
+ *
32
+ * For OAuth sessions: iss:sub or iss:sub:suffix
33
+ * e.g. https://accounts.google.com:114810956681671373980
34
+ */
35
+ export function deriveKeyId(claims: IdTokenClaims, keySuffix?: string, identity?: string): string {
36
+ const base = identity ?? `${claims.iss}:${claims.sub}`;
37
+ return keySuffix ? `${base}:${keySuffix}` : base;
38
+ }
39
+
40
+ /**
41
+ * Build and sign a keygen request.
42
+ *
43
+ * @param identity - For auth key cert sessions, pass the identity string.
44
+ * The key ID becomes `identity[:suffix]` instead of `iss:sub[:suffix]`.
45
+ */
46
+ export async function signKeygenRequest(
47
+ keypair: SessionKeypair,
48
+ claims: IdTokenClaims,
49
+ groupId: string,
50
+ keySuffix?: string,
51
+ identity?: string,
52
+ ): Promise<SignedRequest> {
53
+ const normalizedGroupId = groupId.toLowerCase();
54
+ const keyId = deriveKeyId(claims, keySuffix, identity);
55
+ const nonce = generateNonce();
56
+ const timestamp = Math.floor(Date.now() / 1000);
57
+
58
+ const hash = await canonicalRequestHash(normalizedGroupId, keyId, nonce, timestamp);
59
+ const sig = await signHash(keypair.privateKey, hash);
60
+
61
+ return {
62
+ group_id: normalizedGroupId,
63
+ key_suffix: keySuffix,
64
+ session_pub: keypair.publicKeyHex,
65
+ request_sig: bytesToHex(sig),
66
+ nonce,
67
+ timestamp,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Build and sign a threshold signing request.
73
+ */
74
+ export async function signSignRequest(
75
+ keypair: SessionKeypair,
76
+ claims: IdTokenClaims,
77
+ groupId: string,
78
+ messageHash: Uint8Array,
79
+ keySuffix?: string,
80
+ identity?: string,
81
+ ): Promise<SignedSignRequest> {
82
+ const normalizedGroupId = groupId.toLowerCase();
83
+ const keyId = deriveKeyId(claims, keySuffix, identity);
84
+ const nonce = generateNonce();
85
+ const timestamp = Math.floor(Date.now() / 1000);
86
+
87
+ const hash = await canonicalRequestHash(
88
+ normalizedGroupId,
89
+ keyId,
90
+ nonce,
91
+ timestamp,
92
+ messageHash
93
+ );
94
+ const sig = await signHash(keypair.privateKey, hash);
95
+
96
+ return {
97
+ group_id: normalizedGroupId,
98
+ key_suffix: keySuffix,
99
+ session_pub: keypair.publicKeyHex,
100
+ request_sig: bytesToHex(sig),
101
+ nonce,
102
+ timestamp,
103
+ message_hash: bytesToHex(messageHash),
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Compute the canonical request hash matching the Go node's format.
109
+ *
110
+ * SHA256(groupID ":" keyID ":" nonce ":" timestamp_8BE [":" messageHash])
111
+ */
112
+ async function canonicalRequestHash(
113
+ groupId: string,
114
+ keyId: string,
115
+ nonce: string,
116
+ timestamp: number,
117
+ messageHash?: Uint8Array
118
+ ): Promise<Uint8Array> {
119
+ const parts: Uint8Array[] = [];
120
+ const enc = new TextEncoder();
121
+
122
+ parts.push(enc.encode(groupId));
123
+ parts.push(enc.encode(":"));
124
+ parts.push(enc.encode(keyId));
125
+ parts.push(enc.encode(":"));
126
+ parts.push(enc.encode(nonce));
127
+ parts.push(enc.encode(":"));
128
+
129
+ // timestamp as 8-byte big-endian
130
+ const tsBuf = new ArrayBuffer(8);
131
+ const view = new DataView(tsBuf);
132
+ view.setBigUint64(0, BigInt(timestamp));
133
+ parts.push(new Uint8Array(tsBuf));
134
+
135
+ if (messageHash && messageHash.length > 0) {
136
+ parts.push(enc.encode(":"));
137
+ parts.push(messageHash);
138
+ }
139
+
140
+ // Concatenate
141
+ const total = parts.reduce((n, p) => n + p.length, 0);
142
+ const buf = new Uint8Array(total);
143
+ let offset = 0;
144
+ for (const p of parts) {
145
+ buf.set(p, offset);
146
+ offset += p.length;
147
+ }
148
+
149
+ const digest = await crypto.subtle.digest("SHA-256", buf);
150
+ return new Uint8Array(digest);
151
+ }
152
+
153
+ /**
154
+ * Sign a 32-byte hash with the session private key.
155
+ * Returns 64-byte [R || S] signature (no recovery byte).
156
+ *
157
+ * Uses signAsync which works without configuring hashes.sha256.
158
+ * lowS: true to match go-ethereum's crypto.VerifySignature.
159
+ */
160
+ async function signHash(
161
+ privateKey: Uint8Array,
162
+ hash: Uint8Array
163
+ ): Promise<Uint8Array> {
164
+ const { signAsync } = await import("@noble/secp256k1");
165
+ // prehash: false — our input is already SHA-256'd, don't hash again
166
+ const sig = await signAsync(hash, privateKey, { lowS: true, prehash: false });
167
+ return new Uint8Array(sig);
168
+ }
169
+
170
+ function generateNonce(): string {
171
+ const bytes = new Uint8Array(16);
172
+ crypto.getRandomValues(bytes);
173
+ return bytesToHex(bytes);
174
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Structured EIP-712 signing for scoped keys.
3
+ *
4
+ * Scoped keys reject raw hash signing — the caller must provide a
5
+ * structured payload that the node verifies against the key's scope
6
+ * before computing the hash and signing.
7
+ */
8
+
9
+ import type { SessionKeypair, IdTokenClaims } from "./types";
10
+ import { signKeygenRequest } from "./request";
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Types
14
+ // ---------------------------------------------------------------------------
15
+
16
+ export interface EIP712Domain {
17
+ name?: string;
18
+ version?: string;
19
+ chainId: number;
20
+ verifyingContract: string;
21
+ }
22
+
23
+ export interface EIP712TypedData {
24
+ domain: EIP712Domain;
25
+ types: Record<string, Array<{ name: string; type: string }>>;
26
+ primaryType: string;
27
+ message: Record<string, unknown>;
28
+ }
29
+
30
+ export interface ScopedSignResult {
31
+ signature: string; // raw signature hex
32
+ ecdsaSignature: string; // ECDSA-formatted (r, s, v)
33
+ curve: string;
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Scope construction
38
+ // ---------------------------------------------------------------------------
39
+
40
+ /**
41
+ * Build an EIP-712 domain scope (scheme 0x03).
42
+ *
43
+ * Format: 0x03 | chainId (8 bytes, uint64 BE) | verifyingContract (20 bytes)
44
+ * Total: 29 bytes.
45
+ */
46
+ export function buildEIP712Scope(chainId: number, verifyingContract: string): string {
47
+ const buf = new Uint8Array(29);
48
+ buf[0] = 0x03;
49
+
50
+ // chainId as 8-byte big-endian
51
+ const view = new DataView(buf.buffer);
52
+ view.setBigUint64(1, BigInt(chainId));
53
+
54
+ // verifyingContract as 20 bytes
55
+ const addr = verifyingContract.startsWith("0x")
56
+ ? verifyingContract.slice(2)
57
+ : verifyingContract;
58
+ for (let i = 0; i < 20; i++) {
59
+ buf[9 + i] = parseInt(addr.slice(i * 2, i * 2 + 2), 16);
60
+ }
61
+
62
+ return "0x" + Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Structured signing
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /**
70
+ * Sign a structured EIP-712 payload with a scoped key.
71
+ *
72
+ * The node extracts the domain from the typed data, verifies it matches
73
+ * the key's scope, computes hashTypedData, and threshold-signs.
74
+ *
75
+ * @param nodeUrl - Target group node URL
76
+ * @param proxyEndpoint - CORS proxy URL
77
+ * @param groupId - Group contract address
78
+ * @param keyId - The scoped sub-key to sign with
79
+ * @param curve - Key curve (e.g. "ecdsa_secp256k1")
80
+ * @param typedData - Full EIP-712 typed data structure
81
+ * @param sessionKeypair - Active session keypair
82
+ * @param claims - OAuth/identity claims for session auth
83
+ * @param identity - For auth key cert sessions
84
+ */
85
+ export async function signTypedData(
86
+ nodeUrl: string,
87
+ proxyEndpoint: string,
88
+ groupId: string,
89
+ keyId: string,
90
+ curve: string,
91
+ typedData: EIP712TypedData,
92
+ sessionKeypair: SessionKeypair,
93
+ claims: IdTokenClaims,
94
+ identity?: string,
95
+ ): Promise<ScopedSignResult> {
96
+ // Build session-authenticated request (no message hash — payload is sent separately)
97
+ // The canonical hash must use the full sub-key ID (identity + suffix).
98
+ // Extract suffix from keyId: "oauth:iss:sub:suffix" → suffix is last segment
99
+ // The identity param is "iss:sub", so we need to add the suffix.
100
+ const keyParts = keyId.split(":");
101
+ const keySuffix = keyParts.length > 1 ? keyParts[keyParts.length - 1] : undefined;
102
+
103
+ const signReq = await signKeygenRequest(
104
+ sessionKeypair,
105
+ claims,
106
+ groupId,
107
+ keySuffix,
108
+ identity,
109
+ );
110
+
111
+ const res = await fetch(proxyEndpoint, {
112
+ method: "POST",
113
+ headers: {
114
+ "Content-Type": "application/json",
115
+ "x-node-url": nodeUrl,
116
+ "x-node-path": "/v1/sign",
117
+ },
118
+ body: JSON.stringify({
119
+ group_id: groupId.toLowerCase(),
120
+ key_id: keyId,
121
+ key_suffix: keySuffix,
122
+ curve,
123
+ payload: {
124
+ scheme: "eip712",
125
+ typed_data: typedData,
126
+ },
127
+ session_pub: signReq.session_pub,
128
+ request_sig: signReq.request_sig,
129
+ nonce: signReq.nonce,
130
+ timestamp: signReq.timestamp,
131
+ }),
132
+ });
133
+
134
+ if (!res.ok) {
135
+ const body = await res.text();
136
+ throw new Error(`Scoped sign failed: ${res.status} — ${body}`);
137
+ }
138
+
139
+ const data = await res.json();
140
+ return {
141
+ signature: data.signature,
142
+ ecdsaSignature: data.ecdsa_signature,
143
+ curve: data.curve ?? curve,
144
+ };
145
+ }
146
+
147
+ // ---------------------------------------------------------------------------
148
+ // Presets
149
+ // ---------------------------------------------------------------------------
150
+
151
+ export const CHAIN_PRESETS = [
152
+ {
153
+ label: "USDC on Base",
154
+ chainId: 8453,
155
+ contractName: "USDC",
156
+ verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
157
+ eip712Name: "USD Coin",
158
+ eip712Version: "2",
159
+ },
160
+ {
161
+ label: "USDC on Base Sepolia",
162
+ chainId: 84532,
163
+ contractName: "USDC",
164
+ verifyingContract: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
165
+ eip712Name: "USD Coin",
166
+ eip712Version: "2",
167
+ },
168
+ {
169
+ label: "USDC on Ethereum",
170
+ chainId: 1,
171
+ contractName: "USDC",
172
+ verifyingContract: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
173
+ eip712Name: "USD Coin",
174
+ eip712Version: "2",
175
+ },
176
+ {
177
+ label: "USDC on Sepolia",
178
+ chainId: 11155111,
179
+ contractName: "USDC",
180
+ verifyingContract: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
181
+ eip712Name: "USD Coin",
182
+ eip712Version: "2",
183
+ },
184
+ ] as const;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Server-side ZK proof generation via the bundler's /v1/prove endpoint.
3
+ *
4
+ * Delegates JWT proof generation to the bundler instead of running
5
+ * noir + bb.js client-side via WASM. Faster (~2-3s vs 2-7s) and
6
+ * avoids shipping heavy WASM binaries to the browser.
7
+ *
8
+ * The returned proof + claims + modulus are everything needed to
9
+ * call authenticateWithBootstrap.
10
+ */
11
+
12
+ export interface ServerProofResult {
13
+ proof: Uint8Array;
14
+ sub: string;
15
+ iss: string;
16
+ exp: number;
17
+ aud: string;
18
+ azp: string;
19
+ jwksModulus: Uint8Array;
20
+ sessionPub: string;
21
+ }
22
+
23
+ /**
24
+ * Generate a ZK proof of a JWT via the bundler's server-side prover.
25
+ *
26
+ * @param bundlerProxyUrl - URL of the bundler proxy (e.g. "/api/bundler")
27
+ * @param jwt - Raw JWT from OAuth provider
28
+ * @param sessionPubHex - 33-byte compressed secp256k1 public key, hex-encoded
29
+ */
30
+ export async function generateServerProof(
31
+ bundlerProxyUrl: string,
32
+ jwt: string,
33
+ sessionPubHex: string,
34
+ apiKey?: string,
35
+ ): Promise<ServerProofResult> {
36
+ const headers: Record<string, string> = {
37
+ "Content-Type": "application/json",
38
+ "x-bundler-path": "/v1/prove",
39
+ };
40
+ if (apiKey) headers["X-API-Key"] = apiKey;
41
+
42
+ const res = await fetch(bundlerProxyUrl, {
43
+ method: "POST",
44
+ headers,
45
+ body: JSON.stringify({ jwt, session_pub: sessionPubHex }),
46
+ });
47
+
48
+ const result = await res.json();
49
+
50
+ if (!res.ok) {
51
+ const msg = result.error ?? JSON.stringify(result);
52
+ throw new Error(`Server proof generation failed: ${res.status} — ${msg}`);
53
+ }
54
+ if (result.error) {
55
+ const msg = typeof result.error === "string" ? result.error : JSON.stringify(result.error);
56
+ throw new Error(`Server proof generation failed: ${msg}`);
57
+ }
58
+
59
+ return {
60
+ proof: hexToBytes(result.proof),
61
+ sub: result.sub,
62
+ iss: result.iss,
63
+ exp: result.exp,
64
+ aud: result.aud,
65
+ azp: result.azp,
66
+ jwksModulus: hexToBytes(result.jwks_modulus),
67
+ sessionPub: result.session_pub,
68
+ };
69
+ }
70
+
71
+ function hexToBytes(hex: string): Uint8Array {
72
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
73
+ return new Uint8Array(
74
+ (clean.match(/.{2}/g) ?? []).map((b) => parseInt(b, 16))
75
+ );
76
+ }
package/src/session.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Session keypair management (secp256k1).
3
+ *
4
+ * Generates an ephemeral keypair for authenticating with bootstrap nodes.
5
+ * The private key is held in memory only — never persisted.
6
+ */
7
+
8
+ import type { SessionKeypair } from "./types";
9
+
10
+ /**
11
+ * Generate a new ephemeral secp256k1 session keypair.
12
+ */
13
+ export async function generateSessionKeypair(): Promise<SessionKeypair> {
14
+ const { utils, getPublicKey } = await import("@noble/secp256k1");
15
+ const privateKey = utils.randomSecretKey();
16
+ const publicKeyBytes = getPublicKey(privateKey, true); // compressed
17
+ const publicKeyHex = bytesToHex(publicKeyBytes);
18
+ return { privateKey, publicKeyHex };
19
+ }
20
+
21
+ export function bytesToHex(bytes: Uint8Array): string {
22
+ return Array.from(bytes)
23
+ .map((b) => b.toString(16).padStart(2, "0"))
24
+ .join("");
25
+ }
26
+
27
+ export function hexToBytes(hex: string): Uint8Array {
28
+ const bytes = new Uint8Array(hex.length / 2);
29
+ for (let i = 0; i < bytes.length; i++) {
30
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
31
+ }
32
+ return bytes;
33
+ }
package/src/types.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Shared types for the Signet SDK.
3
+ */
4
+
5
+ /** Decoded Google ID token claims. */
6
+ export interface IdTokenClaims {
7
+ iss: string; // "https://accounts.google.com"
8
+ sub: string; // stable numeric user ID
9
+ email: string;
10
+ name?: string;
11
+ picture?: string;
12
+ azp: string; // OAuth client ID
13
+ aud: string;
14
+ exp: number; // unix timestamp
15
+ iat: number;
16
+ }
17
+
18
+ /** RSA public key from JWKS. */
19
+ export interface JWKSKey {
20
+ kid: string;
21
+ kty: string;
22
+ alg: string;
23
+ n: string; // base64url-encoded modulus
24
+ e: string; // base64url-encoded exponent
25
+ }
26
+
27
+ /** Session keypair (secp256k1). */
28
+ export interface SessionKeypair {
29
+ privateKey: Uint8Array;
30
+ publicKeyHex: string; // 33-byte compressed, hex-encoded
31
+ }
32
+
33
+ /** Witness inputs for the jwt_auth noir circuit. */
34
+ export interface CircuitWitness {
35
+ // Private witness
36
+ data: number[]; // JWT signed data bytes, padded to 1024
37
+ dataLen: number; // actual length of signed data
38
+ base64DecodeOffset: number; // header length + 1
39
+ redcParamsLimbs: string[]; // 18 limbs, decimal strings
40
+ signatureLimbs: string[]; // 18 limbs, decimal strings
41
+
42
+ // Public inputs
43
+ pubkeyModulusLimbs: string[]; // 18 limbs, decimal strings
44
+ iss: string;
45
+ sub: string;
46
+ exp: number;
47
+ aud: string;
48
+ azp: string;
49
+ sessionPub: number[]; // 33 bytes
50
+ }
51
+
52
+ /** Auth request body for bootstrap node /v1/auth (OAuth/ZK path). */
53
+ export interface NodeAuthRequest {
54
+ group_id: string;
55
+ session_pub: string; // hex
56
+ proof: string; // hex
57
+ sub: string;
58
+ iss: string;
59
+ exp: number;
60
+ aud: string;
61
+ azp: string;
62
+ jwks_modulus: string; // hex
63
+ }