@tinycloud/server 2.4.0-beta.20

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/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # @tinycloud/server
2
+
3
+ Reusable server and agent helpers for TinyCloud backends.
4
+
5
+ Servers use a raw Ethereum private key as a stable `did:pkh` identity. OpenKey is
6
+ for browser and human-owner sign-in flows; it is not the backend identity.
7
+
8
+ ```ts
9
+ import {
10
+ createServerDelegateClient,
11
+ createServerIdentity,
12
+ createSiweSession,
13
+ } from "@tinycloud/server";
14
+
15
+ const identity = await createServerIdentity({
16
+ privateKey: process.env.TC_SERVER_PRIVATE_KEY!,
17
+ host: process.env.TC_HOST,
18
+ });
19
+
20
+ console.log(identity.did);
21
+
22
+ const delegated = createServerDelegateClient({
23
+ privateKey: process.env.TC_SERVER_PRIVATE_KEY!,
24
+ host: process.env.TC_HOST,
25
+ delegation: ownerDelegation,
26
+ });
27
+
28
+ const githubToken = await delegated.getSecret("GITHUB_TOKEN", { scope: "githaiku" });
29
+
30
+ const auth = createSiweSession({ jwtSecret: process.env.TC_SERVER_PRIVATE_KEY! });
31
+ const nonce = auth.issueNonce(ownerAddress);
32
+ const session = await auth.verify(siweMessage, signature);
33
+ const owner = auth.verifyToken(session.token);
34
+ ```
35
+
36
+ For dstack TEEs, derive the server key from the guest-agent client and pass it to
37
+ `createServerIdentity`:
38
+
39
+ ```ts
40
+ import { deriveDstackPrivateKey } from "@tinycloud/server";
41
+
42
+ const privateKey = await deriveDstackPrivateKey({
43
+ client: dstackClient,
44
+ path: "my-app/keys/server",
45
+ purpose: "server",
46
+ });
47
+ ```
48
+
49
+ `createServerDelegateClient` activates the whole PortableDelegation before
50
+ reading secrets. That keeps both the delegated KV-get and
51
+ `tinycloud.encryption/decrypt` proof in the activation chain.
package/dist/index.cjs ADDED
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ NonceStore: () => NonceStore,
24
+ ServerAuthError: () => ServerAuthError,
25
+ createServerDelegateClient: () => createServerDelegateClient,
26
+ createServerIdentity: () => createServerIdentity,
27
+ createSiweSession: () => createSiweSession,
28
+ deriveDstackPrivateKey: () => deriveDstackPrivateKey,
29
+ isTinyCloudSessionError: () => isTinyCloudSessionError,
30
+ issueSessionToken: () => issueSessionToken,
31
+ parseDelegation: () => parseDelegation,
32
+ parseEncryptedEnvelope: () => parseEncryptedEnvelope,
33
+ parseSecretPayload: () => parseSecretPayload,
34
+ readDelegatedSecret: () => readDelegatedSecret,
35
+ serverDidForPrivateKey: () => serverDidForPrivateKey,
36
+ verifySessionToken: () => verifySessionToken,
37
+ verifySiweMessage: () => verifySiweMessage,
38
+ withSessionRefresh: () => withSessionRefresh
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+
42
+ // src/identity.ts
43
+ var import_viem = require("viem");
44
+ var import_accounts = require("viem/accounts");
45
+ var import_node_sdk = require("@tinycloud/node-sdk");
46
+ var DEFAULT_HOST = "https://node.tinycloud.xyz";
47
+ async function deriveDstackPrivateKey(options) {
48
+ const res = await options.client.getKey(options.path, options.purpose);
49
+ if (!(res.key instanceof Uint8Array) || res.key.length === 0) {
50
+ throw new Error("dstack getKey returned no key material");
51
+ }
52
+ return (0, import_viem.keccak256)(res.key);
53
+ }
54
+ function serverDidForPrivateKey(privateKey) {
55
+ const account = (0, import_accounts.privateKeyToAccount)(privateKey);
56
+ return `did:pkh:eip155:1:${account.address}`;
57
+ }
58
+ async function createServerIdentity(options) {
59
+ const host = options.host ?? DEFAULT_HOST;
60
+ const node = new import_node_sdk.TinyCloudNode({
61
+ ...options.nodeConfig,
62
+ privateKey: options.privateKey,
63
+ host,
64
+ prefix: options.prefix,
65
+ manifest: options.manifest,
66
+ autoCreateSpace: options.autoCreateSpace ?? false,
67
+ enablePublicSpace: options.enablePublicSpace ?? false,
68
+ includeAccountRegistryPermissions: options.includeAccountRegistryPermissions ?? false
69
+ });
70
+ await node.signIn();
71
+ return {
72
+ node,
73
+ did: node.did,
74
+ host,
75
+ privateKey: options.privateKey
76
+ };
77
+ }
78
+ var SESSION_ERROR_PATTERN = /\b(session\s+expired|invalid\s+session|token\s+expired|expired\s+credentials?|unauthorized|unauthenticated|sign.?in\s*required)\b|\b401\b(?![\d-])/i;
79
+ function isTinyCloudSessionError(error) {
80
+ const message = error instanceof Error ? error.message : String(error);
81
+ return SESSION_ERROR_PATTERN.test(message);
82
+ }
83
+ async function withSessionRefresh(node, fn) {
84
+ try {
85
+ return await fn();
86
+ } catch (error) {
87
+ if (isTinyCloudSessionError(error)) {
88
+ await node.signIn();
89
+ return fn();
90
+ }
91
+ throw error;
92
+ }
93
+ }
94
+
95
+ // src/delegated-secrets.ts
96
+ var import_node_sdk2 = require("@tinycloud/node-sdk");
97
+ var import_sdk_core = require("@tinycloud/sdk-core");
98
+ function createServerDelegateClient(options) {
99
+ const delegation = parseDelegation(options.delegation);
100
+ let nodePromise;
101
+ async function getNode() {
102
+ if (!nodePromise) {
103
+ if (options.node) {
104
+ nodePromise = Promise.resolve(options.node);
105
+ return options.node;
106
+ }
107
+ const identityOptions = {
108
+ privateKey: options.privateKey,
109
+ host: options.host,
110
+ prefix: options.prefix,
111
+ enablePublicSpace: false,
112
+ includeAccountRegistryPermissions: false,
113
+ nodeConfig: options.nodeConfig
114
+ };
115
+ const created = options.nodeFactory ? options.nodeFactory(identityOptions) : createServerIdentity(identityOptions).then(
116
+ (identity) => identity.node
117
+ );
118
+ nodePromise = created;
119
+ return created;
120
+ }
121
+ return nodePromise;
122
+ }
123
+ return {
124
+ async getSecret(name, secretOptions) {
125
+ const node = await getNode();
126
+ return readDelegatedSecret(node, delegation, name, secretOptions);
127
+ }
128
+ };
129
+ }
130
+ async function readDelegatedSecret(node, delegation, name, options) {
131
+ const secretKey = (0, import_sdk_core.resolveSecretPath)(name, options).permissionPaths.vault;
132
+ const access = await node.useDelegation(delegation);
133
+ const result = await access.kv.get(secretKey, { raw: true, prefix: "" });
134
+ if (!result.ok) {
135
+ const message = result.error?.message ?? `failed to read ${secretKey}`;
136
+ throw new Error(`delegated secret ${name} KV get failed: ${message}`);
137
+ }
138
+ const envelope = parseEncryptedEnvelope(
139
+ result.data?.data,
140
+ name
141
+ );
142
+ const proofCid = access.restorable?.delegationCid ?? access.delegation.cid;
143
+ if (!proofCid) {
144
+ throw new Error(`delegated secret ${name} has no decrypt proof`);
145
+ }
146
+ const decrypted = await node.encryption.decryptEnvelope(envelope, { proofs: [proofCid] });
147
+ if (!decrypted.ok) {
148
+ throw new Error(`delegated secret ${name} decrypt failed: ${decrypted.error.message}`);
149
+ }
150
+ return parseSecretPayload(decrypted.data, name);
151
+ }
152
+ function parseDelegation(delegation) {
153
+ return typeof delegation === "string" ? (0, import_node_sdk2.deserializeDelegation)(delegation) : delegation;
154
+ }
155
+ function parseEncryptedEnvelope(rawEnvelope, name = "secret") {
156
+ const parsed = typeof rawEnvelope === "string" ? JSON.parse(rawEnvelope) : rawEnvelope;
157
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.v !== "number" || typeof parsed.networkId !== "string" || typeof parsed.ciphertext !== "string" || typeof parsed.encryptedSymmetricKey !== "string") {
158
+ throw new Error(`delegated secret ${name} did not contain an encrypted envelope`);
159
+ }
160
+ return parsed;
161
+ }
162
+ function parseSecretPayload(plaintext, name = "secret") {
163
+ let parsed;
164
+ try {
165
+ parsed = JSON.parse(new TextDecoder().decode(plaintext));
166
+ } catch {
167
+ throw new Error(`delegated secret ${name} did not contain valid JSON`);
168
+ }
169
+ if (typeof parsed.value !== "string") {
170
+ throw new Error(`delegated secret ${name} did not contain a string value`);
171
+ }
172
+ return parsed.value;
173
+ }
174
+
175
+ // src/siwe-session.ts
176
+ var import_node_crypto = require("crypto");
177
+ var import_viem2 = require("viem");
178
+ var DEFAULT_NONCE_TTL_MS = 5 * 60 * 1e3;
179
+ var DEFAULT_SESSION_TTL_SECONDS = 24 * 60 * 60;
180
+ var ServerAuthError = class extends Error {
181
+ };
182
+ var NonceStore = class {
183
+ constructor(ttlMs = DEFAULT_NONCE_TTL_MS) {
184
+ this.ttlMs = ttlMs;
185
+ }
186
+ nonces = /* @__PURE__ */ new Map();
187
+ issue(address) {
188
+ this.sweep();
189
+ const normalized = (0, import_viem2.getAddress)(address).toLowerCase();
190
+ const nonce = (0, import_node_crypto.randomBytes)(16).toString("hex");
191
+ this.nonces.set(this.key(normalized, nonce), {
192
+ address: normalized,
193
+ createdAt: Date.now()
194
+ });
195
+ return nonce;
196
+ }
197
+ validate(address, nonce) {
198
+ const normalized = (0, import_viem2.getAddress)(address).toLowerCase();
199
+ const key = this.key(normalized, nonce);
200
+ const entry = this.nonces.get(key);
201
+ if (!entry) return false;
202
+ this.nonces.delete(key);
203
+ return Date.now() - entry.createdAt <= this.ttlMs;
204
+ }
205
+ key(address, nonce) {
206
+ return `${address}:${nonce}`;
207
+ }
208
+ sweep() {
209
+ const now = Date.now();
210
+ for (const [key, entry] of this.nonces) {
211
+ if (now - entry.createdAt > this.ttlMs) this.nonces.delete(key);
212
+ }
213
+ }
214
+ };
215
+ async function verifySiweMessage(message, signature) {
216
+ const lines = message.split("\n");
217
+ let claimed;
218
+ try {
219
+ claimed = (0, import_viem2.getAddress)((lines[1] ?? "").trim());
220
+ } catch {
221
+ throw new ServerAuthError("SIWE message is missing a valid address on line 2");
222
+ }
223
+ const nonceMatch = message.match(/^Nonce: (.+)$/m);
224
+ if (!nonceMatch || nonceMatch[1] === void 0) {
225
+ throw new ServerAuthError("SIWE message is missing a Nonce line");
226
+ }
227
+ const nonce = nonceMatch[1].trim();
228
+ let recovered;
229
+ try {
230
+ recovered = await (0, import_viem2.recoverMessageAddress)({ message, signature });
231
+ } catch (error) {
232
+ throw new ServerAuthError(
233
+ `SIWE signature recovery failed: ${error instanceof Error ? error.message : String(error)}`
234
+ );
235
+ }
236
+ if ((0, import_viem2.getAddress)(recovered) !== claimed) {
237
+ throw new ServerAuthError(
238
+ `SIWE signature does not match message address (recovered ${recovered}, expected ${claimed})`
239
+ );
240
+ }
241
+ return { address: claimed, nonce };
242
+ }
243
+ function issueSessionToken(address, secret, ttlSeconds = DEFAULT_SESSION_TTL_SECONDS) {
244
+ const normalized = (0, import_viem2.getAddress)(address);
245
+ const now = Math.floor(Date.now() / 1e3);
246
+ const claims = {
247
+ sub: normalized,
248
+ address: normalized,
249
+ iat: now,
250
+ exp: now + ttlSeconds
251
+ };
252
+ return {
253
+ token: signJwt({ alg: "HS256", typ: "JWT" }, claims, secret),
254
+ expiresIn: ttlSeconds
255
+ };
256
+ }
257
+ function verifySessionToken(token, secret) {
258
+ const claims = verifyJwt(token, secret);
259
+ if (typeof claims.sub !== "string" || claims.sub === "") {
260
+ throw new ServerAuthError("session token missing 'sub' claim");
261
+ }
262
+ return { address: claims.sub };
263
+ }
264
+ function createSiweSession(options) {
265
+ const nonceStore = options.nonceStore ?? new NonceStore();
266
+ const sessionTtlSeconds = options.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS;
267
+ return {
268
+ issueNonce(address) {
269
+ return nonceStore.issue(address);
270
+ },
271
+ async verify(message, signature) {
272
+ const verified = await verifySiweMessage(message, signature);
273
+ if (!nonceStore.validate(verified.address, verified.nonce)) {
274
+ throw new ServerAuthError("nonce is invalid, expired, or already used");
275
+ }
276
+ return issueSessionToken(verified.address, options.jwtSecret, sessionTtlSeconds);
277
+ },
278
+ verifyToken(token) {
279
+ return verifySessionToken(token, options.jwtSecret);
280
+ }
281
+ };
282
+ }
283
+ function signJwt(header, payload, secret) {
284
+ const encodedHeader = base64UrlEncode(JSON.stringify(header));
285
+ const encodedPayload = base64UrlEncode(JSON.stringify(payload));
286
+ const signingInput = `${encodedHeader}.${encodedPayload}`;
287
+ const signature = (0, import_node_crypto.createHmac)("sha256", secret).update(signingInput).digest();
288
+ return `${signingInput}.${base64UrlEncode(signature)}`;
289
+ }
290
+ function verifyJwt(token, secret) {
291
+ const parts = token.split(".");
292
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {
293
+ throw new ServerAuthError("session token is not a valid JWT");
294
+ }
295
+ const signingInput = `${parts[0]}.${parts[1]}`;
296
+ const expected = (0, import_node_crypto.createHmac)("sha256", secret).update(signingInput).digest();
297
+ const actual = base64UrlDecode(parts[2]);
298
+ if (actual.length !== expected.length || !(0, import_node_crypto.timingSafeEqual)(actual, expected)) {
299
+ throw new ServerAuthError("session token signature verification failed");
300
+ }
301
+ const claims = JSON.parse(base64UrlDecode(parts[1]).toString("utf8"));
302
+ if (typeof claims.exp !== "number" || claims.exp <= Math.floor(Date.now() / 1e3)) {
303
+ throw new ServerAuthError("session token expired");
304
+ }
305
+ return claims;
306
+ }
307
+ function base64UrlEncode(value) {
308
+ const buffer = typeof value === "string" ? Buffer.from(value, "utf8") : value;
309
+ return buffer.toString("base64url");
310
+ }
311
+ function base64UrlDecode(value) {
312
+ return Buffer.from(value, "base64url");
313
+ }
314
+ // Annotate the CommonJS export names for ESM import in node:
315
+ 0 && (module.exports = {
316
+ NonceStore,
317
+ ServerAuthError,
318
+ createServerDelegateClient,
319
+ createServerIdentity,
320
+ createSiweSession,
321
+ deriveDstackPrivateKey,
322
+ isTinyCloudSessionError,
323
+ issueSessionToken,
324
+ parseDelegation,
325
+ parseEncryptedEnvelope,
326
+ parseSecretPayload,
327
+ readDelegatedSecret,
328
+ serverDidForPrivateKey,
329
+ verifySessionToken,
330
+ verifySiweMessage,
331
+ withSessionRefresh
332
+ });
333
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/identity.ts","../src/delegated-secrets.ts","../src/siwe-session.ts"],"sourcesContent":["export {\n createServerIdentity,\n deriveDstackPrivateKey,\n isTinyCloudSessionError,\n serverDidForPrivateKey,\n withSessionRefresh,\n type CreateServerIdentityOptions,\n type DeriveDstackPrivateKeyOptions,\n type DstackKeyClient,\n type ServerIdentity,\n} from \"./identity.js\";\n\nexport {\n createServerDelegateClient,\n parseDelegation,\n parseEncryptedEnvelope,\n parseSecretPayload,\n readDelegatedSecret,\n type CreateServerDelegateClientOptions,\n type DelegationInput,\n type ServerDelegateClient,\n} from \"./delegated-secrets.js\";\n\nexport {\n NonceStore,\n ServerAuthError,\n createSiweSession,\n issueSessionToken,\n verifySessionToken,\n verifySiweMessage,\n type CreateSiweSessionOptions,\n type SessionClaims,\n type SessionToken,\n type VerifiedSiwe,\n} from \"./siwe-session.js\";\n","import { keccak256, type Hex } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { TinyCloudNode, type Manifest, type TinyCloudNodeConfig } from \"@tinycloud/node-sdk\";\n\nconst DEFAULT_HOST = \"https://node.tinycloud.xyz\";\n\nexport interface DstackKeyClient {\n getKey(path: string, purpose: string): Promise<{ key: Uint8Array }>;\n}\n\nexport interface DeriveDstackPrivateKeyOptions {\n client: DstackKeyClient;\n path: string;\n purpose: string;\n}\n\nexport async function deriveDstackPrivateKey(\n options: DeriveDstackPrivateKeyOptions,\n): Promise<Hex> {\n const res = await options.client.getKey(options.path, options.purpose);\n if (!(res.key instanceof Uint8Array) || res.key.length === 0) {\n throw new Error(\"dstack getKey returned no key material\");\n }\n return keccak256(res.key);\n}\n\nexport function serverDidForPrivateKey(privateKey: string): string {\n const account = privateKeyToAccount(privateKey as Hex);\n return `did:pkh:eip155:1:${account.address}`;\n}\n\nexport interface CreateServerIdentityOptions {\n privateKey: string;\n host?: string;\n prefix?: string;\n manifest?: Manifest | Manifest[];\n autoCreateSpace?: boolean;\n enablePublicSpace?: boolean;\n includeAccountRegistryPermissions?: boolean;\n nodeConfig?: Omit<\n TinyCloudNodeConfig,\n | \"privateKey\"\n | \"host\"\n | \"prefix\"\n | \"manifest\"\n | \"autoCreateSpace\"\n | \"enablePublicSpace\"\n | \"includeAccountRegistryPermissions\"\n >;\n}\n\nexport interface ServerIdentity {\n node: TinyCloudNode;\n did: string;\n host: string;\n privateKey: string;\n}\n\nexport async function createServerIdentity(\n options: CreateServerIdentityOptions,\n): Promise<ServerIdentity> {\n const host = options.host ?? DEFAULT_HOST;\n const node = new TinyCloudNode({\n ...options.nodeConfig,\n privateKey: options.privateKey,\n host,\n prefix: options.prefix,\n manifest: options.manifest,\n autoCreateSpace: options.autoCreateSpace ?? false,\n enablePublicSpace: options.enablePublicSpace ?? false,\n includeAccountRegistryPermissions: options.includeAccountRegistryPermissions ?? false,\n });\n\n await node.signIn();\n\n return {\n node,\n did: node.did,\n host,\n privateKey: options.privateKey,\n };\n}\n\nconst SESSION_ERROR_PATTERN =\n /\\b(session\\s+expired|invalid\\s+session|token\\s+expired|expired\\s+credentials?|unauthorized|unauthenticated|sign.?in\\s*required)\\b|\\b401\\b(?![\\d-])/i;\n\nexport function isTinyCloudSessionError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return SESSION_ERROR_PATTERN.test(message);\n}\n\nexport async function withSessionRefresh<T>(\n node: TinyCloudNode,\n fn: () => Promise<T>,\n): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (isTinyCloudSessionError(error)) {\n await node.signIn();\n return fn();\n }\n throw error;\n }\n}\n","import {\n TinyCloudNode,\n deserializeDelegation,\n type InlineEncryptedEnvelope,\n type PortableDelegation,\n type SecretScopeOptions,\n} from \"@tinycloud/node-sdk\";\nimport { resolveSecretPath } from \"@tinycloud/sdk-core\";\nimport type { CreateServerIdentityOptions } from \"./identity.js\";\nimport { createServerIdentity } from \"./identity.js\";\n\ntype KvLike = {\n get<T = unknown>(\n key: string,\n options: { raw: true; prefix: string },\n ): Promise<{ ok: true; data: T | unknown } | { ok: false; error?: { message?: string } }>;\n};\n\ntype DelegatedAccessLike = {\n kv: KvLike;\n delegation: Pick<PortableDelegation, \"cid\">;\n restorable?: { delegationCid?: string };\n};\n\ntype DecryptResult =\n | { ok: true; data: Uint8Array }\n | { ok: false; error: { code?: string; message: string } };\n\ntype EncryptionLike = {\n decryptEnvelope(\n envelope: InlineEncryptedEnvelope,\n capabilityProof: { proofs: string[] },\n ): Promise<DecryptResult>;\n};\n\ntype TinyCloudNodeLike = {\n signIn(): Promise<unknown>;\n useDelegation(delegation: PortableDelegation): Promise<DelegatedAccessLike>;\n encryption: EncryptionLike;\n};\n\nexport type DelegationInput = PortableDelegation | string;\n\nexport interface ServerDelegateClient {\n getSecret(name: string, options?: SecretScopeOptions): Promise<string>;\n}\n\nexport interface CreateServerDelegateClientOptions {\n privateKey: string;\n host?: string;\n delegation: DelegationInput;\n prefix?: string;\n nodeConfig?: CreateServerIdentityOptions[\"nodeConfig\"];\n node?: TinyCloudNodeLike;\n nodeFactory?: (options: CreateServerIdentityOptions) => Promise<TinyCloudNodeLike>;\n}\n\nexport function createServerDelegateClient(\n options: CreateServerDelegateClientOptions,\n): ServerDelegateClient {\n const delegation = parseDelegation(options.delegation);\n let nodePromise: Promise<TinyCloudNodeLike> | undefined;\n\n async function getNode(): Promise<TinyCloudNodeLike> {\n if (!nodePromise) {\n if (options.node) {\n nodePromise = Promise.resolve(options.node);\n return options.node;\n }\n const identityOptions: CreateServerIdentityOptions = {\n privateKey: options.privateKey,\n host: options.host,\n prefix: options.prefix,\n enablePublicSpace: false,\n includeAccountRegistryPermissions: false,\n nodeConfig: options.nodeConfig,\n };\n const created = options.nodeFactory\n ? options.nodeFactory(identityOptions)\n : createServerIdentity(identityOptions).then(\n (identity) => identity.node as unknown as TinyCloudNodeLike,\n );\n nodePromise = created;\n return created;\n }\n return nodePromise;\n }\n\n return {\n async getSecret(name: string, secretOptions?: SecretScopeOptions): Promise<string> {\n const node = await getNode();\n return readDelegatedSecret(node, delegation, name, secretOptions);\n },\n };\n}\n\nexport async function readDelegatedSecret(\n node: TinyCloudNodeLike | TinyCloudNode,\n delegation: PortableDelegation,\n name: string,\n options?: SecretScopeOptions,\n): Promise<string> {\n const secretKey = resolveSecretPath(name, options).permissionPaths.vault;\n\n const access = await node.useDelegation(delegation);\n const result = await access.kv.get<unknown>(secretKey, { raw: true, prefix: \"\" });\n if (!result.ok) {\n const message = result.error?.message ?? `failed to read ${secretKey}`;\n throw new Error(`delegated secret ${name} KV get failed: ${message}`);\n }\n\n const envelope = parseEncryptedEnvelope(\n (result.data as { data?: unknown } | undefined)?.data,\n name,\n );\n const proofCid = access.restorable?.delegationCid ?? access.delegation.cid;\n if (!proofCid) {\n throw new Error(`delegated secret ${name} has no decrypt proof`);\n }\n\n const decrypted = await node.encryption.decryptEnvelope(envelope, { proofs: [proofCid] });\n if (!decrypted.ok) {\n throw new Error(`delegated secret ${name} decrypt failed: ${decrypted.error.message}`);\n }\n\n return parseSecretPayload(decrypted.data, name);\n}\n\nexport function parseDelegation(delegation: DelegationInput): PortableDelegation {\n return typeof delegation === \"string\" ? deserializeDelegation(delegation) : delegation;\n}\n\nexport function parseEncryptedEnvelope(\n rawEnvelope: unknown,\n name = \"secret\",\n): InlineEncryptedEnvelope {\n const parsed = typeof rawEnvelope === \"string\" ? JSON.parse(rawEnvelope) : rawEnvelope;\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as Partial<InlineEncryptedEnvelope>).v !== \"number\" ||\n typeof (parsed as Partial<InlineEncryptedEnvelope>).networkId !== \"string\" ||\n typeof (parsed as Partial<InlineEncryptedEnvelope>).ciphertext !== \"string\" ||\n typeof (parsed as Partial<InlineEncryptedEnvelope>).encryptedSymmetricKey !== \"string\"\n ) {\n throw new Error(`delegated secret ${name} did not contain an encrypted envelope`);\n }\n return parsed as InlineEncryptedEnvelope;\n}\n\nexport function parseSecretPayload(plaintext: Uint8Array, name = \"secret\"): string {\n let parsed: { value?: unknown };\n try {\n parsed = JSON.parse(new TextDecoder().decode(plaintext)) as { value?: unknown };\n } catch {\n throw new Error(`delegated secret ${name} did not contain valid JSON`);\n }\n if (typeof parsed.value !== \"string\") {\n throw new Error(`delegated secret ${name} did not contain a string value`);\n }\n return parsed.value;\n}\n","import { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport { getAddress, recoverMessageAddress, type Hex } from \"viem\";\n\nconst DEFAULT_NONCE_TTL_MS = 5 * 60 * 1000;\nconst DEFAULT_SESSION_TTL_SECONDS = 24 * 60 * 60;\n\ninterface NonceEntry {\n address: string;\n createdAt: number;\n}\n\nexport class ServerAuthError extends Error {}\n\nexport class NonceStore {\n private readonly nonces = new Map<string, NonceEntry>();\n\n constructor(private readonly ttlMs = DEFAULT_NONCE_TTL_MS) {}\n\n issue(address: string): string {\n this.sweep();\n const normalized = getAddress(address).toLowerCase();\n const nonce = randomBytes(16).toString(\"hex\");\n this.nonces.set(this.key(normalized, nonce), {\n address: normalized,\n createdAt: Date.now(),\n });\n return nonce;\n }\n\n validate(address: string, nonce: string): boolean {\n const normalized = getAddress(address).toLowerCase();\n const key = this.key(normalized, nonce);\n const entry = this.nonces.get(key);\n if (!entry) return false;\n this.nonces.delete(key);\n return Date.now() - entry.createdAt <= this.ttlMs;\n }\n\n private key(address: string, nonce: string): string {\n return `${address}:${nonce}`;\n }\n\n private sweep(): void {\n const now = Date.now();\n for (const [key, entry] of this.nonces) {\n if (now - entry.createdAt > this.ttlMs) this.nonces.delete(key);\n }\n }\n}\n\nexport interface VerifiedSiwe {\n address: string;\n nonce: string;\n}\n\nexport async function verifySiweMessage(\n message: string,\n signature: string,\n): Promise<VerifiedSiwe> {\n const lines = message.split(\"\\n\");\n let claimed: string;\n try {\n claimed = getAddress((lines[1] ?? \"\").trim());\n } catch {\n throw new ServerAuthError(\"SIWE message is missing a valid address on line 2\");\n }\n\n const nonceMatch = message.match(/^Nonce: (.+)$/m);\n if (!nonceMatch || nonceMatch[1] === undefined) {\n throw new ServerAuthError(\"SIWE message is missing a Nonce line\");\n }\n const nonce = nonceMatch[1].trim();\n\n let recovered: string;\n try {\n recovered = await recoverMessageAddress({ message, signature: signature as Hex });\n } catch (error) {\n throw new ServerAuthError(\n `SIWE signature recovery failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n if (getAddress(recovered) !== claimed) {\n throw new ServerAuthError(\n `SIWE signature does not match message address (recovered ${recovered}, expected ${claimed})`,\n );\n }\n\n return { address: claimed, nonce };\n}\n\nexport interface SessionToken {\n token: string;\n expiresIn: number;\n}\n\nexport interface SessionClaims {\n sub: string;\n address: string;\n iat: number;\n exp: number;\n}\n\nexport function issueSessionToken(\n address: string,\n secret: string,\n ttlSeconds = DEFAULT_SESSION_TTL_SECONDS,\n): SessionToken {\n const normalized = getAddress(address);\n const now = Math.floor(Date.now() / 1000);\n const claims: SessionClaims = {\n sub: normalized,\n address: normalized,\n iat: now,\n exp: now + ttlSeconds,\n };\n return {\n token: signJwt({ alg: \"HS256\", typ: \"JWT\" }, claims, secret),\n expiresIn: ttlSeconds,\n };\n}\n\nexport function verifySessionToken(token: string, secret: string): { address: string } {\n const claims = verifyJwt(token, secret);\n if (typeof claims.sub !== \"string\" || claims.sub === \"\") {\n throw new ServerAuthError(\"session token missing 'sub' claim\");\n }\n return { address: claims.sub };\n}\n\nexport interface CreateSiweSessionOptions {\n jwtSecret: string;\n nonceStore?: NonceStore;\n sessionTtlSeconds?: number;\n}\n\nexport function createSiweSession(options: CreateSiweSessionOptions) {\n const nonceStore = options.nonceStore ?? new NonceStore();\n const sessionTtlSeconds = options.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS;\n\n return {\n issueNonce(address: string): string {\n return nonceStore.issue(address);\n },\n async verify(message: string, signature: string): Promise<SessionToken> {\n const verified = await verifySiweMessage(message, signature);\n if (!nonceStore.validate(verified.address, verified.nonce)) {\n throw new ServerAuthError(\"nonce is invalid, expired, or already used\");\n }\n return issueSessionToken(verified.address, options.jwtSecret, sessionTtlSeconds);\n },\n verifyToken(token: string): { address: string } {\n return verifySessionToken(token, options.jwtSecret);\n },\n };\n}\n\nfunction signJwt(header: object, payload: object, secret: string): string {\n const encodedHeader = base64UrlEncode(JSON.stringify(header));\n const encodedPayload = base64UrlEncode(JSON.stringify(payload));\n const signingInput = `${encodedHeader}.${encodedPayload}`;\n const signature = createHmac(\"sha256\", secret).update(signingInput).digest();\n return `${signingInput}.${base64UrlEncode(signature)}`;\n}\n\nfunction verifyJwt(token: string, secret: string): SessionClaims {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {\n throw new ServerAuthError(\"session token is not a valid JWT\");\n }\n\n const signingInput = `${parts[0]}.${parts[1]}`;\n const expected = createHmac(\"sha256\", secret).update(signingInput).digest();\n const actual = base64UrlDecode(parts[2]);\n if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {\n throw new ServerAuthError(\"session token signature verification failed\");\n }\n\n const claims = JSON.parse(base64UrlDecode(parts[1]).toString(\"utf8\")) as SessionClaims;\n if (typeof claims.exp !== \"number\" || claims.exp <= Math.floor(Date.now() / 1000)) {\n throw new ServerAuthError(\"session token expired\");\n }\n return claims;\n}\n\nfunction base64UrlEncode(value: string | Buffer): string {\n const buffer = typeof value === \"string\" ? Buffer.from(value, \"utf8\") : value;\n return buffer.toString(\"base64url\");\n}\n\nfunction base64UrlDecode(value: string): Buffer {\n return Buffer.from(value, \"base64url\");\n}\n"],"mappings":"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","names":["import_node_sdk","import_viem"]}
@@ -0,0 +1,139 @@
1
+ import { Hex } from 'viem';
2
+ import { Manifest, TinyCloudNodeConfig, TinyCloudNode, PortableDelegation, InlineEncryptedEnvelope, SecretScopeOptions } from '@tinycloud/node-sdk';
3
+
4
+ interface DstackKeyClient {
5
+ getKey(path: string, purpose: string): Promise<{
6
+ key: Uint8Array;
7
+ }>;
8
+ }
9
+ interface DeriveDstackPrivateKeyOptions {
10
+ client: DstackKeyClient;
11
+ path: string;
12
+ purpose: string;
13
+ }
14
+ declare function deriveDstackPrivateKey(options: DeriveDstackPrivateKeyOptions): Promise<Hex>;
15
+ declare function serverDidForPrivateKey(privateKey: string): string;
16
+ interface CreateServerIdentityOptions {
17
+ privateKey: string;
18
+ host?: string;
19
+ prefix?: string;
20
+ manifest?: Manifest | Manifest[];
21
+ autoCreateSpace?: boolean;
22
+ enablePublicSpace?: boolean;
23
+ includeAccountRegistryPermissions?: boolean;
24
+ nodeConfig?: Omit<TinyCloudNodeConfig, "privateKey" | "host" | "prefix" | "manifest" | "autoCreateSpace" | "enablePublicSpace" | "includeAccountRegistryPermissions">;
25
+ }
26
+ interface ServerIdentity {
27
+ node: TinyCloudNode;
28
+ did: string;
29
+ host: string;
30
+ privateKey: string;
31
+ }
32
+ declare function createServerIdentity(options: CreateServerIdentityOptions): Promise<ServerIdentity>;
33
+ declare function isTinyCloudSessionError(error: unknown): boolean;
34
+ declare function withSessionRefresh<T>(node: TinyCloudNode, fn: () => Promise<T>): Promise<T>;
35
+
36
+ type KvLike = {
37
+ get<T = unknown>(key: string, options: {
38
+ raw: true;
39
+ prefix: string;
40
+ }): Promise<{
41
+ ok: true;
42
+ data: T | unknown;
43
+ } | {
44
+ ok: false;
45
+ error?: {
46
+ message?: string;
47
+ };
48
+ }>;
49
+ };
50
+ type DelegatedAccessLike = {
51
+ kv: KvLike;
52
+ delegation: Pick<PortableDelegation, "cid">;
53
+ restorable?: {
54
+ delegationCid?: string;
55
+ };
56
+ };
57
+ type DecryptResult = {
58
+ ok: true;
59
+ data: Uint8Array;
60
+ } | {
61
+ ok: false;
62
+ error: {
63
+ code?: string;
64
+ message: string;
65
+ };
66
+ };
67
+ type EncryptionLike = {
68
+ decryptEnvelope(envelope: InlineEncryptedEnvelope, capabilityProof: {
69
+ proofs: string[];
70
+ }): Promise<DecryptResult>;
71
+ };
72
+ type TinyCloudNodeLike = {
73
+ signIn(): Promise<unknown>;
74
+ useDelegation(delegation: PortableDelegation): Promise<DelegatedAccessLike>;
75
+ encryption: EncryptionLike;
76
+ };
77
+ type DelegationInput = PortableDelegation | string;
78
+ interface ServerDelegateClient {
79
+ getSecret(name: string, options?: SecretScopeOptions): Promise<string>;
80
+ }
81
+ interface CreateServerDelegateClientOptions {
82
+ privateKey: string;
83
+ host?: string;
84
+ delegation: DelegationInput;
85
+ prefix?: string;
86
+ nodeConfig?: CreateServerIdentityOptions["nodeConfig"];
87
+ node?: TinyCloudNodeLike;
88
+ nodeFactory?: (options: CreateServerIdentityOptions) => Promise<TinyCloudNodeLike>;
89
+ }
90
+ declare function createServerDelegateClient(options: CreateServerDelegateClientOptions): ServerDelegateClient;
91
+ declare function readDelegatedSecret(node: TinyCloudNodeLike | TinyCloudNode, delegation: PortableDelegation, name: string, options?: SecretScopeOptions): Promise<string>;
92
+ declare function parseDelegation(delegation: DelegationInput): PortableDelegation;
93
+ declare function parseEncryptedEnvelope(rawEnvelope: unknown, name?: string): InlineEncryptedEnvelope;
94
+ declare function parseSecretPayload(plaintext: Uint8Array, name?: string): string;
95
+
96
+ declare class ServerAuthError extends Error {
97
+ }
98
+ declare class NonceStore {
99
+ private readonly ttlMs;
100
+ private readonly nonces;
101
+ constructor(ttlMs?: number);
102
+ issue(address: string): string;
103
+ validate(address: string, nonce: string): boolean;
104
+ private key;
105
+ private sweep;
106
+ }
107
+ interface VerifiedSiwe {
108
+ address: string;
109
+ nonce: string;
110
+ }
111
+ declare function verifySiweMessage(message: string, signature: string): Promise<VerifiedSiwe>;
112
+ interface SessionToken {
113
+ token: string;
114
+ expiresIn: number;
115
+ }
116
+ interface SessionClaims {
117
+ sub: string;
118
+ address: string;
119
+ iat: number;
120
+ exp: number;
121
+ }
122
+ declare function issueSessionToken(address: string, secret: string, ttlSeconds?: number): SessionToken;
123
+ declare function verifySessionToken(token: string, secret: string): {
124
+ address: string;
125
+ };
126
+ interface CreateSiweSessionOptions {
127
+ jwtSecret: string;
128
+ nonceStore?: NonceStore;
129
+ sessionTtlSeconds?: number;
130
+ }
131
+ declare function createSiweSession(options: CreateSiweSessionOptions): {
132
+ issueNonce(address: string): string;
133
+ verify(message: string, signature: string): Promise<SessionToken>;
134
+ verifyToken(token: string): {
135
+ address: string;
136
+ };
137
+ };
138
+
139
+ export { type CreateServerDelegateClientOptions, type CreateServerIdentityOptions, type CreateSiweSessionOptions, type DelegationInput, type DeriveDstackPrivateKeyOptions, type DstackKeyClient, NonceStore, ServerAuthError, type ServerDelegateClient, type ServerIdentity, type SessionClaims, type SessionToken, type VerifiedSiwe, createServerDelegateClient, createServerIdentity, createSiweSession, deriveDstackPrivateKey, isTinyCloudSessionError, issueSessionToken, parseDelegation, parseEncryptedEnvelope, parseSecretPayload, readDelegatedSecret, serverDidForPrivateKey, verifySessionToken, verifySiweMessage, withSessionRefresh };
@@ -0,0 +1,139 @@
1
+ import { Hex } from 'viem';
2
+ import { Manifest, TinyCloudNodeConfig, TinyCloudNode, PortableDelegation, InlineEncryptedEnvelope, SecretScopeOptions } from '@tinycloud/node-sdk';
3
+
4
+ interface DstackKeyClient {
5
+ getKey(path: string, purpose: string): Promise<{
6
+ key: Uint8Array;
7
+ }>;
8
+ }
9
+ interface DeriveDstackPrivateKeyOptions {
10
+ client: DstackKeyClient;
11
+ path: string;
12
+ purpose: string;
13
+ }
14
+ declare function deriveDstackPrivateKey(options: DeriveDstackPrivateKeyOptions): Promise<Hex>;
15
+ declare function serverDidForPrivateKey(privateKey: string): string;
16
+ interface CreateServerIdentityOptions {
17
+ privateKey: string;
18
+ host?: string;
19
+ prefix?: string;
20
+ manifest?: Manifest | Manifest[];
21
+ autoCreateSpace?: boolean;
22
+ enablePublicSpace?: boolean;
23
+ includeAccountRegistryPermissions?: boolean;
24
+ nodeConfig?: Omit<TinyCloudNodeConfig, "privateKey" | "host" | "prefix" | "manifest" | "autoCreateSpace" | "enablePublicSpace" | "includeAccountRegistryPermissions">;
25
+ }
26
+ interface ServerIdentity {
27
+ node: TinyCloudNode;
28
+ did: string;
29
+ host: string;
30
+ privateKey: string;
31
+ }
32
+ declare function createServerIdentity(options: CreateServerIdentityOptions): Promise<ServerIdentity>;
33
+ declare function isTinyCloudSessionError(error: unknown): boolean;
34
+ declare function withSessionRefresh<T>(node: TinyCloudNode, fn: () => Promise<T>): Promise<T>;
35
+
36
+ type KvLike = {
37
+ get<T = unknown>(key: string, options: {
38
+ raw: true;
39
+ prefix: string;
40
+ }): Promise<{
41
+ ok: true;
42
+ data: T | unknown;
43
+ } | {
44
+ ok: false;
45
+ error?: {
46
+ message?: string;
47
+ };
48
+ }>;
49
+ };
50
+ type DelegatedAccessLike = {
51
+ kv: KvLike;
52
+ delegation: Pick<PortableDelegation, "cid">;
53
+ restorable?: {
54
+ delegationCid?: string;
55
+ };
56
+ };
57
+ type DecryptResult = {
58
+ ok: true;
59
+ data: Uint8Array;
60
+ } | {
61
+ ok: false;
62
+ error: {
63
+ code?: string;
64
+ message: string;
65
+ };
66
+ };
67
+ type EncryptionLike = {
68
+ decryptEnvelope(envelope: InlineEncryptedEnvelope, capabilityProof: {
69
+ proofs: string[];
70
+ }): Promise<DecryptResult>;
71
+ };
72
+ type TinyCloudNodeLike = {
73
+ signIn(): Promise<unknown>;
74
+ useDelegation(delegation: PortableDelegation): Promise<DelegatedAccessLike>;
75
+ encryption: EncryptionLike;
76
+ };
77
+ type DelegationInput = PortableDelegation | string;
78
+ interface ServerDelegateClient {
79
+ getSecret(name: string, options?: SecretScopeOptions): Promise<string>;
80
+ }
81
+ interface CreateServerDelegateClientOptions {
82
+ privateKey: string;
83
+ host?: string;
84
+ delegation: DelegationInput;
85
+ prefix?: string;
86
+ nodeConfig?: CreateServerIdentityOptions["nodeConfig"];
87
+ node?: TinyCloudNodeLike;
88
+ nodeFactory?: (options: CreateServerIdentityOptions) => Promise<TinyCloudNodeLike>;
89
+ }
90
+ declare function createServerDelegateClient(options: CreateServerDelegateClientOptions): ServerDelegateClient;
91
+ declare function readDelegatedSecret(node: TinyCloudNodeLike | TinyCloudNode, delegation: PortableDelegation, name: string, options?: SecretScopeOptions): Promise<string>;
92
+ declare function parseDelegation(delegation: DelegationInput): PortableDelegation;
93
+ declare function parseEncryptedEnvelope(rawEnvelope: unknown, name?: string): InlineEncryptedEnvelope;
94
+ declare function parseSecretPayload(plaintext: Uint8Array, name?: string): string;
95
+
96
+ declare class ServerAuthError extends Error {
97
+ }
98
+ declare class NonceStore {
99
+ private readonly ttlMs;
100
+ private readonly nonces;
101
+ constructor(ttlMs?: number);
102
+ issue(address: string): string;
103
+ validate(address: string, nonce: string): boolean;
104
+ private key;
105
+ private sweep;
106
+ }
107
+ interface VerifiedSiwe {
108
+ address: string;
109
+ nonce: string;
110
+ }
111
+ declare function verifySiweMessage(message: string, signature: string): Promise<VerifiedSiwe>;
112
+ interface SessionToken {
113
+ token: string;
114
+ expiresIn: number;
115
+ }
116
+ interface SessionClaims {
117
+ sub: string;
118
+ address: string;
119
+ iat: number;
120
+ exp: number;
121
+ }
122
+ declare function issueSessionToken(address: string, secret: string, ttlSeconds?: number): SessionToken;
123
+ declare function verifySessionToken(token: string, secret: string): {
124
+ address: string;
125
+ };
126
+ interface CreateSiweSessionOptions {
127
+ jwtSecret: string;
128
+ nonceStore?: NonceStore;
129
+ sessionTtlSeconds?: number;
130
+ }
131
+ declare function createSiweSession(options: CreateSiweSessionOptions): {
132
+ issueNonce(address: string): string;
133
+ verify(message: string, signature: string): Promise<SessionToken>;
134
+ verifyToken(token: string): {
135
+ address: string;
136
+ };
137
+ };
138
+
139
+ export { type CreateServerDelegateClientOptions, type CreateServerIdentityOptions, type CreateSiweSessionOptions, type DelegationInput, type DeriveDstackPrivateKeyOptions, type DstackKeyClient, NonceStore, ServerAuthError, type ServerDelegateClient, type ServerIdentity, type SessionClaims, type SessionToken, type VerifiedSiwe, createServerDelegateClient, createServerIdentity, createSiweSession, deriveDstackPrivateKey, isTinyCloudSessionError, issueSessionToken, parseDelegation, parseEncryptedEnvelope, parseSecretPayload, readDelegatedSecret, serverDidForPrivateKey, verifySessionToken, verifySiweMessage, withSessionRefresh };
package/dist/index.js ADDED
@@ -0,0 +1,293 @@
1
+ // src/identity.ts
2
+ import { keccak256 } from "viem";
3
+ import { privateKeyToAccount } from "viem/accounts";
4
+ import { TinyCloudNode } from "@tinycloud/node-sdk";
5
+ var DEFAULT_HOST = "https://node.tinycloud.xyz";
6
+ async function deriveDstackPrivateKey(options) {
7
+ const res = await options.client.getKey(options.path, options.purpose);
8
+ if (!(res.key instanceof Uint8Array) || res.key.length === 0) {
9
+ throw new Error("dstack getKey returned no key material");
10
+ }
11
+ return keccak256(res.key);
12
+ }
13
+ function serverDidForPrivateKey(privateKey) {
14
+ const account = privateKeyToAccount(privateKey);
15
+ return `did:pkh:eip155:1:${account.address}`;
16
+ }
17
+ async function createServerIdentity(options) {
18
+ const host = options.host ?? DEFAULT_HOST;
19
+ const node = new TinyCloudNode({
20
+ ...options.nodeConfig,
21
+ privateKey: options.privateKey,
22
+ host,
23
+ prefix: options.prefix,
24
+ manifest: options.manifest,
25
+ autoCreateSpace: options.autoCreateSpace ?? false,
26
+ enablePublicSpace: options.enablePublicSpace ?? false,
27
+ includeAccountRegistryPermissions: options.includeAccountRegistryPermissions ?? false
28
+ });
29
+ await node.signIn();
30
+ return {
31
+ node,
32
+ did: node.did,
33
+ host,
34
+ privateKey: options.privateKey
35
+ };
36
+ }
37
+ var SESSION_ERROR_PATTERN = /\b(session\s+expired|invalid\s+session|token\s+expired|expired\s+credentials?|unauthorized|unauthenticated|sign.?in\s*required)\b|\b401\b(?![\d-])/i;
38
+ function isTinyCloudSessionError(error) {
39
+ const message = error instanceof Error ? error.message : String(error);
40
+ return SESSION_ERROR_PATTERN.test(message);
41
+ }
42
+ async function withSessionRefresh(node, fn) {
43
+ try {
44
+ return await fn();
45
+ } catch (error) {
46
+ if (isTinyCloudSessionError(error)) {
47
+ await node.signIn();
48
+ return fn();
49
+ }
50
+ throw error;
51
+ }
52
+ }
53
+
54
+ // src/delegated-secrets.ts
55
+ import {
56
+ deserializeDelegation
57
+ } from "@tinycloud/node-sdk";
58
+ import { resolveSecretPath } from "@tinycloud/sdk-core";
59
+ function createServerDelegateClient(options) {
60
+ const delegation = parseDelegation(options.delegation);
61
+ let nodePromise;
62
+ async function getNode() {
63
+ if (!nodePromise) {
64
+ if (options.node) {
65
+ nodePromise = Promise.resolve(options.node);
66
+ return options.node;
67
+ }
68
+ const identityOptions = {
69
+ privateKey: options.privateKey,
70
+ host: options.host,
71
+ prefix: options.prefix,
72
+ enablePublicSpace: false,
73
+ includeAccountRegistryPermissions: false,
74
+ nodeConfig: options.nodeConfig
75
+ };
76
+ const created = options.nodeFactory ? options.nodeFactory(identityOptions) : createServerIdentity(identityOptions).then(
77
+ (identity) => identity.node
78
+ );
79
+ nodePromise = created;
80
+ return created;
81
+ }
82
+ return nodePromise;
83
+ }
84
+ return {
85
+ async getSecret(name, secretOptions) {
86
+ const node = await getNode();
87
+ return readDelegatedSecret(node, delegation, name, secretOptions);
88
+ }
89
+ };
90
+ }
91
+ async function readDelegatedSecret(node, delegation, name, options) {
92
+ const secretKey = resolveSecretPath(name, options).permissionPaths.vault;
93
+ const access = await node.useDelegation(delegation);
94
+ const result = await access.kv.get(secretKey, { raw: true, prefix: "" });
95
+ if (!result.ok) {
96
+ const message = result.error?.message ?? `failed to read ${secretKey}`;
97
+ throw new Error(`delegated secret ${name} KV get failed: ${message}`);
98
+ }
99
+ const envelope = parseEncryptedEnvelope(
100
+ result.data?.data,
101
+ name
102
+ );
103
+ const proofCid = access.restorable?.delegationCid ?? access.delegation.cid;
104
+ if (!proofCid) {
105
+ throw new Error(`delegated secret ${name} has no decrypt proof`);
106
+ }
107
+ const decrypted = await node.encryption.decryptEnvelope(envelope, { proofs: [proofCid] });
108
+ if (!decrypted.ok) {
109
+ throw new Error(`delegated secret ${name} decrypt failed: ${decrypted.error.message}`);
110
+ }
111
+ return parseSecretPayload(decrypted.data, name);
112
+ }
113
+ function parseDelegation(delegation) {
114
+ return typeof delegation === "string" ? deserializeDelegation(delegation) : delegation;
115
+ }
116
+ function parseEncryptedEnvelope(rawEnvelope, name = "secret") {
117
+ const parsed = typeof rawEnvelope === "string" ? JSON.parse(rawEnvelope) : rawEnvelope;
118
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.v !== "number" || typeof parsed.networkId !== "string" || typeof parsed.ciphertext !== "string" || typeof parsed.encryptedSymmetricKey !== "string") {
119
+ throw new Error(`delegated secret ${name} did not contain an encrypted envelope`);
120
+ }
121
+ return parsed;
122
+ }
123
+ function parseSecretPayload(plaintext, name = "secret") {
124
+ let parsed;
125
+ try {
126
+ parsed = JSON.parse(new TextDecoder().decode(plaintext));
127
+ } catch {
128
+ throw new Error(`delegated secret ${name} did not contain valid JSON`);
129
+ }
130
+ if (typeof parsed.value !== "string") {
131
+ throw new Error(`delegated secret ${name} did not contain a string value`);
132
+ }
133
+ return parsed.value;
134
+ }
135
+
136
+ // src/siwe-session.ts
137
+ import { createHmac, randomBytes, timingSafeEqual } from "crypto";
138
+ import { getAddress, recoverMessageAddress } from "viem";
139
+ var DEFAULT_NONCE_TTL_MS = 5 * 60 * 1e3;
140
+ var DEFAULT_SESSION_TTL_SECONDS = 24 * 60 * 60;
141
+ var ServerAuthError = class extends Error {
142
+ };
143
+ var NonceStore = class {
144
+ constructor(ttlMs = DEFAULT_NONCE_TTL_MS) {
145
+ this.ttlMs = ttlMs;
146
+ }
147
+ nonces = /* @__PURE__ */ new Map();
148
+ issue(address) {
149
+ this.sweep();
150
+ const normalized = getAddress(address).toLowerCase();
151
+ const nonce = randomBytes(16).toString("hex");
152
+ this.nonces.set(this.key(normalized, nonce), {
153
+ address: normalized,
154
+ createdAt: Date.now()
155
+ });
156
+ return nonce;
157
+ }
158
+ validate(address, nonce) {
159
+ const normalized = getAddress(address).toLowerCase();
160
+ const key = this.key(normalized, nonce);
161
+ const entry = this.nonces.get(key);
162
+ if (!entry) return false;
163
+ this.nonces.delete(key);
164
+ return Date.now() - entry.createdAt <= this.ttlMs;
165
+ }
166
+ key(address, nonce) {
167
+ return `${address}:${nonce}`;
168
+ }
169
+ sweep() {
170
+ const now = Date.now();
171
+ for (const [key, entry] of this.nonces) {
172
+ if (now - entry.createdAt > this.ttlMs) this.nonces.delete(key);
173
+ }
174
+ }
175
+ };
176
+ async function verifySiweMessage(message, signature) {
177
+ const lines = message.split("\n");
178
+ let claimed;
179
+ try {
180
+ claimed = getAddress((lines[1] ?? "").trim());
181
+ } catch {
182
+ throw new ServerAuthError("SIWE message is missing a valid address on line 2");
183
+ }
184
+ const nonceMatch = message.match(/^Nonce: (.+)$/m);
185
+ if (!nonceMatch || nonceMatch[1] === void 0) {
186
+ throw new ServerAuthError("SIWE message is missing a Nonce line");
187
+ }
188
+ const nonce = nonceMatch[1].trim();
189
+ let recovered;
190
+ try {
191
+ recovered = await recoverMessageAddress({ message, signature });
192
+ } catch (error) {
193
+ throw new ServerAuthError(
194
+ `SIWE signature recovery failed: ${error instanceof Error ? error.message : String(error)}`
195
+ );
196
+ }
197
+ if (getAddress(recovered) !== claimed) {
198
+ throw new ServerAuthError(
199
+ `SIWE signature does not match message address (recovered ${recovered}, expected ${claimed})`
200
+ );
201
+ }
202
+ return { address: claimed, nonce };
203
+ }
204
+ function issueSessionToken(address, secret, ttlSeconds = DEFAULT_SESSION_TTL_SECONDS) {
205
+ const normalized = getAddress(address);
206
+ const now = Math.floor(Date.now() / 1e3);
207
+ const claims = {
208
+ sub: normalized,
209
+ address: normalized,
210
+ iat: now,
211
+ exp: now + ttlSeconds
212
+ };
213
+ return {
214
+ token: signJwt({ alg: "HS256", typ: "JWT" }, claims, secret),
215
+ expiresIn: ttlSeconds
216
+ };
217
+ }
218
+ function verifySessionToken(token, secret) {
219
+ const claims = verifyJwt(token, secret);
220
+ if (typeof claims.sub !== "string" || claims.sub === "") {
221
+ throw new ServerAuthError("session token missing 'sub' claim");
222
+ }
223
+ return { address: claims.sub };
224
+ }
225
+ function createSiweSession(options) {
226
+ const nonceStore = options.nonceStore ?? new NonceStore();
227
+ const sessionTtlSeconds = options.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS;
228
+ return {
229
+ issueNonce(address) {
230
+ return nonceStore.issue(address);
231
+ },
232
+ async verify(message, signature) {
233
+ const verified = await verifySiweMessage(message, signature);
234
+ if (!nonceStore.validate(verified.address, verified.nonce)) {
235
+ throw new ServerAuthError("nonce is invalid, expired, or already used");
236
+ }
237
+ return issueSessionToken(verified.address, options.jwtSecret, sessionTtlSeconds);
238
+ },
239
+ verifyToken(token) {
240
+ return verifySessionToken(token, options.jwtSecret);
241
+ }
242
+ };
243
+ }
244
+ function signJwt(header, payload, secret) {
245
+ const encodedHeader = base64UrlEncode(JSON.stringify(header));
246
+ const encodedPayload = base64UrlEncode(JSON.stringify(payload));
247
+ const signingInput = `${encodedHeader}.${encodedPayload}`;
248
+ const signature = createHmac("sha256", secret).update(signingInput).digest();
249
+ return `${signingInput}.${base64UrlEncode(signature)}`;
250
+ }
251
+ function verifyJwt(token, secret) {
252
+ const parts = token.split(".");
253
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {
254
+ throw new ServerAuthError("session token is not a valid JWT");
255
+ }
256
+ const signingInput = `${parts[0]}.${parts[1]}`;
257
+ const expected = createHmac("sha256", secret).update(signingInput).digest();
258
+ const actual = base64UrlDecode(parts[2]);
259
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
260
+ throw new ServerAuthError("session token signature verification failed");
261
+ }
262
+ const claims = JSON.parse(base64UrlDecode(parts[1]).toString("utf8"));
263
+ if (typeof claims.exp !== "number" || claims.exp <= Math.floor(Date.now() / 1e3)) {
264
+ throw new ServerAuthError("session token expired");
265
+ }
266
+ return claims;
267
+ }
268
+ function base64UrlEncode(value) {
269
+ const buffer = typeof value === "string" ? Buffer.from(value, "utf8") : value;
270
+ return buffer.toString("base64url");
271
+ }
272
+ function base64UrlDecode(value) {
273
+ return Buffer.from(value, "base64url");
274
+ }
275
+ export {
276
+ NonceStore,
277
+ ServerAuthError,
278
+ createServerDelegateClient,
279
+ createServerIdentity,
280
+ createSiweSession,
281
+ deriveDstackPrivateKey,
282
+ isTinyCloudSessionError,
283
+ issueSessionToken,
284
+ parseDelegation,
285
+ parseEncryptedEnvelope,
286
+ parseSecretPayload,
287
+ readDelegatedSecret,
288
+ serverDidForPrivateKey,
289
+ verifySessionToken,
290
+ verifySiweMessage,
291
+ withSessionRefresh
292
+ };
293
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/identity.ts","../src/delegated-secrets.ts","../src/siwe-session.ts"],"sourcesContent":["import { keccak256, type Hex } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { TinyCloudNode, type Manifest, type TinyCloudNodeConfig } from \"@tinycloud/node-sdk\";\n\nconst DEFAULT_HOST = \"https://node.tinycloud.xyz\";\n\nexport interface DstackKeyClient {\n getKey(path: string, purpose: string): Promise<{ key: Uint8Array }>;\n}\n\nexport interface DeriveDstackPrivateKeyOptions {\n client: DstackKeyClient;\n path: string;\n purpose: string;\n}\n\nexport async function deriveDstackPrivateKey(\n options: DeriveDstackPrivateKeyOptions,\n): Promise<Hex> {\n const res = await options.client.getKey(options.path, options.purpose);\n if (!(res.key instanceof Uint8Array) || res.key.length === 0) {\n throw new Error(\"dstack getKey returned no key material\");\n }\n return keccak256(res.key);\n}\n\nexport function serverDidForPrivateKey(privateKey: string): string {\n const account = privateKeyToAccount(privateKey as Hex);\n return `did:pkh:eip155:1:${account.address}`;\n}\n\nexport interface CreateServerIdentityOptions {\n privateKey: string;\n host?: string;\n prefix?: string;\n manifest?: Manifest | Manifest[];\n autoCreateSpace?: boolean;\n enablePublicSpace?: boolean;\n includeAccountRegistryPermissions?: boolean;\n nodeConfig?: Omit<\n TinyCloudNodeConfig,\n | \"privateKey\"\n | \"host\"\n | \"prefix\"\n | \"manifest\"\n | \"autoCreateSpace\"\n | \"enablePublicSpace\"\n | \"includeAccountRegistryPermissions\"\n >;\n}\n\nexport interface ServerIdentity {\n node: TinyCloudNode;\n did: string;\n host: string;\n privateKey: string;\n}\n\nexport async function createServerIdentity(\n options: CreateServerIdentityOptions,\n): Promise<ServerIdentity> {\n const host = options.host ?? DEFAULT_HOST;\n const node = new TinyCloudNode({\n ...options.nodeConfig,\n privateKey: options.privateKey,\n host,\n prefix: options.prefix,\n manifest: options.manifest,\n autoCreateSpace: options.autoCreateSpace ?? false,\n enablePublicSpace: options.enablePublicSpace ?? false,\n includeAccountRegistryPermissions: options.includeAccountRegistryPermissions ?? false,\n });\n\n await node.signIn();\n\n return {\n node,\n did: node.did,\n host,\n privateKey: options.privateKey,\n };\n}\n\nconst SESSION_ERROR_PATTERN =\n /\\b(session\\s+expired|invalid\\s+session|token\\s+expired|expired\\s+credentials?|unauthorized|unauthenticated|sign.?in\\s*required)\\b|\\b401\\b(?![\\d-])/i;\n\nexport function isTinyCloudSessionError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return SESSION_ERROR_PATTERN.test(message);\n}\n\nexport async function withSessionRefresh<T>(\n node: TinyCloudNode,\n fn: () => Promise<T>,\n): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (isTinyCloudSessionError(error)) {\n await node.signIn();\n return fn();\n }\n throw error;\n }\n}\n","import {\n TinyCloudNode,\n deserializeDelegation,\n type InlineEncryptedEnvelope,\n type PortableDelegation,\n type SecretScopeOptions,\n} from \"@tinycloud/node-sdk\";\nimport { resolveSecretPath } from \"@tinycloud/sdk-core\";\nimport type { CreateServerIdentityOptions } from \"./identity.js\";\nimport { createServerIdentity } from \"./identity.js\";\n\ntype KvLike = {\n get<T = unknown>(\n key: string,\n options: { raw: true; prefix: string },\n ): Promise<{ ok: true; data: T | unknown } | { ok: false; error?: { message?: string } }>;\n};\n\ntype DelegatedAccessLike = {\n kv: KvLike;\n delegation: Pick<PortableDelegation, \"cid\">;\n restorable?: { delegationCid?: string };\n};\n\ntype DecryptResult =\n | { ok: true; data: Uint8Array }\n | { ok: false; error: { code?: string; message: string } };\n\ntype EncryptionLike = {\n decryptEnvelope(\n envelope: InlineEncryptedEnvelope,\n capabilityProof: { proofs: string[] },\n ): Promise<DecryptResult>;\n};\n\ntype TinyCloudNodeLike = {\n signIn(): Promise<unknown>;\n useDelegation(delegation: PortableDelegation): Promise<DelegatedAccessLike>;\n encryption: EncryptionLike;\n};\n\nexport type DelegationInput = PortableDelegation | string;\n\nexport interface ServerDelegateClient {\n getSecret(name: string, options?: SecretScopeOptions): Promise<string>;\n}\n\nexport interface CreateServerDelegateClientOptions {\n privateKey: string;\n host?: string;\n delegation: DelegationInput;\n prefix?: string;\n nodeConfig?: CreateServerIdentityOptions[\"nodeConfig\"];\n node?: TinyCloudNodeLike;\n nodeFactory?: (options: CreateServerIdentityOptions) => Promise<TinyCloudNodeLike>;\n}\n\nexport function createServerDelegateClient(\n options: CreateServerDelegateClientOptions,\n): ServerDelegateClient {\n const delegation = parseDelegation(options.delegation);\n let nodePromise: Promise<TinyCloudNodeLike> | undefined;\n\n async function getNode(): Promise<TinyCloudNodeLike> {\n if (!nodePromise) {\n if (options.node) {\n nodePromise = Promise.resolve(options.node);\n return options.node;\n }\n const identityOptions: CreateServerIdentityOptions = {\n privateKey: options.privateKey,\n host: options.host,\n prefix: options.prefix,\n enablePublicSpace: false,\n includeAccountRegistryPermissions: false,\n nodeConfig: options.nodeConfig,\n };\n const created = options.nodeFactory\n ? options.nodeFactory(identityOptions)\n : createServerIdentity(identityOptions).then(\n (identity) => identity.node as unknown as TinyCloudNodeLike,\n );\n nodePromise = created;\n return created;\n }\n return nodePromise;\n }\n\n return {\n async getSecret(name: string, secretOptions?: SecretScopeOptions): Promise<string> {\n const node = await getNode();\n return readDelegatedSecret(node, delegation, name, secretOptions);\n },\n };\n}\n\nexport async function readDelegatedSecret(\n node: TinyCloudNodeLike | TinyCloudNode,\n delegation: PortableDelegation,\n name: string,\n options?: SecretScopeOptions,\n): Promise<string> {\n const secretKey = resolveSecretPath(name, options).permissionPaths.vault;\n\n const access = await node.useDelegation(delegation);\n const result = await access.kv.get<unknown>(secretKey, { raw: true, prefix: \"\" });\n if (!result.ok) {\n const message = result.error?.message ?? `failed to read ${secretKey}`;\n throw new Error(`delegated secret ${name} KV get failed: ${message}`);\n }\n\n const envelope = parseEncryptedEnvelope(\n (result.data as { data?: unknown } | undefined)?.data,\n name,\n );\n const proofCid = access.restorable?.delegationCid ?? access.delegation.cid;\n if (!proofCid) {\n throw new Error(`delegated secret ${name} has no decrypt proof`);\n }\n\n const decrypted = await node.encryption.decryptEnvelope(envelope, { proofs: [proofCid] });\n if (!decrypted.ok) {\n throw new Error(`delegated secret ${name} decrypt failed: ${decrypted.error.message}`);\n }\n\n return parseSecretPayload(decrypted.data, name);\n}\n\nexport function parseDelegation(delegation: DelegationInput): PortableDelegation {\n return typeof delegation === \"string\" ? deserializeDelegation(delegation) : delegation;\n}\n\nexport function parseEncryptedEnvelope(\n rawEnvelope: unknown,\n name = \"secret\",\n): InlineEncryptedEnvelope {\n const parsed = typeof rawEnvelope === \"string\" ? JSON.parse(rawEnvelope) : rawEnvelope;\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as Partial<InlineEncryptedEnvelope>).v !== \"number\" ||\n typeof (parsed as Partial<InlineEncryptedEnvelope>).networkId !== \"string\" ||\n typeof (parsed as Partial<InlineEncryptedEnvelope>).ciphertext !== \"string\" ||\n typeof (parsed as Partial<InlineEncryptedEnvelope>).encryptedSymmetricKey !== \"string\"\n ) {\n throw new Error(`delegated secret ${name} did not contain an encrypted envelope`);\n }\n return parsed as InlineEncryptedEnvelope;\n}\n\nexport function parseSecretPayload(plaintext: Uint8Array, name = \"secret\"): string {\n let parsed: { value?: unknown };\n try {\n parsed = JSON.parse(new TextDecoder().decode(plaintext)) as { value?: unknown };\n } catch {\n throw new Error(`delegated secret ${name} did not contain valid JSON`);\n }\n if (typeof parsed.value !== \"string\") {\n throw new Error(`delegated secret ${name} did not contain a string value`);\n }\n return parsed.value;\n}\n","import { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport { getAddress, recoverMessageAddress, type Hex } from \"viem\";\n\nconst DEFAULT_NONCE_TTL_MS = 5 * 60 * 1000;\nconst DEFAULT_SESSION_TTL_SECONDS = 24 * 60 * 60;\n\ninterface NonceEntry {\n address: string;\n createdAt: number;\n}\n\nexport class ServerAuthError extends Error {}\n\nexport class NonceStore {\n private readonly nonces = new Map<string, NonceEntry>();\n\n constructor(private readonly ttlMs = DEFAULT_NONCE_TTL_MS) {}\n\n issue(address: string): string {\n this.sweep();\n const normalized = getAddress(address).toLowerCase();\n const nonce = randomBytes(16).toString(\"hex\");\n this.nonces.set(this.key(normalized, nonce), {\n address: normalized,\n createdAt: Date.now(),\n });\n return nonce;\n }\n\n validate(address: string, nonce: string): boolean {\n const normalized = getAddress(address).toLowerCase();\n const key = this.key(normalized, nonce);\n const entry = this.nonces.get(key);\n if (!entry) return false;\n this.nonces.delete(key);\n return Date.now() - entry.createdAt <= this.ttlMs;\n }\n\n private key(address: string, nonce: string): string {\n return `${address}:${nonce}`;\n }\n\n private sweep(): void {\n const now = Date.now();\n for (const [key, entry] of this.nonces) {\n if (now - entry.createdAt > this.ttlMs) this.nonces.delete(key);\n }\n }\n}\n\nexport interface VerifiedSiwe {\n address: string;\n nonce: string;\n}\n\nexport async function verifySiweMessage(\n message: string,\n signature: string,\n): Promise<VerifiedSiwe> {\n const lines = message.split(\"\\n\");\n let claimed: string;\n try {\n claimed = getAddress((lines[1] ?? \"\").trim());\n } catch {\n throw new ServerAuthError(\"SIWE message is missing a valid address on line 2\");\n }\n\n const nonceMatch = message.match(/^Nonce: (.+)$/m);\n if (!nonceMatch || nonceMatch[1] === undefined) {\n throw new ServerAuthError(\"SIWE message is missing a Nonce line\");\n }\n const nonce = nonceMatch[1].trim();\n\n let recovered: string;\n try {\n recovered = await recoverMessageAddress({ message, signature: signature as Hex });\n } catch (error) {\n throw new ServerAuthError(\n `SIWE signature recovery failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n if (getAddress(recovered) !== claimed) {\n throw new ServerAuthError(\n `SIWE signature does not match message address (recovered ${recovered}, expected ${claimed})`,\n );\n }\n\n return { address: claimed, nonce };\n}\n\nexport interface SessionToken {\n token: string;\n expiresIn: number;\n}\n\nexport interface SessionClaims {\n sub: string;\n address: string;\n iat: number;\n exp: number;\n}\n\nexport function issueSessionToken(\n address: string,\n secret: string,\n ttlSeconds = DEFAULT_SESSION_TTL_SECONDS,\n): SessionToken {\n const normalized = getAddress(address);\n const now = Math.floor(Date.now() / 1000);\n const claims: SessionClaims = {\n sub: normalized,\n address: normalized,\n iat: now,\n exp: now + ttlSeconds,\n };\n return {\n token: signJwt({ alg: \"HS256\", typ: \"JWT\" }, claims, secret),\n expiresIn: ttlSeconds,\n };\n}\n\nexport function verifySessionToken(token: string, secret: string): { address: string } {\n const claims = verifyJwt(token, secret);\n if (typeof claims.sub !== \"string\" || claims.sub === \"\") {\n throw new ServerAuthError(\"session token missing 'sub' claim\");\n }\n return { address: claims.sub };\n}\n\nexport interface CreateSiweSessionOptions {\n jwtSecret: string;\n nonceStore?: NonceStore;\n sessionTtlSeconds?: number;\n}\n\nexport function createSiweSession(options: CreateSiweSessionOptions) {\n const nonceStore = options.nonceStore ?? new NonceStore();\n const sessionTtlSeconds = options.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS;\n\n return {\n issueNonce(address: string): string {\n return nonceStore.issue(address);\n },\n async verify(message: string, signature: string): Promise<SessionToken> {\n const verified = await verifySiweMessage(message, signature);\n if (!nonceStore.validate(verified.address, verified.nonce)) {\n throw new ServerAuthError(\"nonce is invalid, expired, or already used\");\n }\n return issueSessionToken(verified.address, options.jwtSecret, sessionTtlSeconds);\n },\n verifyToken(token: string): { address: string } {\n return verifySessionToken(token, options.jwtSecret);\n },\n };\n}\n\nfunction signJwt(header: object, payload: object, secret: string): string {\n const encodedHeader = base64UrlEncode(JSON.stringify(header));\n const encodedPayload = base64UrlEncode(JSON.stringify(payload));\n const signingInput = `${encodedHeader}.${encodedPayload}`;\n const signature = createHmac(\"sha256\", secret).update(signingInput).digest();\n return `${signingInput}.${base64UrlEncode(signature)}`;\n}\n\nfunction verifyJwt(token: string, secret: string): SessionClaims {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {\n throw new ServerAuthError(\"session token is not a valid JWT\");\n }\n\n const signingInput = `${parts[0]}.${parts[1]}`;\n const expected = createHmac(\"sha256\", secret).update(signingInput).digest();\n const actual = base64UrlDecode(parts[2]);\n if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {\n throw new ServerAuthError(\"session token signature verification failed\");\n }\n\n const claims = JSON.parse(base64UrlDecode(parts[1]).toString(\"utf8\")) as SessionClaims;\n if (typeof claims.exp !== \"number\" || claims.exp <= Math.floor(Date.now() / 1000)) {\n throw new ServerAuthError(\"session token expired\");\n }\n return claims;\n}\n\nfunction base64UrlEncode(value: string | Buffer): string {\n const buffer = typeof value === \"string\" ? Buffer.from(value, \"utf8\") : value;\n return buffer.toString(\"base64url\");\n}\n\nfunction base64UrlDecode(value: string): Buffer {\n return Buffer.from(value, \"base64url\");\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@tinycloud/server",
3
+ "version": "2.4.0-beta.20",
4
+ "description": "Reusable TinyCloud server and agent integration helpers",
5
+ "author": "TinyCloud, Inc.",
6
+ "license": "EGPL",
7
+ "type": "module",
8
+ "main": "dist/index.cjs",
9
+ "module": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "clean": "rm -rf dist",
28
+ "test": "bun test src/"
29
+ },
30
+ "dependencies": {
31
+ "@tinycloud/node-sdk": "2.4.0-beta.19",
32
+ "@tinycloud/sdk-core": "2.4.0-beta.19",
33
+ "viem": "^2.21.53"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20",
37
+ "tsup": "^8.0.0",
38
+ "typescript": "^5.9.3"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/tinycloudlabs/web-sdk"
43
+ }
44
+ }