@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/provider.js
CHANGED
|
@@ -11,7 +11,7 @@ import { gzipSync } from 'node:zlib';
|
|
|
11
11
|
import { getLogger } from '@molecule/api-bond';
|
|
12
12
|
import { t } from '@molecule/api-i18n';
|
|
13
13
|
import { FlyApiClient, FlyApiError, normalizeApiUrl } from './api.js';
|
|
14
|
-
import { buildEgressProbeCommand, DEFAULT_EGRESS_POLICY_NAME, DEFAULT_EGRESS_PROBE_TARGETS, extractPolicyId, parseEgressAllowedPorts, parseEgressProbeTargets, verdictForProbeExit, } from './egress.js';
|
|
14
|
+
import { buildEgressProbeCommand, DEFAULT_EGRESS_POLICY_NAME, DEFAULT_EGRESS_PROBE_TARGETS, describePublicReach, extractPolicyId, extractPolicyPorts, parseEgressAllowedPorts, parseEgressProbeTargets, verdictForProbeExit, } from './egress.js';
|
|
15
15
|
import { execCommand, toExecResult } from './exec.js';
|
|
16
16
|
import { assertPrivateRoutesForEnv, decodePrivateRoutes, DEFAULT_PRIVATE_IP_TYPE, encodePrivateRoutes, flycastHost, mergeEgressPorts, parsePrivateServices, PRIVATE_ROUTES_METADATA_SUFFIX, } from './flycast.js';
|
|
17
17
|
import { appNameForProject, mapMachineState, parseSandboxId, toSandboxId } from './ids.js';
|
|
@@ -41,6 +41,38 @@ const DEFAULT_VOLUME_GB = 10;
|
|
|
41
41
|
const LIST_PAGE_SIZE = 200;
|
|
42
42
|
/** Hard cap on pages fetched while listing, so a bad cursor cannot loop forever. */
|
|
43
43
|
const LIST_MAX_PAGES = 50;
|
|
44
|
+
/**
|
|
45
|
+
* Hard cap on how many per-project apps `find()` will walk in one sweep.
|
|
46
|
+
*
|
|
47
|
+
* Requests are paced account-wide (~4.5/s), so enumerating N apps costs N
|
|
48
|
+
* round-trips of wall clock. Past this the sweep is refused OUTRIGHT rather than
|
|
49
|
+
* truncated, because `find()`'s whole contract is that a short answer is never
|
|
50
|
+
* mistaken for an empty one. Raise `FLY_FIND_MAX_APPS` deliberately.
|
|
51
|
+
*/
|
|
52
|
+
const FIND_MAX_APPS = (() => {
|
|
53
|
+
const configured = Number(process.env.FLY_FIND_MAX_APPS);
|
|
54
|
+
return Number.isFinite(configured) && configured > 0 ? configured : 500;
|
|
55
|
+
})();
|
|
56
|
+
/**
|
|
57
|
+
* When a Machine was FIRST started, from its event log.
|
|
58
|
+
*
|
|
59
|
+
* Fly has no `started_at` field; the events array (newest first) is the only
|
|
60
|
+
* record, and its absence is the meaningful case — a Machine created but never
|
|
61
|
+
* started is not "stopped", it is wreckage from an interrupted create that
|
|
62
|
+
* still holds its volume.
|
|
63
|
+
*
|
|
64
|
+
* @param machine - The Machine as Fly reported it.
|
|
65
|
+
* @returns ISO 8601 timestamp of the earliest `start` event, or `null` if it has
|
|
66
|
+
* never started (or Fly sent no events).
|
|
67
|
+
*/
|
|
68
|
+
const firstStartedAt = (machine) => {
|
|
69
|
+
const starts = (machine.events ?? [])
|
|
70
|
+
.filter((event) => event.type === 'start' && typeof event.timestamp === 'number')
|
|
71
|
+
.map((event) => event.timestamp);
|
|
72
|
+
if (!starts.length)
|
|
73
|
+
return null;
|
|
74
|
+
return new Date(Math.min(...starts)).toISOString();
|
|
75
|
+
};
|
|
44
76
|
/**
|
|
45
77
|
* Chunk size for base64-encoded `writeFile` payloads. The binding limit is NOT
|
|
46
78
|
* the OS `MAX_ARG_STRLEN` (128 KB) but Fly's exec API, which silently rejects a
|
|
@@ -204,6 +236,16 @@ class FlyioSandboxProvider {
|
|
|
204
236
|
return this.config.appPerProject;
|
|
205
237
|
return process.env.FLY_SANDBOX_APP_PER_PROJECT !== 'false';
|
|
206
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* The per-project Fly app-name prefix. One resolution shared by app NAMING
|
|
241
|
+
* (`resolveApp`) and app DISCOVERY (`find`), so a sweep can never look for a
|
|
242
|
+
* prefix creation does not use.
|
|
243
|
+
*
|
|
244
|
+
* @returns The configured prefix.
|
|
245
|
+
*/
|
|
246
|
+
appPrefix() {
|
|
247
|
+
return this.config.appPrefix ?? process.env.FLY_SANDBOX_APP_PREFIX ?? DEFAULT_APP_PREFIX;
|
|
248
|
+
}
|
|
207
249
|
/**
|
|
208
250
|
* Resolves the Fly app a project's Machine belongs to.
|
|
209
251
|
*
|
|
@@ -218,7 +260,7 @@ class FlyioSandboxProvider {
|
|
|
218
260
|
*/
|
|
219
261
|
resolveApp(projectId) {
|
|
220
262
|
if (this.appPerProject()) {
|
|
221
|
-
return appNameForProject(this.
|
|
263
|
+
return appNameForProject(this.appPrefix(), projectId);
|
|
222
264
|
}
|
|
223
265
|
const shared = this.config.appName ?? process.env.FLY_SANDBOX_APP;
|
|
224
266
|
if (process.env.NODE_ENV === 'production') {
|
|
@@ -632,7 +674,74 @@ class FlyioSandboxProvider {
|
|
|
632
674
|
rules: [{ action: 'allow', direction: 'egress', ports }],
|
|
633
675
|
};
|
|
634
676
|
await this.client.request(`/apps/${app}/network_policies`, { method: 'POST', body: policy });
|
|
635
|
-
logger.info('Applied Fly egress network policy', {
|
|
677
|
+
logger.info('Applied Fly egress network policy', {
|
|
678
|
+
app,
|
|
679
|
+
name,
|
|
680
|
+
ports,
|
|
681
|
+
updated: Boolean(id),
|
|
682
|
+
// A Fly policy has no destination field (measured — see the egress module
|
|
683
|
+
// description), so every port here is open to every host on the internet.
|
|
684
|
+
// Logged with the apply so the residual appears where the decision is
|
|
685
|
+
// recorded, not only in a document.
|
|
686
|
+
publicReach: describePublicReach(ports),
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Re-applies this provider's egress policy to an app that ALREADY exists.
|
|
691
|
+
*
|
|
692
|
+
* `ensureApp()` applies the policy on the path to creating a Machine, so a
|
|
693
|
+
* change to the configured ports reaches an existing app only when something
|
|
694
|
+
* deploys into it again. Until then Fly still holds the older, wider policy —
|
|
695
|
+
* and Fly applies whatever it holds at Machine BOOT, so a host migration or a
|
|
696
|
+
* crash-restart brings that app back up under the policy the operator thought
|
|
697
|
+
* they had replaced. That window is what this closes: a consumer that knows
|
|
698
|
+
* which apps it created (from its own records — never by enumerating a
|
|
699
|
+
* provider that cannot be enumerated) can walk them at startup and put the
|
|
700
|
+
* current policy in place before any of them next boots.
|
|
701
|
+
*
|
|
702
|
+
* Idempotent, and it updates the policy this provider owns in place rather
|
|
703
|
+
* than stacking a second one.
|
|
704
|
+
* @param app - The Fly app name.
|
|
705
|
+
* @returns The ports now allowed, or `undefined` when no policy is configured
|
|
706
|
+
* at all — in which case nothing was written, because turning "no policy"
|
|
707
|
+
* into one that Fly then uses to drop everything else is not something to
|
|
708
|
+
* infer from a reconciliation sweep.
|
|
709
|
+
* @throws {Error} When the policy cannot be applied. The caller decides what a
|
|
710
|
+
* failed reconcile means; this never reports success for a write that did
|
|
711
|
+
* not happen.
|
|
712
|
+
*/
|
|
713
|
+
async reconcileEgressPolicy(app) {
|
|
714
|
+
const ports = this.egressAllowedPorts();
|
|
715
|
+
if (!ports)
|
|
716
|
+
return undefined;
|
|
717
|
+
await this.applyEgressPolicy(app);
|
|
718
|
+
return ports;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Reads back the egress ports the policy Fly holds for an app allows.
|
|
722
|
+
*
|
|
723
|
+
* Best-effort by design: this feeds a DRIFT check, and a readback that failed
|
|
724
|
+
* proves nothing about the policy — treating "I could not look" as "there is
|
|
725
|
+
* no drift" is the same conflation the verdict mapping refuses, so the caller
|
|
726
|
+
* gets `undefined` and reports on the observation alone.
|
|
727
|
+
* @param app - The Fly app name.
|
|
728
|
+
* @returns The allowed ports, or `undefined` when nothing could be read.
|
|
729
|
+
*/
|
|
730
|
+
async readAppliedPolicyPorts(app) {
|
|
731
|
+
const name = this.config.egressPolicyName ?? DEFAULT_EGRESS_POLICY_NAME;
|
|
732
|
+
try {
|
|
733
|
+
const listed = await this.client.request(`/apps/${app}/network_policies`, {
|
|
734
|
+
nullOn: [404],
|
|
735
|
+
});
|
|
736
|
+
return extractPolicyPorts(listed, name);
|
|
737
|
+
}
|
|
738
|
+
catch (error) {
|
|
739
|
+
logger.warn('Could not read the Fly egress policy back — reporting on the probe alone', {
|
|
740
|
+
app,
|
|
741
|
+
error,
|
|
742
|
+
});
|
|
743
|
+
return undefined;
|
|
744
|
+
}
|
|
636
745
|
}
|
|
637
746
|
/**
|
|
638
747
|
* Requests a shared Anycast IPv4 for the app so `<app>.fly.dev` serves traffic.
|
|
@@ -765,8 +874,11 @@ class FlyioSandboxProvider {
|
|
|
765
874
|
}
|
|
766
875
|
// Also resolved BEFORE anything is provisioned: a sandbox told to dial a
|
|
767
876
|
// `.flycast` host nobody declared would boot healthy and then fail to
|
|
768
|
-
// connect inside the user's own application.
|
|
769
|
-
|
|
877
|
+
// connect inside the user's own application. Env the caller delivers itself
|
|
878
|
+
// is checked too — the host is unroutable however the value arrives, and
|
|
879
|
+
// that env is exactly where a control plane puts the values it refuses to
|
|
880
|
+
// leave in a Machine config.
|
|
881
|
+
assertPrivateRoutesForEnv({ ...config.env, ...config.selfDeliveredEnv }, this.privateServices());
|
|
770
882
|
const app = this.resolveApp(config.projectId);
|
|
771
883
|
const privateRoutes = await this.ensureApp(app);
|
|
772
884
|
// Clear any stale Machine from a prior failed/partial create before
|
|
@@ -809,10 +921,19 @@ class FlyioSandboxProvider {
|
|
|
809
921
|
},
|
|
810
922
|
// `no` matches the Docker provider: a sandbox whose main process exits
|
|
811
923
|
// stays stopped for the caller to inspect, rather than silently looping.
|
|
812
|
-
// Fly's default when unset is `on-failure`.
|
|
813
|
-
|
|
924
|
+
// Fly's default when unset is `on-failure`. A caller running something
|
|
925
|
+
// long-lived overrides it (`config.restartPolicy`).
|
|
926
|
+
restart: { policy: config.restartPolicy ?? 'no' },
|
|
814
927
|
auto_destroy: false,
|
|
815
928
|
};
|
|
929
|
+
// The caller's own main process, instead of the image's entrypoint/cmd.
|
|
930
|
+
// Fly's `init.exec` overrides BOTH ("run this instead of the entrypoint and
|
|
931
|
+
// command in your Docker container definition"), which is what a caller
|
|
932
|
+
// asking for a specific pid 1 means. Without it a Machine that Fly restarts
|
|
933
|
+
// — host migration, `fly machine restart`, an OOM kill — comes back running
|
|
934
|
+
// the image's idle command and serving nothing.
|
|
935
|
+
if (config.command?.length)
|
|
936
|
+
machineConfig.init = { exec: [...config.command] };
|
|
816
937
|
if (config.volumeName) {
|
|
817
938
|
metadata[`${this.metadataPrefix}.volumeName`] = config.volumeName;
|
|
818
939
|
const sizeGB = config.resources?.diskMB
|
|
@@ -822,9 +943,7 @@ class FlyioSandboxProvider {
|
|
|
822
943
|
// The caller may scope the volume to a subtree (e.g. /workspace/<appDir>)
|
|
823
944
|
// so regenerable tooling (node_modules) stays on the fast image-backed
|
|
824
945
|
// rootfs and only durable state pays the volume's small-file IO cost.
|
|
825
|
-
machineConfig.mounts = [
|
|
826
|
-
{ volume: volumeId, path: config.volumeMountPath ?? WORKSPACE_PATH },
|
|
827
|
-
];
|
|
946
|
+
machineConfig.mounts = [{ volume: volumeId, path: config.volumeMountPath ?? WORKSPACE_PATH }];
|
|
828
947
|
}
|
|
829
948
|
const service = this.previewService();
|
|
830
949
|
if (service)
|
|
@@ -934,6 +1053,146 @@ class FlyioSandboxProvider {
|
|
|
934
1053
|
}
|
|
935
1054
|
return sandboxes;
|
|
936
1055
|
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Find managed sandboxes by what the caller labelled them.
|
|
1058
|
+
*
|
|
1059
|
+
* The reconciliation counterpart to `list()`: a control plane that restarted
|
|
1060
|
+
* asks this which of its recorded sandboxes are still alive, and which live
|
|
1061
|
+
* sandboxes no record names. molecule.dev's production sweep is the reason it
|
|
1062
|
+
* exists — with no `find()` the sweep returned at its capability check, so a
|
|
1063
|
+
* Machine that died stayed recorded as `live` and the edge kept routing to it,
|
|
1064
|
+
* while a Machine from a crashed deploy kept running and billing with nothing
|
|
1065
|
+
* left to name it.
|
|
1066
|
+
*
|
|
1067
|
+
* **Enumeration is by APP, not by the org-wide Machines endpoint.** In
|
|
1068
|
+
* per-project mode each project owns a whole Fly app, so the complete set is
|
|
1069
|
+
* "every app whose name starts with this provider's prefix, and every Machine
|
|
1070
|
+
* in each" — which is exactly answerable. `GET /orgs/{org}/machines` (what
|
|
1071
|
+
* `list()` uses) pages with an advisory limit and a cursor Fly may still be
|
|
1072
|
+
* handing out at the page cap, and a TRUNCATED answer here is worse than no
|
|
1073
|
+
* answer: the caller reads a missing Machine as "gone" and stops routing to a
|
|
1074
|
+
* live one.
|
|
1075
|
+
*
|
|
1076
|
+
* Which is the whole contract of this method — **it throws rather than
|
|
1077
|
+
* returning a short list.** Every failure mode below (no app listing, an
|
|
1078
|
+
* incomplete app listing, more apps than the bound, a machine listing that
|
|
1079
|
+
* errors) is "I could not enumerate", and a recovery loop must never read that
|
|
1080
|
+
* as "there is nothing there".
|
|
1081
|
+
*
|
|
1082
|
+
* `ports` is deliberately always empty: Fly routes to a Machine through its
|
|
1083
|
+
* proxy by HOSTNAME with TLS at the edge, not by a `host:port` forward the
|
|
1084
|
+
* control plane can dial. `getPreviewUrl()` is the address; a `{ host, port }`
|
|
1085
|
+
* pair here would be dialled as plain http on 443.
|
|
1086
|
+
*
|
|
1087
|
+
* @param query - Narrowing by label, status and project. All conditions AND.
|
|
1088
|
+
* @returns A descriptor per matching sandbox.
|
|
1089
|
+
* @throws {Error} When the provider cannot enumerate completely.
|
|
1090
|
+
*/
|
|
1091
|
+
async find(query) {
|
|
1092
|
+
const managedKey = `${this.metadataPrefix}.managed`;
|
|
1093
|
+
const projectIdKey = `${this.metadataPrefix}.projectId`;
|
|
1094
|
+
const volumeNameKey = `${this.metadataPrefix}.volumeName`;
|
|
1095
|
+
const templateIdKey = `${this.metadataPrefix}.templateId`;
|
|
1096
|
+
const apps = await this.appsToEnumerate();
|
|
1097
|
+
const descriptors = [];
|
|
1098
|
+
for (const app of apps) {
|
|
1099
|
+
let machines;
|
|
1100
|
+
try {
|
|
1101
|
+
machines = await this.client.request(`/apps/${app}/machines`, {
|
|
1102
|
+
// A 404 is the app itself being gone — a real absence (it was deleted
|
|
1103
|
+
// between the app listing and this call), not a failed query.
|
|
1104
|
+
nullOn: [404],
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
catch (error) {
|
|
1108
|
+
throw new Error(t('codeSandbox.flyio.error.findFailed', { app }, {
|
|
1109
|
+
defaultValue: `Cannot enumerate Fly sandboxes: listing Machines in app "${app}" failed. ` +
|
|
1110
|
+
'Refusing to report a partial result — a caller reconciling against it would ' +
|
|
1111
|
+
'treat every unseen sandbox as gone.',
|
|
1112
|
+
}), { cause: error });
|
|
1113
|
+
}
|
|
1114
|
+
for (const machine of machines ?? []) {
|
|
1115
|
+
const metadata = machine.config?.metadata ?? {};
|
|
1116
|
+
if (metadata[managedKey] !== 'true')
|
|
1117
|
+
continue;
|
|
1118
|
+
const status = mapMachineState(machine.state ?? 'created');
|
|
1119
|
+
const projectId = metadata[projectIdKey] ?? null;
|
|
1120
|
+
if (query?.projectId && projectId !== query.projectId)
|
|
1121
|
+
continue;
|
|
1122
|
+
if (query?.statuses?.length && !query.statuses.includes(status))
|
|
1123
|
+
continue;
|
|
1124
|
+
if (query?.labels) {
|
|
1125
|
+
let matches = true;
|
|
1126
|
+
for (const [key, value] of Object.entries(query.labels)) {
|
|
1127
|
+
if (metadata[key] !== value) {
|
|
1128
|
+
matches = false;
|
|
1129
|
+
break;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (!matches)
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
descriptors.push({
|
|
1136
|
+
id: toSandboxId(app, machine.id),
|
|
1137
|
+
projectId,
|
|
1138
|
+
status,
|
|
1139
|
+
// The caller's own labels are in this map verbatim — `create()` merges
|
|
1140
|
+
// them alongside the provider's `<prefix>.*` keys.
|
|
1141
|
+
labels: { ...metadata },
|
|
1142
|
+
createdAt: machine.created_at ?? null,
|
|
1143
|
+
startedAt: firstStartedAt(machine),
|
|
1144
|
+
templateRef: metadata[templateIdKey] ?? machine.config?.image ?? null,
|
|
1145
|
+
volumeName: metadata[volumeNameKey] ?? null,
|
|
1146
|
+
ports: [],
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
return descriptors;
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* The apps `find()` must look inside — one per project in per-project mode,
|
|
1154
|
+
* the single configured app otherwise.
|
|
1155
|
+
*
|
|
1156
|
+
* Filtering the org's apps by this provider's own prefix is what keeps the
|
|
1157
|
+
* sweep off apps it does not own (the control-plane API, the databases, the
|
|
1158
|
+
* edge proxy), without needing a permission the token may not have.
|
|
1159
|
+
*
|
|
1160
|
+
* @returns Every app name to enumerate.
|
|
1161
|
+
* @throws {Error} When the org's apps cannot be listed completely.
|
|
1162
|
+
*/
|
|
1163
|
+
async appsToEnumerate() {
|
|
1164
|
+
if (!this.appPerProject())
|
|
1165
|
+
return [this.resolveApp('unused')];
|
|
1166
|
+
const org = this.orgSlug();
|
|
1167
|
+
let listing;
|
|
1168
|
+
try {
|
|
1169
|
+
listing = await this.client.request(`/apps?org_slug=${encodeURIComponent(org)}`, {});
|
|
1170
|
+
}
|
|
1171
|
+
catch (error) {
|
|
1172
|
+
throw new Error(t('codeSandbox.flyio.error.appListFailed', { org }, {
|
|
1173
|
+
defaultValue: `Cannot enumerate Fly sandboxes: listing the apps of org "${org}" failed. ` +
|
|
1174
|
+
'Refusing to report a partial result.',
|
|
1175
|
+
}), { cause: error });
|
|
1176
|
+
}
|
|
1177
|
+
const apps = listing?.apps ?? [];
|
|
1178
|
+
const total = listing?.total_apps;
|
|
1179
|
+
if (typeof total === 'number' && apps.length < total) {
|
|
1180
|
+
throw new Error(t('codeSandbox.flyio.error.appListTruncated', { org, returned: String(apps.length), total: String(total) }, {
|
|
1181
|
+
defaultValue: `Fly returned ${apps.length} of ${total} apps for org "${org}" — this listing is ` +
|
|
1182
|
+
'TRUNCATED, so some sandboxes would be missing from the result.',
|
|
1183
|
+
}));
|
|
1184
|
+
}
|
|
1185
|
+
const prefix = `${this.appPrefix()}-`;
|
|
1186
|
+
const owned = apps.map((app) => app.name).filter((name) => name?.startsWith(prefix));
|
|
1187
|
+
if (owned.length > FIND_MAX_APPS) {
|
|
1188
|
+
throw new Error(t('codeSandbox.flyio.error.findTooManyApps', { count: String(owned.length), max: String(FIND_MAX_APPS) }, {
|
|
1189
|
+
defaultValue: `Refusing to enumerate ${owned.length} Fly sandbox apps (cap ${FIND_MAX_APPS}): ` +
|
|
1190
|
+
'requests are paced account-wide, so this sweep would take longer than the caller ' +
|
|
1191
|
+
'can wait. Raise FLY_FIND_MAX_APPS deliberately rather than reading a partial sweep.',
|
|
1192
|
+
}));
|
|
1193
|
+
}
|
|
1194
|
+
return owned;
|
|
1195
|
+
}
|
|
937
1196
|
/**
|
|
938
1197
|
* Destroys a sandbox.
|
|
939
1198
|
*
|
|
@@ -1204,7 +1463,17 @@ class FlyioSandboxProvider {
|
|
|
1204
1463
|
await this.waitForStarted(app, machineId);
|
|
1205
1464
|
const seconds = Math.min(MAX_WAIT_SECONDS, Math.max(1, Math.ceil(timeoutMs / 1000) + PROBE_EXEC_HEADROOM_SECONDS));
|
|
1206
1465
|
const result = toExecResult(await this.rawExec(app, machineId)(['sh', '-c', buildEgressProbeCommand(targets, timeoutMs)], seconds));
|
|
1207
|
-
|
|
1466
|
+
// Read the policy back from Fly rather than trusting the write: the probe
|
|
1467
|
+
// can only speak for the ports it attempted, so a policy widened outside
|
|
1468
|
+
// this provider is invisible to it. This is the observation that catches
|
|
1469
|
+
// that, and it happens before the throwaway app is destroyed.
|
|
1470
|
+
const appliedPolicyPorts = await this.readAppliedPolicyPorts(app);
|
|
1471
|
+
return verdictForProbeExit(result.exitCode, {
|
|
1472
|
+
app,
|
|
1473
|
+
targets,
|
|
1474
|
+
policyPorts,
|
|
1475
|
+
appliedPolicyPorts,
|
|
1476
|
+
});
|
|
1208
1477
|
}
|
|
1209
1478
|
catch (error) {
|
|
1210
1479
|
// A probe that could not RUN proves nothing. Reporting `filtered` here is
|
package/dist/storage.d.ts
CHANGED
|
@@ -24,6 +24,14 @@
|
|
|
24
24
|
* (https://www.tigrisdata.com/docs/api/s3/): PutObject, GetObject, HeadObject,
|
|
25
25
|
* ListObjectsV2, DeleteObject and DeleteObjects.
|
|
26
26
|
*
|
|
27
|
+
* Runs behind an outbound proxy when `HTTPS_PROXY` is set: the AWS SDK v3 builds
|
|
28
|
+
* its own agent and reads no proxy variable, so the client is given a
|
|
29
|
+
* CONNECT-capable one through its `requestHandler` hook
|
|
30
|
+
* (`@molecule/api-proxy-agent`, resolved against the configured endpoint so a
|
|
31
|
+
* store listed in `NO_PROXY` keeps connecting directly). Note this covers only
|
|
32
|
+
* the control plane's calls — the sandbox moves the tar bytes itself over the
|
|
33
|
+
* presigned URLs, through whatever egress path the sandbox has.
|
|
34
|
+
*
|
|
27
35
|
* @module
|
|
28
36
|
*/
|
|
29
37
|
import type { FlyioConfig } from './types.js';
|
package/dist/storage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAcH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,4DAA4D;AAC5D,eAAO,MAAM,uBAAuB,+BAA+B,CAAA;AAEnE;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,SAAS,CAAA;AAE7C;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,QAAmB,CAAA;AAE1D,+CAA+C;AAC/C,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAA;IACX,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,sFAAsF;IACtF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,qFAAqF;IACrF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAC7C,4FAA4F;IAC5F,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;IAC/C,+FAA+F;IAC/F,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC5C,iCAAiC;IACjC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtE,qEAAqE;IACrE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,yDAAyD;IACzD,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE,2DAA2D;IAC3D,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACnE;AAED,kEAAkE;AAClE,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,cAAc,EAAE,OAAO,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;CACf;AAmBD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG,eAAe,GAAG,IAAI,CAqClF;AAmBD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAsI3E"}
|
package/dist/storage.js
CHANGED
|
@@ -24,10 +24,19 @@
|
|
|
24
24
|
* (https://www.tigrisdata.com/docs/api/s3/): PutObject, GetObject, HeadObject,
|
|
25
25
|
* ListObjectsV2, DeleteObject and DeleteObjects.
|
|
26
26
|
*
|
|
27
|
+
* Runs behind an outbound proxy when `HTTPS_PROXY` is set: the AWS SDK v3 builds
|
|
28
|
+
* its own agent and reads no proxy variable, so the client is given a
|
|
29
|
+
* CONNECT-capable one through its `requestHandler` hook
|
|
30
|
+
* (`@molecule/api-proxy-agent`, resolved against the configured endpoint so a
|
|
31
|
+
* store listed in `NO_PROXY` keeps connecting directly). Note this covers only
|
|
32
|
+
* the control plane's calls — the sandbox moves the tar bytes itself over the
|
|
33
|
+
* presigned URLs, through whatever egress path the sandbox has.
|
|
34
|
+
*
|
|
27
35
|
* @module
|
|
28
36
|
*/
|
|
29
37
|
import { DeleteObjectsCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client, } from '@aws-sdk/client-s3';
|
|
30
38
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
39
|
+
import { getProxyAgents } from '@molecule/api-proxy-agent';
|
|
31
40
|
/** Default key prefix for every object this bond writes. */
|
|
32
41
|
export const DEFAULT_TEMPLATE_PREFIX = 'molecule-sandbox-templates';
|
|
33
42
|
/**
|
|
@@ -131,9 +140,17 @@ export function createTemplateStore(config) {
|
|
|
131
140
|
const settings = resolveTemplateStorage(config);
|
|
132
141
|
if (!settings)
|
|
133
142
|
return null;
|
|
143
|
+
// The AWS SDK v3 builds its own `https.Agent` and reads no proxy variable, so
|
|
144
|
+
// on a host whose only egress path is a proxy every template read/write would
|
|
145
|
+
// fail with a bare connection error. `requestHandler` takes NodeHttpHandler
|
|
146
|
+
// OPTIONS, so the agent goes in with no `@smithy/*` dependency. Resolved
|
|
147
|
+
// against the endpoint actually in use, so an internal store that NO_PROXY
|
|
148
|
+
// exempts keeps connecting directly.
|
|
149
|
+
const proxy = getProxyAgents(settings.endpoint || `https://s3.${settings.region}.amazonaws.com`);
|
|
134
150
|
const client = new S3Client({
|
|
135
151
|
region: settings.region,
|
|
136
152
|
...(settings.endpoint ? { endpoint: settings.endpoint } : {}),
|
|
153
|
+
...(proxy ? { requestHandler: proxy } : {}),
|
|
137
154
|
forcePathStyle: settings.forcePathStyle,
|
|
138
155
|
credentials: {
|
|
139
156
|
accessKeyId: settings.accessKeyId,
|
package/dist/types.d.ts
CHANGED
|
@@ -104,10 +104,13 @@ export interface FlyioConfig {
|
|
|
104
104
|
*/
|
|
105
105
|
publicService?: boolean;
|
|
106
106
|
/**
|
|
107
|
-
* Fly Proxy idle behaviour for the preview service. `
|
|
108
|
-
*
|
|
109
|
-
* idle
|
|
110
|
-
*
|
|
107
|
+
* Fly Proxy idle behaviour for the preview service. Defaults to `'off'` —
|
|
108
|
+
* never idle the Machine — because the autostop timer counts the PROXY
|
|
109
|
+
* service's idle time, not the workload's, so a sandbox with nobody looking at
|
|
110
|
+
* its preview is suspended out from under a build that is still running.
|
|
111
|
+
* `suspend` is the scale-to-zero mapping (resume from a memory snapshot on the
|
|
112
|
+
* next request) and `stop` is a full stop with a cold boot on wake; opt into
|
|
113
|
+
* either deliberately.
|
|
111
114
|
*/
|
|
112
115
|
autostop?: 'off' | 'stop' | 'suspend';
|
|
113
116
|
/**
|
|
@@ -403,6 +406,13 @@ export interface FlyNetworkPolicyPort {
|
|
|
403
406
|
* One rule in a Fly network policy. `allow` is the only documented action:
|
|
404
407
|
* "Once you create a rule for a given direction, the default for that direction
|
|
405
408
|
* becomes drop."
|
|
409
|
+
*
|
|
410
|
+
* There is no destination field, and that is measured rather than assumed
|
|
411
|
+
* (2026-08-16, live API): `ipv6_cidrs`, `ipv4_cidrs`, `cidrs`, `destinations`,
|
|
412
|
+
* `apps`, `app`, `to` and `dst` were each POSTed on a rule and each came back
|
|
413
|
+
* absent — the stored rule is `{action, direction, ports}` and nothing more. An
|
|
414
|
+
* `allow tcp/5432` rule therefore permits the Machine to reach port 5432 on
|
|
415
|
+
* every host on the internet.
|
|
406
416
|
*/
|
|
407
417
|
export interface FlyNetworkPolicyRule {
|
|
408
418
|
/** Only `allow` is supported by Fly. */
|
|
@@ -413,8 +423,16 @@ export interface FlyNetworkPolicyRule {
|
|
|
413
423
|
ports: FlyNetworkPolicyPort[];
|
|
414
424
|
}
|
|
415
425
|
/**
|
|
416
|
-
* Which Machines in the app a policy applies to.
|
|
417
|
-
*
|
|
426
|
+
* Which Machines in the app a policy applies to — the SOURCE side only. There is
|
|
427
|
+
* no destination selector: an `apps` array is the one candidate the API answers
|
|
428
|
+
* about, and it answers `400 {"error":"apps array not currently supported in
|
|
429
|
+
* selectors"}` (measured 2026-08-16); every other candidate field is silently
|
|
430
|
+
* dropped. Documented criteria combine with AND, so this provider uses
|
|
431
|
+
* `{ all: true }` alone.
|
|
432
|
+
*
|
|
433
|
+
* The API's LIST response returns this object under the key `netpolSelector`
|
|
434
|
+
* while accepting it as `selector` on write — which is why nothing here reads it
|
|
435
|
+
* back by name.
|
|
418
436
|
*/
|
|
419
437
|
export interface FlyNetworkPolicySelector {
|
|
420
438
|
/** Match every Machine in the app. */
|
|
@@ -457,6 +475,34 @@ export interface FlyMachine {
|
|
|
457
475
|
region?: string;
|
|
458
476
|
private_ip?: string;
|
|
459
477
|
config?: FlyMachineConfig;
|
|
478
|
+
/** RFC 3339 creation time, as Fly reports it. */
|
|
479
|
+
created_at?: string;
|
|
480
|
+
/**
|
|
481
|
+
* Lifecycle events, newest first. The ONLY place Fly records when a Machine
|
|
482
|
+
* was first started — there is no `started_at` field — which is what
|
|
483
|
+
* distinguishes a stopped Machine from one that was created and never ran.
|
|
484
|
+
*/
|
|
485
|
+
events?: FlyMachineEvent[];
|
|
486
|
+
}
|
|
487
|
+
/** One entry of {@link FlyMachine.events}. */
|
|
488
|
+
export interface FlyMachineEvent {
|
|
489
|
+
/** `start`, `launch`, `exit`, … */
|
|
490
|
+
type?: string;
|
|
491
|
+
status?: string;
|
|
492
|
+
/** Epoch milliseconds. */
|
|
493
|
+
timestamp?: number;
|
|
494
|
+
}
|
|
495
|
+
/** An app as returned by `GET /v1/apps?org_slug=…` (the fields this provider reads). */
|
|
496
|
+
export interface FlyApp {
|
|
497
|
+
id?: string;
|
|
498
|
+
name: string;
|
|
499
|
+
machine_count?: number;
|
|
500
|
+
}
|
|
501
|
+
/** Response body of `GET /v1/apps?org_slug=…`. */
|
|
502
|
+
export interface FlyAppList {
|
|
503
|
+
/** Total apps in the org, which the returned page may not cover. */
|
|
504
|
+
total_apps?: number;
|
|
505
|
+
apps?: FlyApp[];
|
|
460
506
|
}
|
|
461
507
|
/** A Machine as returned by the org-wide list endpoint (`GET /orgs/{org}/machines`). */
|
|
462
508
|
export interface FlyOrgMachine extends FlyMachine {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kEAAkE;IAClE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,6FAA6F;IAC7F,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/api-code-sandbox-flyio",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Fly.io Machines (Firecracker microVM) code sandbox provider",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
"license": "Apache-2.0",
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@molecule/api-bond": "1.0.1",
|
|
34
|
-
"@molecule/api-code-sandbox": "1.0
|
|
34
|
+
"@molecule/api-code-sandbox": "1.2.0",
|
|
35
35
|
"@types/node": "26.1.2",
|
|
36
36
|
"typescript": "6.0.3",
|
|
37
37
|
"vitest": "4.1.10"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@molecule/api-bond": "^1.0.1",
|
|
41
|
-
"@molecule/api-code-sandbox": "^1.0
|
|
41
|
+
"@molecule/api-code-sandbox": "^1.2.0",
|
|
42
42
|
"@molecule/api-i18n": "^1.0.1"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@aws-sdk/client-s3": "3.1098.0",
|
|
61
|
-
"@aws-sdk/s3-request-presigner": "3.1098.0"
|
|
61
|
+
"@aws-sdk/s3-request-presigner": "3.1098.0",
|
|
62
|
+
"@molecule/api-proxy-agent": "1.1.0"
|
|
62
63
|
}
|
|
63
64
|
}
|