@ariestools/aries-datalake-core 0.1.13
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 +12 -0
- package/dist/browser/api/errors.d.ts +24 -0
- package/dist/browser/api/errors.d.ts.map +1 -0
- package/dist/browser/api/health.d.ts +12 -0
- package/dist/browser/api/health.d.ts.map +1 -0
- package/dist/browser/api/paths.d.ts +13 -0
- package/dist/browser/api/paths.d.ts.map +1 -0
- package/dist/browser/api/planePaths.d.ts +31 -0
- package/dist/browser/api/planePaths.d.ts.map +1 -0
- package/dist/browser/api/requests.d.ts +18 -0
- package/dist/browser/api/requests.d.ts.map +1 -0
- package/dist/browser/browser.d.ts +21 -0
- package/dist/browser/browser.d.ts.map +1 -0
- package/dist/browser/browser.mjs +144 -0
- package/dist/browser/browser.mjs.map +7 -0
- package/dist/browser/types/AclEntry.d.ts +14 -0
- package/dist/browser/types/AclEntry.d.ts.map +1 -0
- package/dist/browser/types/BrowserPayload.d.ts +48 -0
- package/dist/browser/types/BrowserPayload.d.ts.map +1 -0
- package/dist/browser/types/DatalakeConfig.d.ts +48 -0
- package/dist/browser/types/DatalakeConfig.d.ts.map +1 -0
- package/dist/browser/types/DatalakeDescriptor.d.ts +38 -0
- package/dist/browser/types/DatalakeDescriptor.d.ts.map +1 -0
- package/dist/browser/types/Principal.d.ts +17 -0
- package/dist/browser/types/Principal.d.ts.map +1 -0
- package/dist/browser/types/Region.d.ts +8 -0
- package/dist/browser/types/Region.d.ts.map +1 -0
- package/dist/browser/types/Scope.d.ts +17 -0
- package/dist/browser/types/Scope.d.ts.map +1 -0
- package/dist/browser/types/Status.d.ts +7 -0
- package/dist/browser/types/Status.d.ts.map +1 -0
- package/dist/browser/types/Tier.d.ts +8 -0
- package/dist/browser/types/Tier.d.ts.map +1 -0
- package/dist/browser/types/Token.d.ts +18 -0
- package/dist/browser/types/Token.d.ts.map +1 -0
- package/dist/node/api/errors.d.ts +24 -0
- package/dist/node/api/errors.d.ts.map +1 -0
- package/dist/node/api/health.d.ts +12 -0
- package/dist/node/api/health.d.ts.map +1 -0
- package/dist/node/api/paths.d.ts +13 -0
- package/dist/node/api/paths.d.ts.map +1 -0
- package/dist/node/api/planePaths.d.ts +31 -0
- package/dist/node/api/planePaths.d.ts.map +1 -0
- package/dist/node/api/requests.d.ts +18 -0
- package/dist/node/api/requests.d.ts.map +1 -0
- package/dist/node/auth/sessionTokenCodec.d.ts +69 -0
- package/dist/node/auth/sessionTokenCodec.d.ts.map +1 -0
- package/dist/node/auth/strictHs256.d.ts +13 -0
- package/dist/node/auth/strictHs256.d.ts.map +1 -0
- package/dist/node/auth/tokenCodec.d.ts +49 -0
- package/dist/node/auth/tokenCodec.d.ts.map +1 -0
- package/dist/node/browser.d.ts +21 -0
- package/dist/node/browser.d.ts.map +1 -0
- package/dist/node/index.d.ts +27 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.mjs +423 -0
- package/dist/node/index.mjs.map +7 -0
- package/dist/node/store/DatalakeStore.d.ts +36 -0
- package/dist/node/store/DatalakeStore.d.ts.map +1 -0
- package/dist/node/types/AclEntry.d.ts +14 -0
- package/dist/node/types/AclEntry.d.ts.map +1 -0
- package/dist/node/types/BrowserPayload.d.ts +48 -0
- package/dist/node/types/BrowserPayload.d.ts.map +1 -0
- package/dist/node/types/DatalakeConfig.d.ts +48 -0
- package/dist/node/types/DatalakeConfig.d.ts.map +1 -0
- package/dist/node/types/DatalakeDescriptor.d.ts +38 -0
- package/dist/node/types/DatalakeDescriptor.d.ts.map +1 -0
- package/dist/node/types/Payload.d.ts +75 -0
- package/dist/node/types/Payload.d.ts.map +1 -0
- package/dist/node/types/Principal.d.ts +17 -0
- package/dist/node/types/Principal.d.ts.map +1 -0
- package/dist/node/types/Region.d.ts +8 -0
- package/dist/node/types/Region.d.ts.map +1 -0
- package/dist/node/types/Scope.d.ts +17 -0
- package/dist/node/types/Scope.d.ts.map +1 -0
- package/dist/node/types/Status.d.ts +7 -0
- package/dist/node/types/Status.d.ts.map +1 -0
- package/dist/node/types/Tier.d.ts +8 -0
- package/dist/node/types/Tier.d.ts.map +1 -0
- package/dist/node/types/Token.d.ts +18 -0
- package/dist/node/types/Token.d.ts.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
// src/api/errors.ts
|
|
2
|
+
var API_ERROR_STATUS = {
|
|
3
|
+
bad_request: 400,
|
|
4
|
+
unauthorized: 401,
|
|
5
|
+
forbidden: 403,
|
|
6
|
+
not_found: 404,
|
|
7
|
+
conflict: 409,
|
|
8
|
+
unprocessable: 422,
|
|
9
|
+
rate_limited: 429,
|
|
10
|
+
internal: 500
|
|
11
|
+
};
|
|
12
|
+
var DatalakeApiError = class extends Error {
|
|
13
|
+
code;
|
|
14
|
+
requestId;
|
|
15
|
+
status;
|
|
16
|
+
constructor(status, body) {
|
|
17
|
+
super(body.message);
|
|
18
|
+
this.name = "DatalakeApiError";
|
|
19
|
+
this.code = body.code;
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.requestId = body.requestId;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/api/paths.ts
|
|
26
|
+
var DATALAKE_API_VERSION = "v1";
|
|
27
|
+
function datalakeCollectionPath() {
|
|
28
|
+
return `/${DATALAKE_API_VERSION}/datalakes`;
|
|
29
|
+
}
|
|
30
|
+
function datalakeResourcePath(idOrName) {
|
|
31
|
+
return `${datalakeCollectionPath()}/${encodeURIComponent(idOrName)}`;
|
|
32
|
+
}
|
|
33
|
+
function datalakeGrantsPath(idOrName) {
|
|
34
|
+
return `${datalakeResourcePath(idOrName)}/grants`;
|
|
35
|
+
}
|
|
36
|
+
function datalakeGrantPath(idOrName, principal) {
|
|
37
|
+
return `${datalakeGrantsPath(idOrName)}/${encodeURIComponent(principal)}`;
|
|
38
|
+
}
|
|
39
|
+
function datalakeTokensPath(idOrName) {
|
|
40
|
+
return `${datalakeResourcePath(idOrName)}/tokens`;
|
|
41
|
+
}
|
|
42
|
+
function datalakeHealthPath() {
|
|
43
|
+
return `/${DATALAKE_API_VERSION}/health`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/api/planePaths.ts
|
|
47
|
+
var DATALAKE_HEADER_DUPLICATES = "x-datalake-duplicates";
|
|
48
|
+
var DATALAKE_HEADER_REJECTED = "x-datalake-rejected";
|
|
49
|
+
var DATALAKE_HEADER_NEXT_CURSOR = "x-datalake-next-cursor";
|
|
50
|
+
function datalakePlaneResourcePath(id) {
|
|
51
|
+
return `/${DATALAKE_API_VERSION}/datalakes/${encodeURIComponent(id)}`;
|
|
52
|
+
}
|
|
53
|
+
function datalakePlaneInsertPath(id) {
|
|
54
|
+
return `${datalakePlaneResourcePath(id)}/insert`;
|
|
55
|
+
}
|
|
56
|
+
function datalakePlaneGetPath(id, hash) {
|
|
57
|
+
return `${datalakePlaneResourcePath(id)}/get/${encodeURIComponent(hash)}`;
|
|
58
|
+
}
|
|
59
|
+
function datalakePlaneGetManyPath(id) {
|
|
60
|
+
return `${datalakePlaneResourcePath(id)}/get`;
|
|
61
|
+
}
|
|
62
|
+
function datalakePlaneNextPath(id) {
|
|
63
|
+
return `${datalakePlaneResourcePath(id)}/next`;
|
|
64
|
+
}
|
|
65
|
+
function datalakePlaneDeletePath(id, hash) {
|
|
66
|
+
return `${datalakePlaneResourcePath(id)}/delete/${encodeURIComponent(hash)}`;
|
|
67
|
+
}
|
|
68
|
+
function datalakePlaneClearPath(id) {
|
|
69
|
+
return `${datalakePlaneResourcePath(id)}/clear`;
|
|
70
|
+
}
|
|
71
|
+
function datalakePlaneUsagePath(id) {
|
|
72
|
+
return `${datalakePlaneResourcePath(id)}/usage`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/auth/sessionTokenCodec.ts
|
|
76
|
+
import { createHmac as createHmac2, randomBytes } from "node:crypto";
|
|
77
|
+
|
|
78
|
+
// src/auth/strictHs256.ts
|
|
79
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
80
|
+
var StrictHs256Error = class extends Error {
|
|
81
|
+
code;
|
|
82
|
+
constructor(code, message) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.name = "StrictHs256Error";
|
|
85
|
+
this.code = code;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function verifyStrictHs256(token, signingSecret) {
|
|
89
|
+
const parts = token.split(".");
|
|
90
|
+
if (parts.length !== 3) {
|
|
91
|
+
throw new StrictHs256Error("malformed", "Token is not a JWS compact serialization");
|
|
92
|
+
}
|
|
93
|
+
const [headerPart, payloadPart, signaturePart] = parts;
|
|
94
|
+
const header = decodeJsonObject(headerPart, "Header");
|
|
95
|
+
assertExactKeys(header, ["alg", "typ"], "Header");
|
|
96
|
+
if (header.alg !== "HS256") throw new StrictHs256Error("malformed", "Header alg must be HS256");
|
|
97
|
+
if (header.typ !== "JWT") throw new StrictHs256Error("malformed", "Header typ must be JWT");
|
|
98
|
+
const provided = decodeBase64Url(signaturePart, "Signature");
|
|
99
|
+
const expected = createHmac("sha256", signingSecret).update(`${headerPart}.${payloadPart}`).digest();
|
|
100
|
+
if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) {
|
|
101
|
+
throw new StrictHs256Error("bad_signature", "Signature mismatch");
|
|
102
|
+
}
|
|
103
|
+
return { payload: decodeJsonObject(payloadPart, "Payload") };
|
|
104
|
+
}
|
|
105
|
+
function assertExactKeys(value, expected, label) {
|
|
106
|
+
const actual = Object.keys(value).sort();
|
|
107
|
+
const canonical = [...expected].sort();
|
|
108
|
+
if (actual.length !== canonical.length || actual.some((key, index) => key !== canonical[index])) {
|
|
109
|
+
throw new StrictHs256Error("malformed", `${label} fields must be exactly: ${canonical.join(", ")}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function isFiniteInteger(value) {
|
|
113
|
+
return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value);
|
|
114
|
+
}
|
|
115
|
+
function isNonEmptyString(value) {
|
|
116
|
+
return typeof value === "string" && value.length > 0;
|
|
117
|
+
}
|
|
118
|
+
function decodeJsonObject(value, label) {
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(decodeBase64Url(value, label).toString("utf8"));
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error instanceof StrictHs256Error) throw error;
|
|
124
|
+
throw new StrictHs256Error("malformed", `${label} is not valid JSON`);
|
|
125
|
+
}
|
|
126
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
127
|
+
throw new StrictHs256Error("malformed", `${label} must be a JSON object`);
|
|
128
|
+
}
|
|
129
|
+
return parsed;
|
|
130
|
+
}
|
|
131
|
+
function decodeBase64Url(value, label) {
|
|
132
|
+
if (value.length === 0 || !/^[\w-]+$/.test(value)) {
|
|
133
|
+
throw new StrictHs256Error("malformed", `${label} is not canonical base64url`);
|
|
134
|
+
}
|
|
135
|
+
const decoded = Buffer.from(value, "base64url");
|
|
136
|
+
if (decoded.toString("base64url") !== value) {
|
|
137
|
+
throw new StrictHs256Error("malformed", `${label} is not canonical base64url`);
|
|
138
|
+
}
|
|
139
|
+
return decoded;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/auth/sessionTokenCodec.ts
|
|
143
|
+
function base64UrlEncode(input) {
|
|
144
|
+
const buffer = typeof input === "string" ? Buffer.from(input) : input;
|
|
145
|
+
return buffer.toString("base64url");
|
|
146
|
+
}
|
|
147
|
+
function signSessionToken(input) {
|
|
148
|
+
const issuedAt = Math.floor(Date.now() / 1e3);
|
|
149
|
+
const expiresAt = issuedAt + input.ttlSeconds;
|
|
150
|
+
const payload = {
|
|
151
|
+
sub: input.address.toLowerCase(),
|
|
152
|
+
typ: "session",
|
|
153
|
+
iat: issuedAt,
|
|
154
|
+
exp: expiresAt,
|
|
155
|
+
jti: randomBytes(8).toString("hex")
|
|
156
|
+
};
|
|
157
|
+
const header = base64UrlEncode(JSON.stringify({ alg: "HS256", typ: "JWT" }));
|
|
158
|
+
const body = base64UrlEncode(JSON.stringify(payload));
|
|
159
|
+
const toSign = `${header}.${body}`;
|
|
160
|
+
const signature = createHmac2("sha256", input.signingSecret).update(toSign).digest("base64url");
|
|
161
|
+
return {
|
|
162
|
+
token: `${toSign}.${signature}`,
|
|
163
|
+
expiresAt: new Date(expiresAt * 1e3).toISOString()
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
var SessionTokenVerificationError = class extends Error {
|
|
167
|
+
code;
|
|
168
|
+
constructor(code, message) {
|
|
169
|
+
super(message);
|
|
170
|
+
this.name = "SessionTokenVerificationError";
|
|
171
|
+
this.code = code;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
function verifySessionToken(input) {
|
|
175
|
+
let raw;
|
|
176
|
+
try {
|
|
177
|
+
raw = verifyStrictHs256(input.token, input.signingSecret).payload;
|
|
178
|
+
assertExactKeys(raw, ["exp", "iat", "jti", "sub", "typ"], "Payload");
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (error instanceof StrictHs256Error) {
|
|
181
|
+
throw new SessionTokenVerificationError(error.code, error.message);
|
|
182
|
+
}
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
if (raw.typ !== "session") {
|
|
186
|
+
throw new SessionTokenVerificationError("wrong_type", "Not a session token");
|
|
187
|
+
}
|
|
188
|
+
if (!isNonEmptyString(raw.sub)) throw new SessionTokenVerificationError("malformed", "Payload sub must be a non-empty string");
|
|
189
|
+
if (!isNonEmptyString(raw.jti)) throw new SessionTokenVerificationError("malformed", "Payload jti must be a non-empty string");
|
|
190
|
+
if (!isFiniteInteger(raw.iat)) throw new SessionTokenVerificationError("malformed", "Payload iat must be a finite integer");
|
|
191
|
+
if (!isFiniteInteger(raw.exp)) throw new SessionTokenVerificationError("malformed", "Payload exp must be a finite integer");
|
|
192
|
+
if (raw.exp <= raw.iat) throw new SessionTokenVerificationError("malformed", "Payload exp must be greater than iat");
|
|
193
|
+
const now = input.now ?? Math.floor(Date.now() / 1e3);
|
|
194
|
+
if (raw.exp <= now) {
|
|
195
|
+
throw new SessionTokenVerificationError("expired", "Token has expired");
|
|
196
|
+
}
|
|
197
|
+
if (raw.iat > now + 60) {
|
|
198
|
+
throw new SessionTokenVerificationError("not_yet_valid", "Token issued in the future");
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
sub: raw.sub,
|
|
202
|
+
typ: raw.typ,
|
|
203
|
+
iat: raw.iat,
|
|
204
|
+
exp: raw.exp,
|
|
205
|
+
jti: raw.jti
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function signChallenge(input) {
|
|
209
|
+
const issuedAt = Math.floor(Date.now() / 1e3);
|
|
210
|
+
const expiresAt = issuedAt + input.ttlSeconds;
|
|
211
|
+
const payload = {
|
|
212
|
+
address: input.address.toLowerCase(),
|
|
213
|
+
typ: "challenge",
|
|
214
|
+
iat: issuedAt,
|
|
215
|
+
exp: expiresAt,
|
|
216
|
+
jti: randomBytes(8).toString("hex")
|
|
217
|
+
};
|
|
218
|
+
const header = base64UrlEncode(JSON.stringify({ alg: "HS256", typ: "JWT" }));
|
|
219
|
+
const body = base64UrlEncode(JSON.stringify(payload));
|
|
220
|
+
const toSign = `${header}.${body}`;
|
|
221
|
+
const signature = createHmac2("sha256", input.signingSecret).update(toSign).digest("base64url");
|
|
222
|
+
return {
|
|
223
|
+
challenge: `${toSign}.${signature}`,
|
|
224
|
+
expiresAt: new Date(expiresAt * 1e3).toISOString()
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function verifyChallenge(input) {
|
|
228
|
+
let raw;
|
|
229
|
+
try {
|
|
230
|
+
raw = verifyStrictHs256(input.challenge, input.signingSecret).payload;
|
|
231
|
+
assertExactKeys(raw, ["address", "exp", "iat", "jti", "typ"], "Challenge payload");
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error instanceof StrictHs256Error) {
|
|
234
|
+
throw new SessionTokenVerificationError(error.code, error.message);
|
|
235
|
+
}
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
if (raw.typ !== "challenge") {
|
|
239
|
+
throw new SessionTokenVerificationError("wrong_type", "Not a challenge token");
|
|
240
|
+
}
|
|
241
|
+
if (!isNonEmptyString(raw.address) || !/^(?:0x)?[\da-fA-F]{40}$/.test(raw.address)) {
|
|
242
|
+
throw new SessionTokenVerificationError("malformed", "Challenge address must be a 20-byte hex address");
|
|
243
|
+
}
|
|
244
|
+
if (!isNonEmptyString(raw.jti)) throw new SessionTokenVerificationError("malformed", "Challenge jti must be a non-empty string");
|
|
245
|
+
if (!isFiniteInteger(raw.iat)) throw new SessionTokenVerificationError("malformed", "Challenge iat must be a finite integer");
|
|
246
|
+
if (!isFiniteInteger(raw.exp)) throw new SessionTokenVerificationError("malformed", "Challenge exp must be a finite integer");
|
|
247
|
+
if (raw.exp <= raw.iat) throw new SessionTokenVerificationError("malformed", "Challenge exp must be greater than iat");
|
|
248
|
+
const now = input.now ?? Math.floor(Date.now() / 1e3);
|
|
249
|
+
if (raw.exp <= now) {
|
|
250
|
+
throw new SessionTokenVerificationError("expired", "Challenge has expired");
|
|
251
|
+
}
|
|
252
|
+
if (raw.iat > now + 60) {
|
|
253
|
+
throw new SessionTokenVerificationError("not_yet_valid", "Challenge issued in the future");
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
address: raw.address,
|
|
257
|
+
typ: raw.typ,
|
|
258
|
+
iat: raw.iat,
|
|
259
|
+
exp: raw.exp,
|
|
260
|
+
jti: raw.jti
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/auth/tokenCodec.ts
|
|
265
|
+
import { createHmac as createHmac3, randomBytes as randomBytes2 } from "node:crypto";
|
|
266
|
+
function base64UrlEncode2(input) {
|
|
267
|
+
const buffer = typeof input === "string" ? Buffer.from(input) : input;
|
|
268
|
+
return buffer.toString("base64url");
|
|
269
|
+
}
|
|
270
|
+
function signToken(input) {
|
|
271
|
+
const issuedAt = Math.floor(Date.now() / 1e3);
|
|
272
|
+
const expiresAt = issuedAt + input.ttlSeconds;
|
|
273
|
+
const payload = {
|
|
274
|
+
sub: input.ownerId,
|
|
275
|
+
dl: input.datalakeId,
|
|
276
|
+
role: input.role,
|
|
277
|
+
iat: issuedAt,
|
|
278
|
+
exp: expiresAt,
|
|
279
|
+
jti: randomBytes2(8).toString("hex")
|
|
280
|
+
};
|
|
281
|
+
const header = base64UrlEncode2(JSON.stringify({ alg: "HS256", typ: "JWT" }));
|
|
282
|
+
const body = base64UrlEncode2(JSON.stringify(payload));
|
|
283
|
+
const toSign = `${header}.${body}`;
|
|
284
|
+
const signature = createHmac3("sha256", input.signingSecret).update(toSign).digest("base64url");
|
|
285
|
+
return {
|
|
286
|
+
token: `${toSign}.${signature}`,
|
|
287
|
+
datalakeId: input.datalakeId,
|
|
288
|
+
role: input.role,
|
|
289
|
+
expiresAt: new Date(expiresAt * 1e3).toISOString(),
|
|
290
|
+
url: input.url
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
var TokenVerificationError = class extends Error {
|
|
294
|
+
code;
|
|
295
|
+
constructor(code, message) {
|
|
296
|
+
super(message);
|
|
297
|
+
this.name = "TokenVerificationError";
|
|
298
|
+
this.code = code;
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
function verifyToken(input) {
|
|
302
|
+
let raw;
|
|
303
|
+
try {
|
|
304
|
+
raw = verifyStrictHs256(input.token, input.signingSecret).payload;
|
|
305
|
+
assertExactKeys(raw, ["dl", "exp", "iat", "jti", "role", "sub"], "Payload");
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (error instanceof StrictHs256Error) {
|
|
308
|
+
throw new TokenVerificationError(error.code, error.message);
|
|
309
|
+
}
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
if (!isNonEmptyString(raw.sub)) throw new TokenVerificationError("malformed", "Payload sub must be a non-empty string");
|
|
313
|
+
if (!isNonEmptyString(raw.dl)) throw new TokenVerificationError("malformed", "Payload dl must be a non-empty string");
|
|
314
|
+
if (raw.role !== "viewer" && raw.role !== "runner") {
|
|
315
|
+
throw new TokenVerificationError("malformed", "Payload role must be viewer or runner");
|
|
316
|
+
}
|
|
317
|
+
if (!isFiniteInteger(raw.iat)) throw new TokenVerificationError("malformed", "Payload iat must be a finite integer");
|
|
318
|
+
if (!isFiniteInteger(raw.exp)) throw new TokenVerificationError("malformed", "Payload exp must be a finite integer");
|
|
319
|
+
if (!isNonEmptyString(raw.jti)) throw new TokenVerificationError("malformed", "Payload jti must be a non-empty string");
|
|
320
|
+
if (raw.exp <= raw.iat) throw new TokenVerificationError("malformed", "Payload exp must be greater than iat");
|
|
321
|
+
const payload = {
|
|
322
|
+
sub: raw.sub,
|
|
323
|
+
dl: raw.dl,
|
|
324
|
+
role: raw.role,
|
|
325
|
+
iat: raw.iat,
|
|
326
|
+
exp: raw.exp,
|
|
327
|
+
jti: raw.jti
|
|
328
|
+
};
|
|
329
|
+
const now = input.now ?? Math.floor(Date.now() / 1e3);
|
|
330
|
+
if (payload.exp <= now) {
|
|
331
|
+
throw new TokenVerificationError("expired", "Token has expired");
|
|
332
|
+
}
|
|
333
|
+
if (payload.iat > now + 60) {
|
|
334
|
+
throw new TokenVerificationError("not_yet_valid", "Token issued in the future");
|
|
335
|
+
}
|
|
336
|
+
return payload;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// src/store/DatalakeStore.ts
|
|
340
|
+
var ROLE_RANK = {
|
|
341
|
+
viewer: 1,
|
|
342
|
+
runner: 2
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// src/types/Principal.ts
|
|
346
|
+
var PUBLIC_PRINCIPAL = "public";
|
|
347
|
+
function isPublicPrincipal(principal) {
|
|
348
|
+
return principal === PUBLIC_PRINCIPAL;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/types/Region.ts
|
|
352
|
+
var DATALAKE_REGIONS = ["us-west-2", "us-east-1", "eu-west-1"];
|
|
353
|
+
var DEFAULT_REGION = "us-west-2";
|
|
354
|
+
|
|
355
|
+
// src/types/Scope.ts
|
|
356
|
+
var DATALAKE_CONTROL_AUDIENCE = "aries-datalake-control";
|
|
357
|
+
var DATALAKE_WALLET_JWT_SCHEMA = "network.xyo.auth.signin";
|
|
358
|
+
var DATALAKE_CONTROL_OPERATIONS = [
|
|
359
|
+
"datalake:create",
|
|
360
|
+
"datalake:describe",
|
|
361
|
+
"datalake:list"
|
|
362
|
+
];
|
|
363
|
+
var DATALAKE_DATA_OPERATIONS = [
|
|
364
|
+
"payload:append",
|
|
365
|
+
"payload:read",
|
|
366
|
+
"usage:read"
|
|
367
|
+
];
|
|
368
|
+
var DATALAKE_CONTROL_SCOPE = DATALAKE_CONTROL_OPERATIONS.join(" ");
|
|
369
|
+
var DATALAKE_DATA_SCOPE = DATALAKE_DATA_OPERATIONS.join(" ");
|
|
370
|
+
function isDatalakeControlOperation(value) {
|
|
371
|
+
return DATALAKE_CONTROL_OPERATIONS.includes(value);
|
|
372
|
+
}
|
|
373
|
+
function isDatalakeDataOperation(value) {
|
|
374
|
+
return DATALAKE_DATA_OPERATIONS.includes(value);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/types/Tier.ts
|
|
378
|
+
var DATALAKE_TIERS = ["small", "medium", "large", "archive"];
|
|
379
|
+
export {
|
|
380
|
+
API_ERROR_STATUS,
|
|
381
|
+
DATALAKE_API_VERSION,
|
|
382
|
+
DATALAKE_CONTROL_AUDIENCE,
|
|
383
|
+
DATALAKE_CONTROL_OPERATIONS,
|
|
384
|
+
DATALAKE_CONTROL_SCOPE,
|
|
385
|
+
DATALAKE_DATA_OPERATIONS,
|
|
386
|
+
DATALAKE_DATA_SCOPE,
|
|
387
|
+
DATALAKE_HEADER_DUPLICATES,
|
|
388
|
+
DATALAKE_HEADER_NEXT_CURSOR,
|
|
389
|
+
DATALAKE_HEADER_REJECTED,
|
|
390
|
+
DATALAKE_REGIONS,
|
|
391
|
+
DATALAKE_TIERS,
|
|
392
|
+
DATALAKE_WALLET_JWT_SCHEMA,
|
|
393
|
+
DEFAULT_REGION,
|
|
394
|
+
DatalakeApiError,
|
|
395
|
+
PUBLIC_PRINCIPAL,
|
|
396
|
+
ROLE_RANK,
|
|
397
|
+
SessionTokenVerificationError,
|
|
398
|
+
TokenVerificationError,
|
|
399
|
+
datalakeCollectionPath,
|
|
400
|
+
datalakeGrantPath,
|
|
401
|
+
datalakeGrantsPath,
|
|
402
|
+
datalakeHealthPath,
|
|
403
|
+
datalakePlaneClearPath,
|
|
404
|
+
datalakePlaneDeletePath,
|
|
405
|
+
datalakePlaneGetManyPath,
|
|
406
|
+
datalakePlaneGetPath,
|
|
407
|
+
datalakePlaneInsertPath,
|
|
408
|
+
datalakePlaneNextPath,
|
|
409
|
+
datalakePlaneResourcePath,
|
|
410
|
+
datalakePlaneUsagePath,
|
|
411
|
+
datalakeResourcePath,
|
|
412
|
+
datalakeTokensPath,
|
|
413
|
+
isDatalakeControlOperation,
|
|
414
|
+
isDatalakeDataOperation,
|
|
415
|
+
isPublicPrincipal,
|
|
416
|
+
signChallenge,
|
|
417
|
+
signSessionToken,
|
|
418
|
+
signToken,
|
|
419
|
+
verifyChallenge,
|
|
420
|
+
verifySessionToken,
|
|
421
|
+
verifyToken
|
|
422
|
+
};
|
|
423
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/api/errors.ts", "../../src/api/paths.ts", "../../src/api/planePaths.ts", "../../src/auth/sessionTokenCodec.ts", "../../src/auth/strictHs256.ts", "../../src/auth/tokenCodec.ts", "../../src/store/DatalakeStore.ts", "../../src/types/Principal.ts", "../../src/types/Region.ts", "../../src/types/Scope.ts", "../../src/types/Tier.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Standard error body returned by the control plane for any non-2xx response.\n * The `code` is a stable machine-readable identifier; `message` is the\n * human-readable description.\n */\nexport interface ApiErrorBody {\n code: ApiErrorCode\n message: string\n /** Optional request-correlation id for server logs. */\n requestId?: string\n}\n\nexport type ApiErrorCode\n = | 'bad_request'\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'conflict'\n | 'unprocessable'\n | 'rate_limited'\n | 'internal'\n\nexport const API_ERROR_STATUS: Record<ApiErrorCode, number> = {\n bad_request: 400,\n unauthorized: 401,\n forbidden: 403,\n not_found: 404,\n conflict: 409,\n unprocessable: 422,\n rate_limited: 429,\n internal: 500,\n}\n\n/**\n * Thrown by `RestDatalakeClient` when the server returns a non-2xx response.\n * Carries the structured error body so callers can branch on `.code`.\n */\nexport class DatalakeApiError extends Error {\n readonly code: ApiErrorCode\n readonly requestId?: string\n readonly status: number\n\n constructor(status: number, body: ApiErrorBody) {\n super(body.message)\n this.name = 'DatalakeApiError'\n this.code = body.code\n this.status = status\n this.requestId = body.requestId\n }\n}\n", "/**\n * Canonical path builders for the datalake control-plane HTTP API.\n * Both the REST client and the control-plane service import from here so\n * the wire contract has a single source of truth.\n */\n\nexport const DATALAKE_API_VERSION = 'v1'\n\nexport function datalakeCollectionPath(): string {\n return `/${DATALAKE_API_VERSION}/datalakes`\n}\n\nexport function datalakeResourcePath(idOrName: string): string {\n return `${datalakeCollectionPath()}/${encodeURIComponent(idOrName)}`\n}\n\nexport function datalakeGrantsPath(idOrName: string): string {\n return `${datalakeResourcePath(idOrName)}/grants`\n}\n\nexport function datalakeGrantPath(idOrName: string, principal: string): string {\n return `${datalakeGrantsPath(idOrName)}/${encodeURIComponent(principal)}`\n}\n\nexport function datalakeTokensPath(idOrName: string): string {\n return `${datalakeResourcePath(idOrName)}/tokens`\n}\n\nexport function datalakeHealthPath(): string {\n return `/${DATALAKE_API_VERSION}/health`\n}\n", "/**\n * Data-plane path builders. The data plane is addressed by datalake id\n * (globally unique), not by name (which is scoped to owner). Route names\n * mirror the XYO archivist REST middleware (`/insert`, `/get/:hash`, `/next`)\n * so a generic archivist client can talk to a datalake-plane after picking\n * the per-datalake prefix.\n */\n\nimport { DATALAKE_API_VERSION } from './paths.ts'\n\n/**\n * Header that conveys how many payloads in the insert request were skipped\n * because their hash already existed in the datalake.\n */\nexport const DATALAKE_HEADER_DUPLICATES = 'x-datalake-duplicates'\n\n/**\n * Header that conveys the comma-separated list of archivist-computed\n * `_hash` values rejected by the datalake's schema policy.\n */\nexport const DATALAKE_HEADER_REJECTED = 'x-datalake-rejected'\n\n/**\n * Header that conveys the cursor (`_sequence`) of the last payload in a\n * `next` response page, when more pages may exist.\n */\nexport const DATALAKE_HEADER_NEXT_CURSOR = 'x-datalake-next-cursor'\n\nexport function datalakePlaneResourcePath(id: string): string {\n return `/${DATALAKE_API_VERSION}/datalakes/${encodeURIComponent(id)}`\n}\n\nexport function datalakePlaneInsertPath(id: string): string {\n return `${datalakePlaneResourcePath(id)}/insert`\n}\n\nexport function datalakePlaneGetPath(id: string, hash: string): string {\n return `${datalakePlaneResourcePath(id)}/get/${encodeURIComponent(hash)}`\n}\n\nexport function datalakePlaneGetManyPath(id: string): string {\n return `${datalakePlaneResourcePath(id)}/get`\n}\n\nexport function datalakePlaneNextPath(id: string): string {\n return `${datalakePlaneResourcePath(id)}/next`\n}\n\nexport function datalakePlaneDeletePath(id: string, hash: string): string {\n return `${datalakePlaneResourcePath(id)}/delete/${encodeURIComponent(hash)}`\n}\n\nexport function datalakePlaneClearPath(id: string): string {\n return `${datalakePlaneResourcePath(id)}/clear`\n}\n\nexport function datalakePlaneUsagePath(id: string): string {\n return `${datalakePlaneResourcePath(id)}/usage`\n}\n", "import { createHmac, randomBytes } from 'node:crypto'\n\nimport {\n assertExactKeys, isFiniteInteger, isNonEmptyString, StrictHs256Error, verifyStrictHs256,\n} from './strictHs256.ts'\n\n/**\n * Session tokens issued after a successful XYO Wallet sign-in. The wire\n * format mirrors `tokenCodec.ts` (HS256 JWT) but the payload carries no\n * `dl` claim \u2014 sessions are scoped to a user (wallet address), not a\n * single datalake. The control plane mints these; both planes verify\n * them via `verifySessionToken`.\n */\n\nexport interface SessionTokenPayload {\n /** Expiration (seconds since epoch). */\n exp: number\n /** Issued-at (seconds since epoch). */\n iat: number\n /** Unique token id. */\n jti: string\n /** Wallet address that authenticated; used as the `ownerId`. */\n sub: string\n /** Token kind. Always `'session'` for this codec. */\n typ: 'session'\n}\n\nexport interface SignSessionTokenInput {\n address: string\n signingSecret: string\n ttlSeconds: number\n}\n\nexport interface SignedSessionToken {\n expiresAt: string\n token: string\n}\n\nfunction base64UrlEncode(input: Buffer | string): string {\n const buffer = typeof input === 'string' ? Buffer.from(input) : input\n return buffer.toString('base64url')\n}\n\nexport function signSessionToken(input: SignSessionTokenInput): SignedSessionToken {\n const issuedAt = Math.floor(Date.now() / 1000)\n const expiresAt = issuedAt + input.ttlSeconds\n const payload: SessionTokenPayload = {\n sub: input.address.toLowerCase(),\n typ: 'session',\n iat: issuedAt,\n exp: expiresAt,\n jti: randomBytes(8).toString('hex'),\n }\n const header = base64UrlEncode(JSON.stringify({ alg: 'HS256', typ: 'JWT' }))\n const body = base64UrlEncode(JSON.stringify(payload))\n const toSign = `${header}.${body}`\n const signature = createHmac('sha256', input.signingSecret).update(toSign).digest('base64url')\n return {\n token: `${toSign}.${signature}`,\n expiresAt: new Date(expiresAt * 1000).toISOString(),\n }\n}\n\nexport type SessionTokenVerificationErrorCode = 'malformed' | 'bad_signature' | 'expired' | 'not_yet_valid' | 'wrong_type'\n\nexport class SessionTokenVerificationError extends Error {\n readonly code: SessionTokenVerificationErrorCode\n\n constructor(code: SessionTokenVerificationErrorCode, message: string) {\n super(message)\n this.name = 'SessionTokenVerificationError'\n this.code = code\n }\n}\n\nexport interface VerifySessionTokenInput {\n now?: number\n signingSecret: string\n token: string\n}\n\nexport function verifySessionToken(input: VerifySessionTokenInput): SessionTokenPayload {\n let raw: Record<string, unknown>\n try {\n raw = verifyStrictHs256(input.token, input.signingSecret).payload\n assertExactKeys(raw, ['exp', 'iat', 'jti', 'sub', 'typ'], 'Payload')\n } catch (error) {\n if (error instanceof StrictHs256Error) {\n throw new SessionTokenVerificationError(error.code, error.message)\n }\n throw error\n }\n if (raw.typ !== 'session') {\n throw new SessionTokenVerificationError('wrong_type', 'Not a session token')\n }\n if (!isNonEmptyString(raw.sub)) throw new SessionTokenVerificationError('malformed', 'Payload sub must be a non-empty string')\n if (!isNonEmptyString(raw.jti)) throw new SessionTokenVerificationError('malformed', 'Payload jti must be a non-empty string')\n if (!isFiniteInteger(raw.iat)) throw new SessionTokenVerificationError('malformed', 'Payload iat must be a finite integer')\n if (!isFiniteInteger(raw.exp)) throw new SessionTokenVerificationError('malformed', 'Payload exp must be a finite integer')\n if (raw.exp <= raw.iat) throw new SessionTokenVerificationError('malformed', 'Payload exp must be greater than iat')\n\n const now = input.now ?? Math.floor(Date.now() / 1000)\n if (raw.exp <= now) {\n throw new SessionTokenVerificationError('expired', 'Token has expired')\n }\n if (raw.iat > now + 60) {\n throw new SessionTokenVerificationError('not_yet_valid', 'Token issued in the future')\n }\n return {\n sub: raw.sub,\n typ: raw.typ,\n iat: raw.iat,\n exp: raw.exp,\n jti: raw.jti,\n }\n}\n\n/**\n * Single-use challenge token. The wallet signs the entire `token` string\n * (the wallet doesn't need to parse it). The server stores no state \u2014 it\n * just verifies its own signature on the challenge then verifies the\n * wallet's signature over the challenge string.\n */\n\nexport interface ChallengeTokenPayload {\n address: string\n exp: number\n iat: number\n jti: string\n typ: 'challenge'\n}\n\nexport interface SignChallengeInput {\n address: string\n signingSecret: string\n ttlSeconds: number\n}\n\nexport interface SignedChallenge {\n challenge: string\n expiresAt: string\n}\n\nexport function signChallenge(input: SignChallengeInput): SignedChallenge {\n const issuedAt = Math.floor(Date.now() / 1000)\n const expiresAt = issuedAt + input.ttlSeconds\n const payload: ChallengeTokenPayload = {\n address: input.address.toLowerCase(),\n typ: 'challenge',\n iat: issuedAt,\n exp: expiresAt,\n jti: randomBytes(8).toString('hex'),\n }\n const header = base64UrlEncode(JSON.stringify({ alg: 'HS256', typ: 'JWT' }))\n const body = base64UrlEncode(JSON.stringify(payload))\n const toSign = `${header}.${body}`\n const signature = createHmac('sha256', input.signingSecret).update(toSign).digest('base64url')\n return {\n challenge: `${toSign}.${signature}`,\n expiresAt: new Date(expiresAt * 1000).toISOString(),\n }\n}\n\nexport function verifyChallenge(input: { challenge: string; now?: number; signingSecret: string }): ChallengeTokenPayload {\n let raw: Record<string, unknown>\n try {\n raw = verifyStrictHs256(input.challenge, input.signingSecret).payload\n assertExactKeys(raw, ['address', 'exp', 'iat', 'jti', 'typ'], 'Challenge payload')\n } catch (error) {\n if (error instanceof StrictHs256Error) {\n throw new SessionTokenVerificationError(error.code, error.message)\n }\n throw error\n }\n if (raw.typ !== 'challenge') {\n throw new SessionTokenVerificationError('wrong_type', 'Not a challenge token')\n }\n if (!isNonEmptyString(raw.address) || !/^(?:0x)?[\\da-fA-F]{40}$/.test(raw.address)) {\n throw new SessionTokenVerificationError('malformed', 'Challenge address must be a 20-byte hex address')\n }\n if (!isNonEmptyString(raw.jti)) throw new SessionTokenVerificationError('malformed', 'Challenge jti must be a non-empty string')\n if (!isFiniteInteger(raw.iat)) throw new SessionTokenVerificationError('malformed', 'Challenge iat must be a finite integer')\n if (!isFiniteInteger(raw.exp)) throw new SessionTokenVerificationError('malformed', 'Challenge exp must be a finite integer')\n if (raw.exp <= raw.iat) throw new SessionTokenVerificationError('malformed', 'Challenge exp must be greater than iat')\n const now = input.now ?? Math.floor(Date.now() / 1000)\n if (raw.exp <= now) {\n throw new SessionTokenVerificationError('expired', 'Challenge has expired')\n }\n if (raw.iat > now + 60) {\n throw new SessionTokenVerificationError('not_yet_valid', 'Challenge issued in the future')\n }\n return {\n address: raw.address,\n typ: raw.typ,\n iat: raw.iat,\n exp: raw.exp,\n jti: raw.jti,\n }\n}\n", "import { createHmac, timingSafeEqual } from 'node:crypto'\n\nexport type StrictHs256ErrorCode = 'bad_signature' | 'malformed'\n\nexport class StrictHs256Error extends Error {\n readonly code: StrictHs256ErrorCode\n\n constructor(code: StrictHs256ErrorCode, message: string) {\n super(message)\n this.name = 'StrictHs256Error'\n this.code = code\n }\n}\n\nexport interface StrictHs256Parts {\n payload: Record<string, unknown>\n}\n\nexport function verifyStrictHs256(token: string, signingSecret: string): StrictHs256Parts {\n const parts = token.split('.')\n if (parts.length !== 3) {\n throw new StrictHs256Error('malformed', 'Token is not a JWS compact serialization')\n }\n const [headerPart, payloadPart, signaturePart] = parts as [string, string, string]\n const header = decodeJsonObject(headerPart, 'Header')\n assertExactKeys(header, ['alg', 'typ'], 'Header')\n if (header.alg !== 'HS256') throw new StrictHs256Error('malformed', 'Header alg must be HS256')\n if (header.typ !== 'JWT') throw new StrictHs256Error('malformed', 'Header typ must be JWT')\n\n const provided = decodeBase64Url(signaturePart, 'Signature')\n const expected = createHmac('sha256', signingSecret)\n .update(`${headerPart}.${payloadPart}`)\n .digest()\n if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) {\n throw new StrictHs256Error('bad_signature', 'Signature mismatch')\n }\n\n return { payload: decodeJsonObject(payloadPart, 'Payload') }\n}\n\nexport function assertExactKeys(\n value: Record<string, unknown>,\n expected: readonly string[],\n label: string,\n): void {\n const actual = Object.keys(value).sort()\n const canonical = [...expected].sort()\n if (actual.length !== canonical.length || actual.some((key, index) => key !== canonical[index])) {\n throw new StrictHs256Error('malformed', `${label} fields must be exactly: ${canonical.join(', ')}`)\n }\n}\n\nexport function isFiniteInteger(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value)\n}\n\nexport function isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0\n}\n\nfunction decodeJsonObject(value: string, label: string): Record<string, unknown> {\n let parsed: unknown\n try {\n parsed = JSON.parse(decodeBase64Url(value, label).toString('utf8'))\n } catch (error) {\n if (error instanceof StrictHs256Error) throw error\n throw new StrictHs256Error('malformed', `${label} is not valid JSON`)\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new StrictHs256Error('malformed', `${label} must be a JSON object`)\n }\n return parsed as Record<string, unknown>\n}\n\nfunction decodeBase64Url(value: string, label: string): Buffer {\n if (value.length === 0 || !/^[\\w-]+$/.test(value)) {\n throw new StrictHs256Error('malformed', `${label} is not canonical base64url`)\n }\n const decoded = Buffer.from(value, 'base64url')\n if (decoded.toString('base64url') !== value) {\n throw new StrictHs256Error('malformed', `${label} is not canonical base64url`)\n }\n return decoded\n}\n", "import { createHmac, randomBytes } from 'node:crypto'\n\nimport type { DatalakeRole } from '../types/Principal.ts'\nimport type { DatalakeToken } from '../types/Token.ts'\nimport {\n assertExactKeys, isFiniteInteger, isNonEmptyString, StrictHs256Error, verifyStrictHs256,\n} from './strictHs256.ts'\n\n/**\n * HS256 token layout used for data-plane access. The signature is a small\n * subset of RFC 7519 \u2014 enough to interoperate with any JWT verifier but tiny\n * enough to implement without pulling a full JWT library. Replace with a\n * proper OIDC-signed JWT in P3.\n */\n\nexport interface TokenPayload {\n /** Datalake id the token grants access to. */\n dl: string\n /** Expiration (seconds since epoch). */\n exp: number\n /** Issued-at (seconds since epoch). */\n iat: number\n /** Unique token id \u2014 helpful for future revocation lists. */\n jti: string\n /** Role granted. */\n role: DatalakeRole\n /** Owner (user id) that minted the token. */\n sub: string\n}\n\nexport interface SignTokenInput {\n datalakeId: string\n ownerId: string\n role: DatalakeRole\n signingSecret: string\n ttlSeconds: number\n url: string\n}\n\nfunction base64UrlEncode(input: Buffer | string): string {\n const buffer = typeof input === 'string' ? Buffer.from(input) : input\n return buffer.toString('base64url')\n}\n\nexport function signToken(input: SignTokenInput): DatalakeToken {\n const issuedAt = Math.floor(Date.now() / 1000)\n const expiresAt = issuedAt + input.ttlSeconds\n const payload: TokenPayload = {\n sub: input.ownerId,\n dl: input.datalakeId,\n role: input.role,\n iat: issuedAt,\n exp: expiresAt,\n jti: randomBytes(8).toString('hex'),\n }\n const header = base64UrlEncode(JSON.stringify({ alg: 'HS256', typ: 'JWT' }))\n const body = base64UrlEncode(JSON.stringify(payload))\n const toSign = `${header}.${body}`\n const signature = createHmac('sha256', input.signingSecret).update(toSign).digest('base64url')\n return {\n token: `${toSign}.${signature}`,\n datalakeId: input.datalakeId,\n role: input.role,\n expiresAt: new Date(expiresAt * 1000).toISOString(),\n url: input.url,\n }\n}\n\nexport type TokenVerificationErrorCode = 'malformed' | 'bad_signature' | 'expired' | 'not_yet_valid'\n\nexport class TokenVerificationError extends Error {\n readonly code: TokenVerificationErrorCode\n\n constructor(code: TokenVerificationErrorCode, message: string) {\n super(message)\n this.name = 'TokenVerificationError'\n this.code = code\n }\n}\n\nexport interface VerifyTokenInput {\n /** Current wall-clock time (seconds since epoch). Injectable for tests. */\n now?: number\n signingSecret: string\n token: string\n}\n\n/**\n * Verifies an HS256 token issued by `signToken` and returns its payload.\n * Throws `TokenVerificationError` if the token is malformed, bears a bad\n * signature, or has expired.\n */\nexport function verifyToken(input: VerifyTokenInput): TokenPayload {\n let raw: Record<string, unknown>\n try {\n raw = verifyStrictHs256(input.token, input.signingSecret).payload\n assertExactKeys(raw, ['dl', 'exp', 'iat', 'jti', 'role', 'sub'], 'Payload')\n } catch (error) {\n if (error instanceof StrictHs256Error) {\n throw new TokenVerificationError(error.code, error.message)\n }\n throw error\n }\n\n if (!isNonEmptyString(raw.sub)) throw new TokenVerificationError('malformed', 'Payload sub must be a non-empty string')\n if (!isNonEmptyString(raw.dl)) throw new TokenVerificationError('malformed', 'Payload dl must be a non-empty string')\n if (raw.role !== 'viewer' && raw.role !== 'runner') {\n throw new TokenVerificationError('malformed', 'Payload role must be viewer or runner')\n }\n if (!isFiniteInteger(raw.iat)) throw new TokenVerificationError('malformed', 'Payload iat must be a finite integer')\n if (!isFiniteInteger(raw.exp)) throw new TokenVerificationError('malformed', 'Payload exp must be a finite integer')\n if (!isNonEmptyString(raw.jti)) throw new TokenVerificationError('malformed', 'Payload jti must be a non-empty string')\n if (raw.exp <= raw.iat) throw new TokenVerificationError('malformed', 'Payload exp must be greater than iat')\n\n const payload: TokenPayload = {\n sub: raw.sub,\n dl: raw.dl,\n role: raw.role,\n iat: raw.iat,\n exp: raw.exp,\n jti: raw.jti,\n }\n\n const now = input.now ?? Math.floor(Date.now() / 1000)\n if (payload.exp <= now) {\n throw new TokenVerificationError('expired', 'Token has expired')\n }\n if (payload.iat > now + 60) {\n // Allow 60 seconds of clock skew into the future.\n throw new TokenVerificationError('not_yet_valid', 'Token issued in the future')\n }\n return payload\n}\n", "import type { AclEntry } from '../types/AclEntry.ts'\nimport type { DatalakeDescriptor } from '../types/DatalakeDescriptor.ts'\nimport type { DatalakePrincipal, DatalakeRole } from '../types/Principal.ts'\n\n/**\n * Storage interface for the control plane. Lives in `datalake-core` so\n * decorators (`@ariestools/aries-datalake-k8s-bridge`) and alternate\n * implementations can depend on the contract without taking a dependency\n * on the full control plane.\n *\n * The in-memory implementation is used in development and for tests; a\n * Postgres-backed implementation handles production. Both ship in\n * `@ariestools/aries-datalake-control`.\n */\nexport interface DatalakeStore {\n /** Find by globally unique id (no owner scoping). Used by the data plane. */\n findById(id: string): Promise<DatalakeDescriptor | undefined>\n /** Find by id or name, scoped to a single owner. */\n findByIdOrName(ownerId: string, idOrName: string): Promise<DatalakeDescriptor | undefined>\n /** Insert a fresh descriptor. Caller fills in id/timestamps. */\n insert(descriptor: DatalakeDescriptor): Promise<void>\n /** Return all datalakes owned by the given user. */\n listByOwner(ownerId: string): Promise<DatalakeDescriptor[]>\n /** Remove a datalake. */\n remove(id: string): Promise<void>\n /** Drop an ACL entry by principal. */\n removeAcl(id: string, principal: DatalakePrincipal): Promise<DatalakeDescriptor>\n /** Replace an existing descriptor (full update). */\n update(descriptor: DatalakeDescriptor): Promise<void>\n /** Add or overwrite an ACL entry on a datalake, returning the updated descriptor. */\n upsertAcl(id: string, entry: AclEntry): Promise<DatalakeDescriptor>\n}\n\n/**\n * Roles considered higher privilege than others. Used when merging grants.\n */\nexport const ROLE_RANK: Record<DatalakeRole, number> = {\n viewer: 1,\n runner: 2,\n}\n", "/**\n * Access-control role granted to a principal on a datalake.\n * Maps 1:1 to XL1's DataLakeViewer (read) and DataLakeRunner (read/write) monikers.\n */\nexport type DatalakeRole = 'viewer' | 'runner'\n\n/**\n * A principal identifier. Either a control-plane user id, or the reserved\n * value `PUBLIC_PRINCIPAL` (`'public'`) which opens access to unauthenticated\n * callers at the granted role.\n */\nexport type DatalakePrincipal = string\n\nexport const PUBLIC_PRINCIPAL = 'public'\n\n/**\n * Type guard for the reserved `public` principal.\n */\nexport function isPublicPrincipal(principal: DatalakePrincipal): boolean {\n return principal === PUBLIC_PRINCIPAL\n}\n", "/**\n * Cloud regions where datalakes can be provisioned. Starts with AWS us-west-2;\n * additional regions are added as the control plane expands.\n */\nexport type DatalakeRegion = 'us-west-2' | 'us-east-1' | 'eu-west-1'\n\nexport const DATALAKE_REGIONS: readonly DatalakeRegion[] = ['us-west-2', 'us-east-1', 'eu-west-1'] as const\n\nexport const DEFAULT_REGION: DatalakeRegion = 'us-west-2'\n", "/** Audience used by browser wallet JWTs for control-plane operations. */\nexport const DATALAKE_CONTROL_AUDIENCE = 'aries-datalake-control'\n\n/** Schema required on every wallet sign-in JWT accepted by Aries. */\nexport const DATALAKE_WALLET_JWT_SCHEMA = 'network.xyo.auth.signin'\n\n/** Browser operations intentionally exposed by the datalake control plane. */\nexport const DATALAKE_CONTROL_OPERATIONS = [\n 'datalake:create',\n 'datalake:describe',\n 'datalake:list',\n] as const\n\n/** Browser operations intentionally exposed by the datalake data plane. */\nexport const DATALAKE_DATA_OPERATIONS = [\n 'payload:append',\n 'payload:read',\n 'usage:read',\n] as const\n\nexport type DatalakeControlOperation = typeof DATALAKE_CONTROL_OPERATIONS[number]\nexport type DatalakeDataOperation = typeof DATALAKE_DATA_OPERATIONS[number]\nexport type DatalakeOperation = DatalakeControlOperation | DatalakeDataOperation\n\n/** Canonical complete scope strings used when requesting browser wallet approval. */\nexport const DATALAKE_CONTROL_SCOPE = DATALAKE_CONTROL_OPERATIONS.join(' ')\nexport const DATALAKE_DATA_SCOPE = DATALAKE_DATA_OPERATIONS.join(' ')\n\nexport function isDatalakeControlOperation(value: string): value is DatalakeControlOperation {\n return (DATALAKE_CONTROL_OPERATIONS as readonly string[]).includes(value)\n}\n\nexport function isDatalakeDataOperation(value: string): value is DatalakeDataOperation {\n return (DATALAKE_DATA_OPERATIONS as readonly string[]).includes(value)\n}\n", "/**\n * Datalake service tiers. Each tier maps to a storage driver + resource profile\n * on the control plane: small/medium use the shared hot pool, large gets a\n * dedicated RestDataLake pod, archive uses the S3 + DynamoDB cold driver.\n */\nexport type DatalakeTier = 'small' | 'medium' | 'large' | 'archive'\n\nexport const DATALAKE_TIERS: readonly DatalakeTier[] = ['small', 'medium', 'large', 'archive'] as const\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["createHmac", "createHmac", "createHmac", "randomBytes", "base64UrlEncode", "randomBytes", "createHmac"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AclEntry } from '../types/AclEntry.ts';
|
|
2
|
+
import type { DatalakeDescriptor } from '../types/DatalakeDescriptor.ts';
|
|
3
|
+
import type { DatalakePrincipal, DatalakeRole } from '../types/Principal.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Storage interface for the control plane. Lives in `datalake-core` so
|
|
6
|
+
* decorators (`@ariestools/aries-datalake-k8s-bridge`) and alternate
|
|
7
|
+
* implementations can depend on the contract without taking a dependency
|
|
8
|
+
* on the full control plane.
|
|
9
|
+
*
|
|
10
|
+
* The in-memory implementation is used in development and for tests; a
|
|
11
|
+
* Postgres-backed implementation handles production. Both ship in
|
|
12
|
+
* `@ariestools/aries-datalake-control`.
|
|
13
|
+
*/
|
|
14
|
+
export interface DatalakeStore {
|
|
15
|
+
/** Find by globally unique id (no owner scoping). Used by the data plane. */
|
|
16
|
+
findById(id: string): Promise<DatalakeDescriptor | undefined>;
|
|
17
|
+
/** Find by id or name, scoped to a single owner. */
|
|
18
|
+
findByIdOrName(ownerId: string, idOrName: string): Promise<DatalakeDescriptor | undefined>;
|
|
19
|
+
/** Insert a fresh descriptor. Caller fills in id/timestamps. */
|
|
20
|
+
insert(descriptor: DatalakeDescriptor): Promise<void>;
|
|
21
|
+
/** Return all datalakes owned by the given user. */
|
|
22
|
+
listByOwner(ownerId: string): Promise<DatalakeDescriptor[]>;
|
|
23
|
+
/** Remove a datalake. */
|
|
24
|
+
remove(id: string): Promise<void>;
|
|
25
|
+
/** Drop an ACL entry by principal. */
|
|
26
|
+
removeAcl(id: string, principal: DatalakePrincipal): Promise<DatalakeDescriptor>;
|
|
27
|
+
/** Replace an existing descriptor (full update). */
|
|
28
|
+
update(descriptor: DatalakeDescriptor): Promise<void>;
|
|
29
|
+
/** Add or overwrite an ACL entry on a datalake, returning the updated descriptor. */
|
|
30
|
+
upsertAcl(id: string, entry: AclEntry): Promise<DatalakeDescriptor>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Roles considered higher privilege than others. Used when merging grants.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ROLE_RANK: Record<DatalakeRole, number>;
|
|
36
|
+
//# sourceMappingURL=DatalakeStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatalakeStore.d.ts","sourceRoot":"","sources":["../../../src/store/DatalakeStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAE5E;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAA;IAC7D,oDAAoD;IACpD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAA;IAC1F,gEAAgE;IAChE,MAAM,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD,oDAAoD;IACpD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAA;IAC3D,yBAAyB;IACzB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,sCAAsC;IACtC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAChF,oDAAoD;IACpD,MAAM,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD,qFAAqF;IACrF,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CACpE;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAGlD,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DatalakePrincipal, DatalakeRole } from './Principal.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A single grant on a datalake. The control plane stores one row per
|
|
4
|
+
* (datalakeId, principal) pair; `role` is the highest privilege granted.
|
|
5
|
+
*/
|
|
6
|
+
export interface AclEntry {
|
|
7
|
+
/** ISO-8601 timestamp of when the grant was created. */
|
|
8
|
+
grantedAt: string;
|
|
9
|
+
/** Optional user id of the grantor. */
|
|
10
|
+
grantedBy?: string;
|
|
11
|
+
principal: DatalakePrincipal;
|
|
12
|
+
role: DatalakeRole;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=AclEntry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AclEntry.d.ts","sourceRoot":"","sources":["../../../src/types/AclEntry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAErE;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,iBAAiB,CAAA;IAC5B,IAAI,EAAE,YAAY,CAAA;CACnB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** JSON values accepted and returned by the browser datalake wire API. */
|
|
2
|
+
export type JsonPrimitive = boolean | null | number | string;
|
|
3
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
|
|
4
|
+
export interface JsonObject {
|
|
5
|
+
[key: string]: JsonValue;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Browser wire representation of an XYO payload. Canonical protocol
|
|
9
|
+
* validation and storage remain responsibilities of the Node data plane.
|
|
10
|
+
*/
|
|
11
|
+
export interface Payload {
|
|
12
|
+
[key: string]: JsonValue | undefined;
|
|
13
|
+
/** Optional client-side lineage metadata. */
|
|
14
|
+
$sources?: string[];
|
|
15
|
+
/** XYO payload schema identifier. */
|
|
16
|
+
schema: string;
|
|
17
|
+
}
|
|
18
|
+
/** Storage metadata serialized by the datalake data plane. */
|
|
19
|
+
export interface StorageMeta {
|
|
20
|
+
_dataHash: string;
|
|
21
|
+
_hash: string;
|
|
22
|
+
_sequence: string;
|
|
23
|
+
}
|
|
24
|
+
export type WithStorageMeta<T extends Payload = Payload> = T & StorageMeta;
|
|
25
|
+
/** A payload as it leaves the data plane after archivist processing. */
|
|
26
|
+
export type StoredPayload = WithStorageMeta<Payload>;
|
|
27
|
+
export interface NextPayloadsOptions {
|
|
28
|
+
cursor?: string;
|
|
29
|
+
limit?: number;
|
|
30
|
+
order?: 'asc' | 'desc';
|
|
31
|
+
schemas?: string[];
|
|
32
|
+
}
|
|
33
|
+
export type NextPayloadsResponse = StoredPayload[];
|
|
34
|
+
export type InsertPayloadsBody = Payload | Payload[];
|
|
35
|
+
export type InsertPayloadsResponse = StoredPayload[];
|
|
36
|
+
export interface DatalakeInsertSummary {
|
|
37
|
+
duplicates: number;
|
|
38
|
+
rejected: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface InsertPayloadsResult {
|
|
41
|
+
inserted: StoredPayload[];
|
|
42
|
+
summary: DatalakeInsertSummary;
|
|
43
|
+
}
|
|
44
|
+
export interface NextPayloadsResult {
|
|
45
|
+
nextCursor?: string;
|
|
46
|
+
payloads: StoredPayload[];
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=BrowserPayload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserPayload.d.ts","sourceRoot":"","sources":["../../../src/types/BrowserPayload.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAC5D,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,SAAS,EAAE,CAAA;AAEhE,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;IACpC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,CAAC,GAAG,WAAW,CAAA;AAE1E,wEAAwE;AACxE,MAAM,MAAM,aAAa,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;AAEpD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,MAAM,oBAAoB,GAAG,aAAa,EAAE,CAAA;AAClD,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,OAAO,EAAE,CAAA;AACpD,MAAM,MAAM,sBAAsB,GAAG,aAAa,EAAE,CAAA;AAEpD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,OAAO,EAAE,qBAAqB,CAAA;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,aAAa,EAAE,CAAA;CAC1B"}
|