1import * as AWS from "alchemy/AWS";
2import * as Effect from "effect/Effect";
3import { HttpServerRequest } from "effect/http/HttpServerRequest";
4import * as HttpServerResponse from "effect/http/HttpServerResponse";
5import { VM_MARKER } from "./vm/marker.ts";
6
7/** Bare role; the image grants it the trust + build permissions it needs. */
8export const ShellBuildRole = AWS.IAM.Role("StressMicrovmBuildRole");
9
10/**
11 * An **effectful** AWS Lambda MicroVM image: this module is both the image
12 * declaration and the program that runs INSIDE the VM. Under `alchemy dev`
13 * the image is built and the VMs are booted by the floci emulator, as real
14 * Docker containers.
15 *
16 * It is the most expensive resource in the stack by a wide margin, which is
17 * exactly why it is here: the stress suite asserts that all the churn it
18 * inflicts elsewhere never rebuilds this image.
19 *
20 * Exposes both halves of the MicroVM protocol so the Worker can drive
21 * either: a raw `fetch` route (`/echo`) and a typed RPC method (`hello`).
22 */
23export class ShellMicrovm extends AWS.Lambda.MicrovmImage<
24 ShellMicrovm,
25 {
26 hello: (message: string) => Effect.Effect<string>;
27 }
28>()("StressMicrovm") {}
29
30export default ShellMicrovm.make(
31 ShellBuildRole.pipe(
32 Effect.map((buildRole) => ({
33 main: import.meta.filename,
34 buildRole,
35 runtime: "bun" as const,
36 resources: [{ minimumMemoryInMiB: 512 }],
37 cpuConfigurations: [{ architecture: "ARM_64" }],
38 })),
39 ),
40 Effect.gen(function* () {
41 return {
42 fetch: Effect.gen(function* () {
43 const request = yield* HttpServerRequest;
44 const url = new URL(request.url, "http://microvm");
45 if (url.pathname === "/echo") {
46 return yield* HttpServerResponse.json({
47 marker: VM_MARKER,
48 message: url.searchParams.get("message") ?? "",
49 });
50 }
51 return HttpServerResponse.text(VM_MARKER);
52 }),
53 hello: (message: string) => Effect.succeed(`hello, ${message}!`),
54 };
55 }),
56);