@molecule/api-code-sandbox-flyio 1.0.18 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +205 -32
- package/dist/api.d.ts +5 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +11 -0
- package/dist/egress.d.ts +97 -2
- package/dist/egress.d.ts.map +1 -1
- package/dist/egress.js +166 -6
- package/dist/flycast.d.ts +18 -7
- package/dist/flycast.d.ts.map +1 -1
- package/dist/flycast.js +18 -7
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +280 -11
- package/dist/storage.d.ts +8 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +17 -0
- package/dist/types.d.ts +52 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -4
package/dist/egress.js
CHANGED
|
@@ -46,11 +46,46 @@
|
|
|
46
46
|
* and that `name` is mandatory — which is why {@link DEFAULT_EGRESS_POLICY_NAME}
|
|
47
47
|
* is always sent.
|
|
48
48
|
*
|
|
49
|
+
* **THE PORT LIST IS THE WHOLE POLICY — there is no destination, and this was
|
|
50
|
+
* measured rather than inferred (2026-08-16, live API, throwaway app).** Every
|
|
51
|
+
* plausible destination field was POSTed and read back: on a RULE,
|
|
52
|
+
* `ipv6_cidrs` / `ipv4_cidrs` / `cidrs` / `destinations` / `apps` / `app` / `to`
|
|
53
|
+
* / `dst` are all accepted by the decoder and DROPPED — the stored rule comes
|
|
54
|
+
* back as `{action, direction, ports}` and nothing else. On the SELECTOR (which
|
|
55
|
+
* chooses which Machines a policy covers, i.e. the SOURCE, never a destination)
|
|
56
|
+
* only `all`, `machines` and `metadata` persist; `apps` is the one field the API
|
|
57
|
+
* answers about, with `400 {"error":"apps array not currently supported in
|
|
58
|
+
* selectors"}`. So an `allow tcp/5432` rule permits that Machine to reach port
|
|
59
|
+
* 5432 on EVERY host on the internet, and no arrangement of this object can say
|
|
60
|
+
* otherwise. {@link describePublicReach} states that in the verdict rather than
|
|
61
|
+
* leaving an operator to deduce it.
|
|
62
|
+
*
|
|
63
|
+
* **The derived private-service ports ARE load-bearing** (the open question this
|
|
64
|
+
* module and FLY-OPERATOR-SETUP §9 both carried since 2026-08-05). Measured the
|
|
65
|
+
* same day: with a policy allowing only `udp:53`, a Machine on a custom 6PN
|
|
66
|
+
* could resolve `molecule-pg-tenant.flycast` and `molecule-api.flycast` but
|
|
67
|
+
* every TCP connect to them was dropped, and re-applying `tcp:443` re-opened
|
|
68
|
+
* only 443. Fly's "policies do not affect traffic routed through the Fly Proxy"
|
|
69
|
+
* is about INGRESS; a Machine's egress TOWARD a Flycast address is filtered like
|
|
70
|
+
* any other. Dropping a declared service's port would therefore take out every
|
|
71
|
+
* database connection in the fleet.
|
|
72
|
+
*
|
|
49
73
|
* @module
|
|
50
74
|
*/
|
|
51
75
|
import { shellQuote } from './utilities.js';
|
|
52
76
|
/** Default name of the egress policy this provider owns on each sandbox app. */
|
|
53
77
|
export const DEFAULT_EGRESS_POLICY_NAME = 'molecule-sandbox-egress';
|
|
78
|
+
/**
|
|
79
|
+
* UDP port name resolution runs on.
|
|
80
|
+
*
|
|
81
|
+
* Excluded from {@link describePublicReach}'s "reachable on" list because DNS is
|
|
82
|
+
* not a TCP channel and is a residual of its own (a resolver forwards queries
|
|
83
|
+
* upstream on the platform's behalf, which is why DNS tunnelling is tracked
|
|
84
|
+
* separately in `docs/sandbox-egress-enforcement.md` rather than as an open
|
|
85
|
+
* port). It is never excluded from {@link unexpectedPolicyPorts}: a policy that
|
|
86
|
+
* allows DNS this provider did not configure is still drift.
|
|
87
|
+
*/
|
|
88
|
+
const DNS_PORT = 53;
|
|
54
89
|
/**
|
|
55
90
|
* Probe exit status meaning at least one raw connection to a public IP
|
|
56
91
|
* SUCCEEDED. Deliberately not 1, so an interpreter error (which exits 1) can
|
|
@@ -175,6 +210,14 @@ export function buildEgressProbeCommand(targets, timeoutMs) {
|
|
|
175
210
|
`s.on('error',()=>done())}`;
|
|
176
211
|
return `node -e ${shellQuote(script)}`;
|
|
177
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Renders a port list the way an operator reads it back.
|
|
215
|
+
* @param ports - The ports.
|
|
216
|
+
* @returns `tcp/443, udp/53`.
|
|
217
|
+
*/
|
|
218
|
+
function formatPorts(ports) {
|
|
219
|
+
return ports.map((port) => `${port.protocol}/${port.port}`).join(', ');
|
|
220
|
+
}
|
|
178
221
|
/**
|
|
179
222
|
* Describes the policy this provider applied, for the verdict message.
|
|
180
223
|
* @param policyPorts - Allowed ports, or `undefined` when no policy is applied.
|
|
@@ -183,8 +226,55 @@ export function buildEgressProbeCommand(targets, timeoutMs) {
|
|
|
183
226
|
function describePolicy(policyPorts) {
|
|
184
227
|
if (!policyPorts)
|
|
185
228
|
return 'no Fly network policy applied by this provider';
|
|
186
|
-
|
|
187
|
-
|
|
229
|
+
return `a Fly network policy allowing egress on ${formatPorts(policyPorts)}`;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* States, in the verdict itself, what an allowed port actually permits.
|
|
233
|
+
*
|
|
234
|
+
* A Fly network policy has no destination (see the module description — measured
|
|
235
|
+
* against the live API, not inferred), so every allowed port is open to every
|
|
236
|
+
* host on the internet, not only to the private service it was derived for. That
|
|
237
|
+
* is the single most consequential thing about this mechanism and it was invisible
|
|
238
|
+
* everywhere it mattered: the probe attempts one port the policy denies, reports
|
|
239
|
+
* `filtered`, and an operator reasonably reads that as "nothing gets out".
|
|
240
|
+
*
|
|
241
|
+
* Naming it here means the residual is restated on every re-probe (the consumer
|
|
242
|
+
* re-verifies every 15 minutes) instead of living only in a document.
|
|
243
|
+
* @param policyPorts - Allowed ports, or `undefined` when no policy is applied.
|
|
244
|
+
* @returns A sentence naming the public reach, or `''` when there is nothing to
|
|
245
|
+
* say (no policy, or nothing but UDP/53 — DNS is a documented residual of its
|
|
246
|
+
* own and not a TCP channel).
|
|
247
|
+
*/
|
|
248
|
+
export function describePublicReach(policyPorts) {
|
|
249
|
+
const reachable = (policyPorts ?? []).filter((port) => port.protocol !== 'udp' || port.port !== DNS_PORT);
|
|
250
|
+
if (reachable.length === 0)
|
|
251
|
+
return '';
|
|
252
|
+
return (`Every allowed port is open to EVERY host, not only to the private service it exists for — ` +
|
|
253
|
+
`a Fly network policy matches protocol and port with no destination field of any kind — so ` +
|
|
254
|
+
`code in this app can still reach an arbitrary public host on ${formatPorts(reachable)}. ` +
|
|
255
|
+
'Route what must be filtered by host through an egress proxy on one of those ports.');
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Finds ports the policy Fly holds allows that this provider never configured.
|
|
259
|
+
*
|
|
260
|
+
* The raw-connect probe can only speak for the targets it was handed, so a
|
|
261
|
+
* policy widened outside this provider — edited by hand to unblock something, or
|
|
262
|
+
* left behind by an older build — is invisible to it: the probe's own port stays
|
|
263
|
+
* denied and the verdict stays `filtered` while a port nothing here asked for is
|
|
264
|
+
* open to the whole internet. Comparing what Fly holds against what this
|
|
265
|
+
* provider intended is the one check that can catch that, and it is why the
|
|
266
|
+
* verdict reads the policy back rather than trusting the write.
|
|
267
|
+
* @param applied - Ports in the policy Fly actually holds, or `undefined` when
|
|
268
|
+
* the readback found nothing (in which case there is nothing to compare).
|
|
269
|
+
* @param intended - Ports this provider configured, or `undefined` when it
|
|
270
|
+
* applies no policy.
|
|
271
|
+
* @returns The applied ports with no counterpart in `intended`, in order.
|
|
272
|
+
*/
|
|
273
|
+
export function unexpectedPolicyPorts(applied, intended) {
|
|
274
|
+
if (!applied)
|
|
275
|
+
return [];
|
|
276
|
+
const wanted = intended ?? [];
|
|
277
|
+
return applied.filter((port) => !wanted.some((own) => own.protocol === port.protocol && own.port === port.port));
|
|
188
278
|
}
|
|
189
279
|
/**
|
|
190
280
|
* Maps a probe's exit status onto an {@link EgressVerdict}.
|
|
@@ -211,6 +301,24 @@ function describePolicy(policyPorts) {
|
|
|
211
301
|
export function verdictForProbeExit(exitCode, context) {
|
|
212
302
|
const targets = context.targets.map(formatEgressProbeTarget).join(', ');
|
|
213
303
|
const policy = describePolicy(context.policyPorts);
|
|
304
|
+
const reach = describePublicReach(context.appliedPolicyPorts ?? context.policyPorts);
|
|
305
|
+
// Drift, and it outranks a blocked probe: the probe attempts the targets it
|
|
306
|
+
// was handed, so a port widened outside this provider is open with nothing
|
|
307
|
+
// observing it. `open` is the honest state — something out there permits
|
|
308
|
+
// traffic this provider never configured.
|
|
309
|
+
const unexpected = unexpectedPolicyPorts(context.appliedPolicyPorts, context.policyPorts);
|
|
310
|
+
if (unexpected.length > 0) {
|
|
311
|
+
return {
|
|
312
|
+
state: 'open',
|
|
313
|
+
detail: (`The Fly network policy in force on app "${context.app}" allows ` +
|
|
314
|
+
`${formatPorts(unexpected)}, which this provider never configured (it applies ` +
|
|
315
|
+
`${policy}). ${describePublicReach(context.appliedPolicyPorts)}`).trim(),
|
|
316
|
+
remediation: 'Something outside this provider widened the policy. Inspect it with ' +
|
|
317
|
+
`\`GET /v1/apps/${context.app}/network_policies\`, remove the rule that does not belong, ` +
|
|
318
|
+
'and restart the Machines — Fly applies a policy at boot, so an already-running Machine ' +
|
|
319
|
+
'keeps the wider one until it is recreated.',
|
|
320
|
+
};
|
|
321
|
+
}
|
|
214
322
|
if (exitCode === EGRESS_PROBE_EXIT_REACHED) {
|
|
215
323
|
return {
|
|
216
324
|
state: 'open',
|
|
@@ -231,7 +339,7 @@ export function verdictForProbeExit(exitCode, context) {
|
|
|
231
339
|
return {
|
|
232
340
|
state: 'filtered',
|
|
233
341
|
detail: `Raw TCP connects to ${targets} from a Machine in Fly app "${context.app}" ` +
|
|
234
|
-
`(${policy}) were refused or timed out
|
|
342
|
+
`(${policy}) were refused or timed out.${reach ? ` ${reach}` : ''}`,
|
|
235
343
|
};
|
|
236
344
|
}
|
|
237
345
|
return {
|
|
@@ -267,6 +375,59 @@ function isRecord(value) {
|
|
|
267
375
|
* @returns The matching policy's id, or `undefined`.
|
|
268
376
|
*/
|
|
269
377
|
export function extractPolicyId(response, name) {
|
|
378
|
+
const entry = findPolicy(response, name);
|
|
379
|
+
return entry && typeof entry.id === 'string' && entry.id ? entry.id : undefined;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Reads back the egress ports the policy Fly ACTUALLY holds allows.
|
|
383
|
+
*
|
|
384
|
+
* The write is not the observation: this provider POSTs a policy, but what
|
|
385
|
+
* governs a Machine is whatever Fly has when that Machine boots — which may
|
|
386
|
+
* differ because a person, another tool or an older build changed it. Reading it
|
|
387
|
+
* back is what lets {@link unexpectedPolicyPorts} catch a policy wider than the
|
|
388
|
+
* one this provider configured, so `verifyEgress()` can fail on drift its raw
|
|
389
|
+
* connects were never going to attempt.
|
|
390
|
+
*
|
|
391
|
+
* Parsed as defensively as {@link extractPolicyId}, and for the same reason:
|
|
392
|
+
* neither Fly document specifies the LIST response shape. The live API answers
|
|
393
|
+
* with a bare array whose selector key is `netpolSelector` rather than the
|
|
394
|
+
* `selector` it accepts (verified 2026-08-16), which is exactly why nothing here
|
|
395
|
+
* depends on any key but `name` and `rules`.
|
|
396
|
+
* @param response - The raw parsed LIST response.
|
|
397
|
+
* @param name - The policy name to match.
|
|
398
|
+
* @returns The allowed egress ports, or `undefined` when no policy of that name
|
|
399
|
+
* is present or the shape is unrecognized. `undefined` means "nothing to
|
|
400
|
+
* compare", never "nothing is allowed".
|
|
401
|
+
*/
|
|
402
|
+
export function extractPolicyPorts(response, name) {
|
|
403
|
+
const entry = findPolicy(response, name);
|
|
404
|
+
if (!entry || !Array.isArray(entry.rules))
|
|
405
|
+
return undefined;
|
|
406
|
+
const ports = [];
|
|
407
|
+
for (const rule of entry.rules) {
|
|
408
|
+
if (!isRecord(rule) || rule.direction !== 'egress' || !Array.isArray(rule.ports))
|
|
409
|
+
continue;
|
|
410
|
+
for (const port of rule.ports) {
|
|
411
|
+
if (!isRecord(port))
|
|
412
|
+
continue;
|
|
413
|
+
const protocol = port.protocol;
|
|
414
|
+
const value = port.port;
|
|
415
|
+
if ((protocol !== 'tcp' && protocol !== 'udp') || typeof value !== 'number')
|
|
416
|
+
continue;
|
|
417
|
+
if (!ports.some((seen) => seen.protocol === protocol && seen.port === value)) {
|
|
418
|
+
ports.push({ protocol, port: value });
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return ports;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Finds the named policy in a LIST response.
|
|
426
|
+
* @param response - The raw parsed LIST response.
|
|
427
|
+
* @param name - The policy name to match.
|
|
428
|
+
* @returns The matching entry, or `undefined`.
|
|
429
|
+
*/
|
|
430
|
+
function findPolicy(response, name) {
|
|
270
431
|
const list = Array.isArray(response)
|
|
271
432
|
? response
|
|
272
433
|
: isRecord(response)
|
|
@@ -275,9 +436,8 @@ export function extractPolicyId(response, name) {
|
|
|
275
436
|
if (!Array.isArray(list))
|
|
276
437
|
return undefined;
|
|
277
438
|
for (const entry of list) {
|
|
278
|
-
if (isRecord(entry) && entry.name === name
|
|
279
|
-
return entry
|
|
280
|
-
}
|
|
439
|
+
if (isRecord(entry) && entry.name === name)
|
|
440
|
+
return entry;
|
|
281
441
|
}
|
|
282
442
|
return undefined;
|
|
283
443
|
}
|
package/dist/flycast.d.ts
CHANGED
|
@@ -115,14 +115,25 @@ export declare function parsePrivateServices(raw: string | undefined): FlyPrivat
|
|
|
115
115
|
* still exactly what they wrote plus the ports their own declarations require,
|
|
116
116
|
* and the applied policy is logged.
|
|
117
117
|
*
|
|
118
|
-
*
|
|
119
|
-
* policies only apply to traffic directly to and from Machines. They do
|
|
120
|
-
* affect traffic routed through the Fly Proxy"
|
|
118
|
+
* The addition is load-bearing — VERIFIED 2026-08-16, not inferred. Fly states
|
|
119
|
+
* "Network policies only apply to traffic directly to and from Machines. They do
|
|
120
|
+
* not affect traffic routed through the Fly Proxy"
|
|
121
121
|
* (https://fly.io/docs/machines/guides-examples/network-policies/), and Flycast
|
|
122
|
-
* traffic IS routed through Fly Proxy
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
122
|
+
* traffic IS routed through Fly Proxy, so it was an open question whether these
|
|
123
|
+
* ports did anything. Measured on a throwaway app with a Flycast address into
|
|
124
|
+
* its own 6PN: under a policy allowing only `udp:53`, `molecule-pg-tenant.flycast`
|
|
125
|
+
* and `molecule-api.flycast` both RESOLVED and every TCP connect to them was
|
|
126
|
+
* dropped; re-applying the policy with `tcp:443` opened 443 and nothing else.
|
|
127
|
+
* Fly's sentence is about INGRESS — a Machine's egress TOWARD a Flycast address
|
|
128
|
+
* is filtered like any other. Drop a declared service's port and every database
|
|
129
|
+
* connection in the fleet goes with it.
|
|
130
|
+
*
|
|
131
|
+
* The cost of each derived port, stated plainly because a Fly policy has no
|
|
132
|
+
* destination field of any kind (also measured — see the `egress.ts` module
|
|
133
|
+
* description): the port is opened to EVERY host on the internet, not only to
|
|
134
|
+
* the private service it was derived for. That residual cannot be closed at this
|
|
135
|
+
* layer; it is named in `verifyEgress()`'s verdict and in
|
|
136
|
+
* `docs/sandbox-egress-enforcement.md`.
|
|
126
137
|
* @param ports - The operator's configured ports, or `undefined` when no policy
|
|
127
138
|
* is being applied at all.
|
|
128
139
|
* @param services - The declared private services, or `undefined`.
|
package/dist/flycast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flycast.d.ts","sourceRoot":"","sources":["../src/flycast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEzE;;;;GAIG;AACH,eAAO,MAAM,cAAc,aAAa,CAAA;AAExC;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,kBAAkB,CAAA;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,eAAe,CAAA;AAKnD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,EAAE,GAAG,SAAS,CAqC7F;AAED
|
|
1
|
+
{"version":3,"file":"flycast.d.ts","sourceRoot":"","sources":["../src/flycast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEzE;;;;GAIG;AACH,eAAO,MAAM,cAAc,aAAa,CAAA;AAExC;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,kBAAkB,CAAA;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,eAAe,CAAA;AAKnD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,EAAE,GAAG,SAAS,CAqC7F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,oBAAoB,EAAE,GAAG,SAAS,EACzC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,SAAS,GACxC,oBAAoB,EAAE,GAAG,SAAS,CAUpC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAI1E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUnF;AA2BD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EACvC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,SAAS,GACxC,IAAI,CAwCN"}
|
package/dist/flycast.js
CHANGED
|
@@ -147,14 +147,25 @@ export function parsePrivateServices(raw) {
|
|
|
147
147
|
* still exactly what they wrote plus the ports their own declarations require,
|
|
148
148
|
* and the applied policy is logged.
|
|
149
149
|
*
|
|
150
|
-
*
|
|
151
|
-
* policies only apply to traffic directly to and from Machines. They do
|
|
152
|
-
* affect traffic routed through the Fly Proxy"
|
|
150
|
+
* The addition is load-bearing — VERIFIED 2026-08-16, not inferred. Fly states
|
|
151
|
+
* "Network policies only apply to traffic directly to and from Machines. They do
|
|
152
|
+
* not affect traffic routed through the Fly Proxy"
|
|
153
153
|
* (https://fly.io/docs/machines/guides-examples/network-policies/), and Flycast
|
|
154
|
-
* traffic IS routed through Fly Proxy
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
154
|
+
* traffic IS routed through Fly Proxy, so it was an open question whether these
|
|
155
|
+
* ports did anything. Measured on a throwaway app with a Flycast address into
|
|
156
|
+
* its own 6PN: under a policy allowing only `udp:53`, `molecule-pg-tenant.flycast`
|
|
157
|
+
* and `molecule-api.flycast` both RESOLVED and every TCP connect to them was
|
|
158
|
+
* dropped; re-applying the policy with `tcp:443` opened 443 and nothing else.
|
|
159
|
+
* Fly's sentence is about INGRESS — a Machine's egress TOWARD a Flycast address
|
|
160
|
+
* is filtered like any other. Drop a declared service's port and every database
|
|
161
|
+
* connection in the fleet goes with it.
|
|
162
|
+
*
|
|
163
|
+
* The cost of each derived port, stated plainly because a Fly policy has no
|
|
164
|
+
* destination field of any kind (also measured — see the `egress.ts` module
|
|
165
|
+
* description): the port is opened to EVERY host on the internet, not only to
|
|
166
|
+
* the private service it was derived for. That residual cannot be closed at this
|
|
167
|
+
* layer; it is named in `verifyEgress()`'s verdict and in
|
|
168
|
+
* `docs/sandbox-egress-enforcement.md`.
|
|
158
169
|
* @param ports - The operator's configured ports, or `undefined` when no policy
|
|
159
170
|
* is being applied at all.
|
|
160
171
|
* @param services - The declared private services, or `undefined`.
|
package/dist/index.d.ts
CHANGED
|
@@ -281,12 +281,14 @@
|
|
|
281
281
|
* exist for providers whose templates are host-local; object storage is already
|
|
282
282
|
* the shared store, so there is nothing to publish or fetch.
|
|
283
283
|
*
|
|
284
|
-
* **`
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
284
|
+
* **`importFiles` IS implemented; `exportFiles` is not.** Import takes the same
|
|
285
|
+
* route the template restore does — a presigned object-store URL the Machine
|
|
286
|
+
* pulls itself, falling back to chunked base64 over `exec` when no store is
|
|
287
|
+
* configured — so an app tree can be delivered into a fresh Machine. There is no
|
|
288
|
+
* inverse: Fly's only read channel is the `exec` endpoint's JSON response, and a
|
|
289
|
+
* base64 emulation of a whole-tree export would look supported and route every
|
|
290
|
+
* byte through the control plane. Pair a Fly destination with a source that CAN
|
|
291
|
+
* export (the core's rooting contract makes them interchangeable).
|
|
290
292
|
*
|
|
291
293
|
* **Sandbox ids are composite: `"<app>:<machineId>"`.** A Fly Machine id is only
|
|
292
294
|
* unique within its app and every endpoint is `/v1/apps/{app}/machines/{id}`, so
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0UG;AAEH,cAAc,UAAU,CAAA;AACxB,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAE9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAMjE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,eAUrB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -281,12 +281,14 @@
|
|
|
281
281
|
* exist for providers whose templates are host-local; object storage is already
|
|
282
282
|
* the shared store, so there is nothing to publish or fetch.
|
|
283
283
|
*
|
|
284
|
-
* **`
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
284
|
+
* **`importFiles` IS implemented; `exportFiles` is not.** Import takes the same
|
|
285
|
+
* route the template restore does — a presigned object-store URL the Machine
|
|
286
|
+
* pulls itself, falling back to chunked base64 over `exec` when no store is
|
|
287
|
+
* configured — so an app tree can be delivered into a fresh Machine. There is no
|
|
288
|
+
* inverse: Fly's only read channel is the `exec` endpoint's JSON response, and a
|
|
289
|
+
* base64 emulation of a whole-tree export would look supported and route every
|
|
290
|
+
* byte through the control plane. Pair a Fly destination with a source that CAN
|
|
291
|
+
* export (the core's rooting contract makes them interchangeable).
|
|
290
292
|
*
|
|
291
293
|
* **Sandbox ids are composite: `"<app>:<machineId>"`.** A Fly Machine id is only
|
|
292
294
|
* unique within its app and every endpoint is `/v1/apps/{app}/machines/{id}`, so
|
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAWV,eAAe,EAGhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAwBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAIV,WAAW,EAUZ,MAAM,YAAY,CAAA;AA0tEnB;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,YAAY,EACrB,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,GACzB,eAAe,CAEjB"}
|