@volter/world-core 3.0.156 → 3.0.158
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/attach.cjs +2 -0
- package/capability-decision.cjs +6 -4
- package/dist/attach.cjs +2 -0
- package/dist/capability-decision.cjs +6 -4
- package/dist/generated/pack-facts.json +77 -8
- package/dist/network-policy.cjs +11 -1
- package/dist/network-policy.d.cts +1 -0
- package/dist/pack-facts.d.cts +3 -0
- package/dist/required-credential-transport.cjs +29 -0
- package/dist/required-credential-transport.d.cts +3 -0
- package/dist/src/blob-store.d.ts +16 -0
- package/dist/src/blob-store.js +76 -1
- package/dist/src/derived-core.d.ts +72 -6
- package/dist/src/derived-core.js +192 -51
- package/dist/src/derived-real.js +97 -15
- package/dist/src/derived.d.ts +5 -0
- package/dist/src/executor.d.ts +7 -0
- package/dist/src/executor.js +15 -1
- package/dist/src/git/lfs.d.ts +13 -3
- package/dist/src/git/lfs.js +41 -9
- package/dist/src/head.d.ts +2 -0
- package/dist/src/head.js +6 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/live/ingest.js +9 -13
- package/dist/src/live/rtmp.js +7 -5
- package/dist/src/live/segmenter.js +7 -1
- package/dist/src/machines.js +5 -1
- package/dist/src/managed-go-programs.d.ts +55 -0
- package/dist/src/managed-go-programs.js +133 -0
- package/dist/src/mongodb/stream.d.ts +23 -0
- package/dist/src/pack-fetch.d.ts +6 -1
- package/dist/src/pack-fetch.js +53 -7
- package/dist/src/pack-paths.d.ts +2 -1
- package/dist/src/pack-paths.js +6 -1
- package/dist/src/resource-blob.d.ts +4 -0
- package/dist/src/resource-blob.js +8 -1
- package/dist/src/runtime.d.ts +3 -3
- package/dist/src/runtime.js +2 -2
- package/dist/src/serve-http.js +53 -3
- package/dist/src/sockets.d.ts +4 -1
- package/dist/src/sockets.js +59 -6
- package/dist/src/vendor-call.js +58 -0
- package/dist/src/world-store.d.ts +15 -1
- package/dist/src/world-store.js +78 -2
- package/generated/pack-facts.json +77 -8
- package/network-policy.cjs +11 -1
- package/network-policy.d.cts +1 -0
- package/pack-facts.d.cts +3 -0
- package/package.json +8 -1
- package/required-credential-transport.cjs +29 -0
- package/required-credential-transport.d.cts +3 -0
- package/src/blob-store.ts +59 -1
- package/src/derived-core.ts +215 -51
- package/src/derived-real.ts +80 -14
- package/src/derived.ts +6 -1
- package/src/executor.ts +17 -3
- package/src/git/lfs.ts +40 -9
- package/src/head.ts +6 -0
- package/src/index.ts +1 -0
- package/src/live/ingest.ts +7 -3
- package/src/live/rtmp.ts +7 -5
- package/src/live/segmenter.ts +7 -1
- package/src/machines.ts +5 -1
- package/src/managed-go-programs.ts +146 -0
- package/src/mongodb/stream.ts +10 -0
- package/src/pack-fetch.ts +50 -9
- package/src/pack-paths.ts +5 -1
- package/src/resource-blob.ts +9 -1
- package/src/runtime.ts +4 -2
- package/src/serve-http.ts +43 -3
- package/src/sockets.ts +39 -8
- package/src/vendor-call.ts +54 -0
- package/src/world-store.ts +66 -2
package/attach.cjs
CHANGED
|
@@ -35,6 +35,8 @@ if (ref) {
|
|
|
35
35
|
throw new Error(`[twin-attach] could not fetch the world manifest from ${url}: ${(probe.stderr || '').trim()}`);
|
|
36
36
|
}
|
|
37
37
|
const manifest = JSON.parse(probe.stdout);
|
|
38
|
+
if (manifest === null || typeof manifest !== 'object' || Array.isArray(manifest)) throw new Error('[twin-attach] remote world returned no manifest');
|
|
39
|
+
if (Object.hasOwn(manifest, 'requiredCredentials')) throw new Error('[twin-attach] required credential remote transport is unsupported; refusing before bridges or injection');
|
|
38
40
|
const { worldAttachmentNetworkEnv, worldStrictEgress } = require('./network-policy.cjs');
|
|
39
41
|
Object.assign(process.env, worldAttachmentNetworkEnv(ref, manifest.network, process.env, worldStrictEgress(manifest.env ?? {})));
|
|
40
42
|
// a served World's twins as loopback listeners that add its key (./http-bridge.cjs), so a client with its own HTTP
|
package/capability-decision.cjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// scoped proxy, a tab's relay, a native image run's Seatbelt profile) enforces this answer and decides nothing itself.
|
|
5
5
|
// Pure: no I/O, no environment and nothing at import. What only the substrate can observe (the twin route, the vendors
|
|
6
6
|
// sharing a host, which loopback ports are the World's doors or the run's own listeners) arrives as `facts`.
|
|
7
|
-
const { allowsWorldNetworkEgress, isWorldInternalHost, worldNetworkNeedsPathInspection, worldNetworkUsesPassthrough } = require('./network-policy.cjs');
|
|
7
|
+
const { allowsWorldNetworkEgress, explicitlyGrantsWorldNetworkEgress, isWorldInternalHost, worldNetworkNeedsPathInspection, worldNetworkUsesPassthrough } = require('./network-policy.cjs');
|
|
8
8
|
|
|
9
9
|
/** The declaration of a guest with no declaration format: an ordinary host process, which can reach what the host can.
|
|
10
10
|
* Its own loopback is what it observably opened (`facts.ownListener`). A substrate whose guest can declare never uses it. */
|
|
@@ -135,7 +135,8 @@ function unlisted(url, target, method, { declaration, grant = {} }) {
|
|
|
135
135
|
if (grant.strict === true) return refuse(capability, target, 'grant', 'sealed', `this sandbox refuses untwinned host ${url.hostname}`, declaration);
|
|
136
136
|
if (grant.unreadable) return refuse(capability, target, 'grant', 'policy-unreadable', `the World network policy is unreadable (${grant.unreadable}); refusing ${url.hostname}`, declaration);
|
|
137
137
|
if (grant.policy && !allowsWorldNetworkEgress(grant.policy, url, method ?? 'GET')) {
|
|
138
|
-
|
|
138
|
+
// a tunnel (CONNECT) shows no method: the refusal names what was asked, never a method nobody sent
|
|
139
|
+
return refuse(capability, target, 'grant', 'not-granted', `the World network policy does not allow ${method ? `${String(method).toUpperCase()} ` : ''}${url.origin}`, declaration);
|
|
139
140
|
}
|
|
140
141
|
return null;
|
|
141
142
|
}
|
|
@@ -254,9 +255,10 @@ function decideRead(request, { declaration, facts = {} }) {
|
|
|
254
255
|
return refuse(capability, path, 'declaration', 'read-undeclared', `in Docker this container cannot read ${path}: it sees its own filesystem, the World's files and its declared mounts; declare a mount`, declaration);
|
|
255
256
|
}
|
|
256
257
|
|
|
257
|
-
/** Whether the World's build-phase grant lists `url` itself, which then wins over a twin of its host.
|
|
258
|
+
/** Whether the World's build-phase grant lists `url` itself, which then wins over a twin of its host. The unlisted
|
|
259
|
+
* public build reads do not: on a twinned host the twin answers them. */
|
|
258
260
|
function publicBuildGrant(grant, url) {
|
|
259
|
-
return grant.strict !== true && !!grant.policy && grant.policy.phase === 'build' &&
|
|
261
|
+
return grant.strict !== true && !!grant.policy && grant.policy.phase === 'build' && explicitlyGrantsWorldNetworkEgress(grant.policy, url);
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
/** A path the guest writes as its data (a `VOLUME`). Declared, it passes run-scoped: writable for the run, not outliving
|
package/dist/attach.cjs
CHANGED
|
@@ -35,6 +35,8 @@ if (ref) {
|
|
|
35
35
|
throw new Error(`[twin-attach] could not fetch the world manifest from ${url}: ${(probe.stderr || '').trim()}`);
|
|
36
36
|
}
|
|
37
37
|
const manifest = JSON.parse(probe.stdout);
|
|
38
|
+
if (manifest === null || typeof manifest !== 'object' || Array.isArray(manifest)) throw new Error('[twin-attach] remote world returned no manifest');
|
|
39
|
+
if (Object.hasOwn(manifest, 'requiredCredentials')) throw new Error('[twin-attach] required credential remote transport is unsupported; refusing before bridges or injection');
|
|
38
40
|
const { worldAttachmentNetworkEnv, worldStrictEgress } = require('./network-policy.cjs');
|
|
39
41
|
Object.assign(process.env, worldAttachmentNetworkEnv(ref, manifest.network, process.env, worldStrictEgress(manifest.env ?? {})));
|
|
40
42
|
// a served World's twins as loopback listeners that add its key (./http-bridge.cjs), so a client with its own HTTP
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// scoped proxy, a tab's relay, a native image run's Seatbelt profile) enforces this answer and decides nothing itself.
|
|
5
5
|
// Pure: no I/O, no environment and nothing at import. What only the substrate can observe (the twin route, the vendors
|
|
6
6
|
// sharing a host, which loopback ports are the World's doors or the run's own listeners) arrives as `facts`.
|
|
7
|
-
const { allowsWorldNetworkEgress, isWorldInternalHost, worldNetworkNeedsPathInspection, worldNetworkUsesPassthrough } = require('./network-policy.cjs');
|
|
7
|
+
const { allowsWorldNetworkEgress, explicitlyGrantsWorldNetworkEgress, isWorldInternalHost, worldNetworkNeedsPathInspection, worldNetworkUsesPassthrough } = require('./network-policy.cjs');
|
|
8
8
|
|
|
9
9
|
/** The declaration of a guest with no declaration format: an ordinary host process, which can reach what the host can.
|
|
10
10
|
* Its own loopback is what it observably opened (`facts.ownListener`). A substrate whose guest can declare never uses it. */
|
|
@@ -135,7 +135,8 @@ function unlisted(url, target, method, { declaration, grant = {} }) {
|
|
|
135
135
|
if (grant.strict === true) return refuse(capability, target, 'grant', 'sealed', `this sandbox refuses untwinned host ${url.hostname}`, declaration);
|
|
136
136
|
if (grant.unreadable) return refuse(capability, target, 'grant', 'policy-unreadable', `the World network policy is unreadable (${grant.unreadable}); refusing ${url.hostname}`, declaration);
|
|
137
137
|
if (grant.policy && !allowsWorldNetworkEgress(grant.policy, url, method ?? 'GET')) {
|
|
138
|
-
|
|
138
|
+
// a tunnel (CONNECT) shows no method: the refusal names what was asked, never a method nobody sent
|
|
139
|
+
return refuse(capability, target, 'grant', 'not-granted', `the World network policy does not allow ${method ? `${String(method).toUpperCase()} ` : ''}${url.origin}`, declaration);
|
|
139
140
|
}
|
|
140
141
|
return null;
|
|
141
142
|
}
|
|
@@ -254,9 +255,10 @@ function decideRead(request, { declaration, facts = {} }) {
|
|
|
254
255
|
return refuse(capability, path, 'declaration', 'read-undeclared', `in Docker this container cannot read ${path}: it sees its own filesystem, the World's files and its declared mounts; declare a mount`, declaration);
|
|
255
256
|
}
|
|
256
257
|
|
|
257
|
-
/** Whether the World's build-phase grant lists `url` itself, which then wins over a twin of its host.
|
|
258
|
+
/** Whether the World's build-phase grant lists `url` itself, which then wins over a twin of its host. The unlisted
|
|
259
|
+
* public build reads do not: on a twinned host the twin answers them. */
|
|
258
260
|
function publicBuildGrant(grant, url) {
|
|
259
|
-
return grant.strict !== true && !!grant.policy && grant.policy.phase === 'build' &&
|
|
261
|
+
return grant.strict !== true && !!grant.policy && grant.policy.phase === 'build' && explicitlyGrantsWorldNetworkEgress(grant.policy, url);
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
/** A path the guest writes as its data (a `VOLUME`). Declared, it passes run-scoped: writable for the run, not outliving
|
|
@@ -221,6 +221,50 @@
|
|
|
221
221
|
},
|
|
222
222
|
"serveExport": "createAxiomTwinServer"
|
|
223
223
|
},
|
|
224
|
+
"bitly": {
|
|
225
|
+
"adoption": {
|
|
226
|
+
"sdks": [],
|
|
227
|
+
"envStems": [
|
|
228
|
+
"BITLY",
|
|
229
|
+
"BUFFER_BITLY"
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
"hosts": [
|
|
233
|
+
{
|
|
234
|
+
"host": "api-ssl.bitly.com"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"host": "bitly.com",
|
|
238
|
+
"pathPattern": "^/oauth/authorize/?$"
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
"archetype": "crud",
|
|
242
|
+
"protocol": {
|
|
243
|
+
"declared": "3",
|
|
244
|
+
"major": 3,
|
|
245
|
+
"standing": "current"
|
|
246
|
+
},
|
|
247
|
+
"transport": "rest",
|
|
248
|
+
"resources": [
|
|
249
|
+
"group",
|
|
250
|
+
"bitlink"
|
|
251
|
+
],
|
|
252
|
+
"endpointEnvNone": "Dub uses literal vendor URLs and native fetch; the World routes them by host.",
|
|
253
|
+
"credentialDoor": {
|
|
254
|
+
"path": "/_twin/signup",
|
|
255
|
+
"body": {
|
|
256
|
+
"email": "publisher@example.test",
|
|
257
|
+
"password": "throwaway-bitly-password",
|
|
258
|
+
"login": "publisher"
|
|
259
|
+
},
|
|
260
|
+
"fill": {
|
|
261
|
+
"BITLY_CLIENT_ID": "client_id",
|
|
262
|
+
"BITLY_CLIENT_SECRET": "client_secret",
|
|
263
|
+
"BUFFER_BITLY_API_KEY": "access_token"
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"serveExport": "createBitlyTwinServer"
|
|
267
|
+
},
|
|
224
268
|
"cerebras": {
|
|
225
269
|
"adoption": {
|
|
226
270
|
"sdks": [
|
|
@@ -270,6 +314,9 @@
|
|
|
270
314
|
{
|
|
271
315
|
"host": "api.clerk.com"
|
|
272
316
|
},
|
|
317
|
+
{
|
|
318
|
+
"host": "dashboard.clerk.com"
|
|
319
|
+
},
|
|
273
320
|
{
|
|
274
321
|
"suffix": ".clerk.accounts.dev"
|
|
275
322
|
},
|
|
@@ -594,7 +641,7 @@
|
|
|
594
641
|
},
|
|
595
642
|
"transport": "rest",
|
|
596
643
|
"resources": [],
|
|
597
|
-
"endpointEnvNone": "
|
|
644
|
+
"endpointEnvNone": "No scanned application calls Deepgram; the World claims api.deepgram.com",
|
|
598
645
|
"serveExport": "createDeepgramTwinServer"
|
|
599
646
|
},
|
|
600
647
|
"discord": {
|
|
@@ -842,7 +889,7 @@
|
|
|
842
889
|
},
|
|
843
890
|
"transport": "rest",
|
|
844
891
|
"resources": [],
|
|
845
|
-
"endpointEnvNone": "
|
|
892
|
+
"endpointEnvNone": "the customer life calls https://fal.run/fal-ai/<model>; the World claims the hosts",
|
|
846
893
|
"credentialDoor": {
|
|
847
894
|
"path": "/_twin/app-credentials",
|
|
848
895
|
"body": {},
|
|
@@ -1012,7 +1059,7 @@
|
|
|
1012
1059
|
},
|
|
1013
1060
|
{
|
|
1014
1061
|
"host": "github.com",
|
|
1015
|
-
"pathPattern": "^/login|^/session|^/settings/|^/organizations/|^/apps/|^/[^/]+/[^/]+/(?:info/refs|git-upload-pack|git-receive-pack)$|^/[^/]+/[^/]+\\.git
|
|
1062
|
+
"pathPattern": "^/login|^/session|^/settings/|^/organizations/|^/apps/|^/[^/]+/[^/]+/(?:info/refs|git-upload-pack|git-receive-pack)$|^/[^/]+/[^/]+\\.git/|^/[^/]+/[^/]+/?$|^/[^/]+/[^/]+/(?:issues|pulls?|blob|raw|tree|commits?|compare)(?:/|$)"
|
|
1016
1063
|
}
|
|
1017
1064
|
],
|
|
1018
1065
|
"archetype": "crud",
|
|
@@ -1088,7 +1135,7 @@
|
|
|
1088
1135
|
},
|
|
1089
1136
|
{
|
|
1090
1137
|
"host": "fonts.googleapis.com",
|
|
1091
|
-
"pathPattern": "^/
|
|
1138
|
+
"pathPattern": "^/css2?$"
|
|
1092
1139
|
},
|
|
1093
1140
|
{
|
|
1094
1141
|
"host": "fonts.gstatic.com",
|
|
@@ -1417,8 +1464,7 @@
|
|
|
1417
1464
|
"host": "api.linear.app"
|
|
1418
1465
|
},
|
|
1419
1466
|
{
|
|
1420
|
-
"host": "linear.app"
|
|
1421
|
-
"pathPattern": "^/oauth/"
|
|
1467
|
+
"host": "linear.app"
|
|
1422
1468
|
}
|
|
1423
1469
|
],
|
|
1424
1470
|
"archetype": "crud",
|
|
@@ -1438,7 +1484,7 @@
|
|
|
1438
1484
|
"comment",
|
|
1439
1485
|
"issue_label"
|
|
1440
1486
|
],
|
|
1441
|
-
"endpointEnvNone": "Linear uses api.linear.app/graphql and linear.app
|
|
1487
|
+
"endpointEnvNone": "Linear uses api.linear.app/graphql and linear.app for its workspace and OAuth authorize page; the World claims those hosts",
|
|
1442
1488
|
"credentialDoor": {
|
|
1443
1489
|
"path": "/_twin/local-credentials",
|
|
1444
1490
|
"body": {},
|
|
@@ -2048,6 +2094,10 @@
|
|
|
2048
2094
|
"host": "reddit.com",
|
|
2049
2095
|
"demandPathPattern": "^/api/"
|
|
2050
2096
|
},
|
|
2097
|
+
{
|
|
2098
|
+
"host": "old.reddit.com",
|
|
2099
|
+
"demandPathPattern": "^/api/"
|
|
2100
|
+
},
|
|
2051
2101
|
{
|
|
2052
2102
|
"host": "reddit-uploaded-media.s3-accelerate.amazonaws.com"
|
|
2053
2103
|
},
|
|
@@ -2163,6 +2213,10 @@
|
|
|
2163
2213
|
},
|
|
2164
2214
|
{
|
|
2165
2215
|
"host": "inbound-cdn.resend.com"
|
|
2216
|
+
},
|
|
2217
|
+
{
|
|
2218
|
+
"host": "resend.com",
|
|
2219
|
+
"pathPattern": "^/emails(?:/|$)"
|
|
2166
2220
|
}
|
|
2167
2221
|
],
|
|
2168
2222
|
"archetype": "crud",
|
|
@@ -2319,6 +2373,9 @@
|
|
|
2319
2373
|
]
|
|
2320
2374
|
},
|
|
2321
2375
|
"hosts": [
|
|
2376
|
+
{
|
|
2377
|
+
"host": "app.slack.com"
|
|
2378
|
+
},
|
|
2322
2379
|
{
|
|
2323
2380
|
"host": "slack.com"
|
|
2324
2381
|
},
|
|
@@ -2697,7 +2754,7 @@
|
|
|
2697
2754
|
"publish",
|
|
2698
2755
|
"video"
|
|
2699
2756
|
],
|
|
2700
|
-
"endpointEnvNone": "
|
|
2757
|
+
"endpointEnvNone": "an app calls open.tiktokapis.com and sends people to www.tiktok.com; the World claims the hosts",
|
|
2701
2758
|
"credentialDoor": {
|
|
2702
2759
|
"path": "/_twin/app-credentials",
|
|
2703
2760
|
"body": {},
|
|
@@ -3280,6 +3337,18 @@
|
|
|
3280
3337
|
"_code_challenge",
|
|
3281
3338
|
"upload"
|
|
3282
3339
|
],
|
|
3340
|
+
"credentialDoor": {
|
|
3341
|
+
"path": "/_twin/app-credentials",
|
|
3342
|
+
"body": {},
|
|
3343
|
+
"fill": {
|
|
3344
|
+
"TWITTER_API_KEY": "consumer_key",
|
|
3345
|
+
"TWITTER_API_SECRET": "consumer_secret",
|
|
3346
|
+
"TWITTER_ACCESS_TOKEN": "access_token",
|
|
3347
|
+
"TWITTER_ACCESS_SECRET": "access_token_secret",
|
|
3348
|
+
"TWITTER_CLIENT_ID": "client_id",
|
|
3349
|
+
"TWITTER_CLIENT_SECRET": "client_secret"
|
|
3350
|
+
}
|
|
3351
|
+
},
|
|
3283
3352
|
"serveExport": "createXTwinServer"
|
|
3284
3353
|
},
|
|
3285
3354
|
"xai": {
|
package/dist/network-policy.cjs
CHANGED
|
@@ -140,6 +140,16 @@ function allowsWorldNetworkEgress(policy, value, method = 'GET') {
|
|
|
140
140
|
return granted(policy.egress, to) || (policy.phase === 'build' && policy.publicReads === true && PUBLIC_BUILD_READS.includes(to.url.origin));
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/** Whether the config grants this request by name (`egress` for a read, `writes` for any method): the grant that takes a
|
|
144
|
+
* build World's request past a twinned host's twin to its real origin. The unlisted public build reads are not one:
|
|
145
|
+
* on a twinned host the twin answers them (architecture, "Reads are granted generously, writes narrowly"). */
|
|
146
|
+
function explicitlyGrantsWorldNetworkEgress(policy, value, method = 'GET') {
|
|
147
|
+
const to = destination(value, true);
|
|
148
|
+
if (!to) return false;
|
|
149
|
+
if (granted(policy.writes ?? [], to)) return true;
|
|
150
|
+
return READ_METHODS.includes(String(method).toUpperCase()) && granted(policy.egress, to);
|
|
151
|
+
}
|
|
152
|
+
|
|
143
153
|
function worldNetworkUsesPassthrough(policy, value) {
|
|
144
154
|
const to = destination(value, true);
|
|
145
155
|
if (!to || policy.phase !== 'build') return false;
|
|
@@ -222,4 +232,4 @@ function worldEgressRefusal(value, env = process.env, method = 'GET') {
|
|
|
222
232
|
return answer.outcome === 'refuse' ? `${answer.reason} (${refusalSide(answer)})` : null;
|
|
223
233
|
}
|
|
224
234
|
|
|
225
|
-
module.exports = { worldAttachmentNetworkEnv, worldStrictEgress, worldGrantFromEnv, PUBLIC_BUILD_READS, READ_METHODS, intersectWorldNetwork, worldNetworkNeedsPathInspection, worldNetworkUsesPassthrough, isWorldInternalHost, worldEgressRefusal, WORLD_NETWORK_POLICY_ENV, normalizeWorldNetwork, createWorldNetworkPolicy, parseWorldNetworkPolicy, worldNetworkPolicyFromEnv, allowsWorldNetworkEgress };
|
|
235
|
+
module.exports = { worldAttachmentNetworkEnv, worldStrictEgress, worldGrantFromEnv, PUBLIC_BUILD_READS, READ_METHODS, intersectWorldNetwork, worldNetworkNeedsPathInspection, worldNetworkUsesPassthrough, isWorldInternalHost, worldEgressRefusal, WORLD_NETWORK_POLICY_ENV, normalizeWorldNetwork, createWorldNetworkPolicy, parseWorldNetworkPolicy, worldNetworkPolicyFromEnv, allowsWorldNetworkEgress, explicitlyGrantsWorldNetworkEgress };
|
|
@@ -10,6 +10,7 @@ export function worldNetworkPolicyFromEnv(env: Readonly<Record<string, string |
|
|
|
10
10
|
export const READ_METHODS: readonly string[];
|
|
11
11
|
export const PUBLIC_BUILD_READS: readonly string[];
|
|
12
12
|
export function allowsWorldNetworkEgress(policy: WorldNetworkPolicy, value: string | URL, method?: string): boolean;
|
|
13
|
+
export function explicitlyGrantsWorldNetworkEgress(policy: WorldNetworkPolicy, value: string | URL, method?: string): boolean;
|
|
13
14
|
export function isWorldInternalHost(hostname: string): boolean;
|
|
14
15
|
export function worldEgressRefusal(value: string | URL, env?: Readonly<Record<string, string | undefined>>, method?: string): string | null;
|
|
15
16
|
export function worldNetworkNeedsPathInspection(policy: WorldNetworkPolicy, value: string | URL): boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Metadata-only admission for native machine backings. Imports start no process and read no file.
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
exports.refuseRequiredMachineTransport = function refuseRequiredMachineTransport(source, transport) {
|
|
5
|
+
const path = source.VOLTER_WORLD_INSTANCE;
|
|
6
|
+
if (!path) {
|
|
7
|
+
if (source.VOLTER_WORLD) throw new Error('World machine execution has no retained credential declaration');
|
|
8
|
+
return; // No registered World declaration is asserted by the standalone provider.
|
|
9
|
+
}
|
|
10
|
+
const instance = JSON.parse(require('node:fs').readFileSync(path, 'utf8'));
|
|
11
|
+
if (!instance || typeof instance !== 'object' || Array.isArray(instance)
|
|
12
|
+
|| !instance.env || instance.env.VOLTER_WORLD_INSTANCE !== path || instance.env.VOLTER_WORLD !== source.VOLTER_WORLD
|
|
13
|
+
|| !Object.hasOwn(instance, 'requiredCredentials')) throw new Error('World machine boot lacks its own retained credential declaration');
|
|
14
|
+
const record = instance.requiredCredentials;
|
|
15
|
+
if (!record || typeof record !== 'object' || Array.isArray(record)
|
|
16
|
+
|| Object.keys(record).some(key => !['version', 'owners', 'valuesSha256'].includes(key))
|
|
17
|
+
|| !Object.hasOwn(record, 'version') || record.version !== 1 || !Object.hasOwn(record, 'owners')
|
|
18
|
+
|| !record.owners || typeof record.owners !== 'object' || Array.isArray(record.owners))
|
|
19
|
+
throw new Error('World machine credential declaration has an unsupported version or shape');
|
|
20
|
+
if (Object.keys(record.owners).length) {
|
|
21
|
+
const error = new Error(`${transport} does not carry required credential bindings`);
|
|
22
|
+
error.code = 'WORLD_MACHINE_CAPABILITY_GAP';
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
if (Object.hasOwn(record, 'valuesSha256')) {
|
|
26
|
+
const empty = require('node:crypto').createHash('sha256').update('[]').digest('hex');
|
|
27
|
+
if (record.valuesSha256 !== empty) throw new Error('World machine empty credential declaration changed');
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Refuse a registered World's required declarations before a backing with no credential transport acts.
|
|
2
|
+
* Reads only the actual instance named by the provider's own environment; standalone providers assert no World proof. */
|
|
3
|
+
export declare function refuseRequiredMachineTransport(source: Readonly<Record<string, string | undefined>>, transport: string): void;
|
package/dist/src/blob-store.d.ts
CHANGED
|
@@ -19,7 +19,22 @@ export interface BlobStore {
|
|
|
19
19
|
* whole. Optional: `readBlobRange` falls back to `get` (a whole read) for a backend without it —
|
|
20
20
|
* the hosted World's R2 store today. */
|
|
21
21
|
getRange?(key: string, start: number, endInclusive: number): Promise<Uint8Array | null>;
|
|
22
|
+
/** A body's chunks written to `key` as they arrive and hashed as they are written, kept (atomically, as `put`)
|
|
23
|
+
* only when `accept` holds for their size and SHA-256; otherwise nothing is kept. True when kept. So an upload
|
|
24
|
+
* of a large object (a model's weights) never holds more than a chunk. Optional: `writeBlobStream` collects
|
|
25
|
+
* the chunks and `put`s them for a backend without it. */
|
|
26
|
+
putStream?(key: string, chunks: AsyncIterable<Uint8Array>, accept: (written: BlobWritten) => boolean): Promise<boolean>;
|
|
22
27
|
}
|
|
28
|
+
/** What a streamed write wrote: its byte length and its SHA-256 (hex). */
|
|
29
|
+
export type BlobWritten = {
|
|
30
|
+
size: number;
|
|
31
|
+
sha256: string;
|
|
32
|
+
};
|
|
33
|
+
/** A body's chunks to `key` through the active store, kept when `accept` holds for what was written (the store's
|
|
34
|
+
* own streamed write where it has one, else the chunks collected and `put`). True when kept. */
|
|
35
|
+
export declare function writeBlobStream(key: string, chunks: AsyncIterable<Uint8Array>, accept: (written: BlobWritten) => boolean): Promise<boolean>;
|
|
36
|
+
/** A web stream's chunks, read by its reader (a browser's ReadableStream need not be async-iterable). */
|
|
37
|
+
export declare function streamChunks(body: ReadableStream<Uint8Array>): AsyncGenerator<Uint8Array>;
|
|
23
38
|
/** A range of the blob at `key` through the active store: native when the store has it, else sliced. */
|
|
24
39
|
export declare function readBlobRange(key: string, start: number, endInclusive: number): Promise<Uint8Array | null>;
|
|
25
40
|
/** Today's layout, verbatim: keys are filesystem paths. */
|
|
@@ -30,6 +45,7 @@ export declare class FsBlobStore implements BlobStore {
|
|
|
30
45
|
});
|
|
31
46
|
get(key: string): Promise<Uint8Array | null>;
|
|
32
47
|
put(key: string, bytes: Uint8Array): Promise<void>;
|
|
48
|
+
putStream(key: string, chunks: AsyncIterable<Uint8Array>, accept: (written: BlobWritten) => boolean): Promise<boolean>;
|
|
33
49
|
exists(key: string): Promise<boolean>;
|
|
34
50
|
remove(key: string): Promise<void>;
|
|
35
51
|
list(prefix: string): Promise<string[]>;
|
package/dist/src/blob-store.js
CHANGED
|
@@ -19,11 +19,50 @@
|
|
|
19
19
|
// local worlds change NOTHING — scrub, backup, and inspection all still see the same tree.
|
|
20
20
|
// A remote adapter maps the path to an object key (namespace prefix + path).
|
|
21
21
|
import { createHash, randomUUID } from 'node:crypto';
|
|
22
|
-
import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, renameSync, rmSync, statSync, writeFileSync, writeSync } from 'node:fs';
|
|
23
23
|
import { dirname, join } from 'node:path';
|
|
24
24
|
import { volterHome } from "./volter-home.js";
|
|
25
25
|
import { sharedBlobIndex } from "./shared-blob-index.js";
|
|
26
26
|
import { refuseReadOnlyWrite, writesRefused } from "./request-scope.js";
|
|
27
|
+
/** A body's chunks to `key` through the active store, kept when `accept` holds for what was written (the store's
|
|
28
|
+
* own streamed write where it has one, else the chunks collected and `put`). True when kept. */
|
|
29
|
+
export async function writeBlobStream(key, chunks, accept) {
|
|
30
|
+
const store = getActiveBlobStore();
|
|
31
|
+
if (store.putStream)
|
|
32
|
+
return store.putStream(key, chunks, accept);
|
|
33
|
+
const parts = [];
|
|
34
|
+
let size = 0;
|
|
35
|
+
for await (const chunk of chunks) {
|
|
36
|
+
parts.push(chunk);
|
|
37
|
+
size += chunk.byteLength;
|
|
38
|
+
}
|
|
39
|
+
const bytes = new Uint8Array(size);
|
|
40
|
+
let at = 0;
|
|
41
|
+
for (const part of parts) {
|
|
42
|
+
bytes.set(part, at);
|
|
43
|
+
at += part.byteLength;
|
|
44
|
+
}
|
|
45
|
+
if (!accept({ size, sha256: blobDigest(bytes) }))
|
|
46
|
+
return false;
|
|
47
|
+
await store.put(key, bytes);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
/** A web stream's chunks, read by its reader (a browser's ReadableStream need not be async-iterable). */
|
|
51
|
+
export async function* streamChunks(body) {
|
|
52
|
+
const reader = body.getReader();
|
|
53
|
+
try {
|
|
54
|
+
for (;;) {
|
|
55
|
+
const { done, value } = await reader.read();
|
|
56
|
+
if (done)
|
|
57
|
+
return;
|
|
58
|
+
if (value)
|
|
59
|
+
yield value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
reader.releaseLock();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
27
66
|
/** A range of the blob at `key` through the active store: native when the store has it, else sliced. */
|
|
28
67
|
export async function readBlobRange(key, start, endInclusive) {
|
|
29
68
|
// one clamping for every backend: a negative or non-finite start reads from 0, a range that ends
|
|
@@ -72,6 +111,40 @@ export class FsBlobStore {
|
|
|
72
111
|
rmSync(tmp, { force: true });
|
|
73
112
|
}
|
|
74
113
|
}
|
|
114
|
+
async putStream(key, chunks, accept) {
|
|
115
|
+
mkdirSync(dirname(key), { recursive: true });
|
|
116
|
+
const tmp = `${key}.tmp.${randomUUID()}`;
|
|
117
|
+
const hash = createHash('sha256');
|
|
118
|
+
let size = 0;
|
|
119
|
+
try {
|
|
120
|
+
const fd = openSync(tmp, 'wx');
|
|
121
|
+
try {
|
|
122
|
+
for await (const chunk of chunks) {
|
|
123
|
+
hash.update(chunk);
|
|
124
|
+
size += chunk.byteLength;
|
|
125
|
+
for (let at = 0; at < chunk.byteLength;)
|
|
126
|
+
at += writeSync(fd, chunk, at, chunk.byteLength - at);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
closeSync(fd);
|
|
131
|
+
}
|
|
132
|
+
const sha256 = hash.digest('hex');
|
|
133
|
+
if (!accept({ size, sha256 }))
|
|
134
|
+
return false;
|
|
135
|
+
renameSync(tmp, key);
|
|
136
|
+
if (this.options.sharedIndexRoot !== false) {
|
|
137
|
+
try {
|
|
138
|
+
sharedBlobIndex(this.options.sharedIndexRoot ?? join(volterHome(), 'blob-index-v1'), statSync(dirname(key)).dev, sha256).record(key);
|
|
139
|
+
}
|
|
140
|
+
catch { /* The index is an optimization, never a prerequisite for a blob write. */ }
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
rmSync(tmp, { force: true });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
75
148
|
async exists(key) {
|
|
76
149
|
return existsSync(key);
|
|
77
150
|
}
|
|
@@ -180,7 +253,9 @@ function readOnlyBlobs(store) {
|
|
|
180
253
|
exists: (key) => store.exists(key),
|
|
181
254
|
list: (prefix) => store.list(prefix),
|
|
182
255
|
size: (key) => store.size(key),
|
|
256
|
+
...(store.getRange ? { getRange: (key, start, endInclusive) => store.getRange(key, start, endInclusive) } : {}),
|
|
183
257
|
put: async () => { refuseReadOnlyWrite('blobs'); },
|
|
258
|
+
putStream: async () => { refuseReadOnlyWrite('blobs'); return false; },
|
|
184
259
|
remove: async () => { refuseReadOnlyWrite('blobs'); },
|
|
185
260
|
};
|
|
186
261
|
}
|