Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ build-kernel: ## Build kernel artifacts for kernel boot
$(call require-arch)
nix build .#packages.$(ARCH).$(MIXTAPE)-kernel-artifacts --impure

.PHONY: build-lambda-microvm-source
build-lambda-microvm-source: ## Build Dockerfile source zip for AWS Lambda MicroVM image creation
$(call require-arch)
nix build .#packages.$(ARCH).$(MIXTAPE)-lambda-microvm-source --impure

# -- VM development operations ------------------------------------------------

.PHONY: run
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ handling agent lifecycle and acting as a control plane for agent operators:
| Raw EFI | `system.build.raw` | `stereos.img` | Canonical artifact. Apple Virt Framework bootable |
| QCOW2 | `system.build.qcow2` | `stereos.qcow2` | Derived from raw via `qemu-img convert`. QEMU/KVM |
| Kernel artifacts | `system.build.kernelArtifacts` | `bzImage`, `initrd`, `cmdline`, `init` | Direct-kernel boot (bypasses UEFI/GRUB) |
| Lambda MicroVM source | `packages.<system>.<mixtape>-lambda-microvm-source` | Dockerfile source zip | AWS Lambda MicroVM image creation |

The Lambda MicroVM source bundle is not a full stereOS VM image. It packages
stereOS userspace into a Dockerfile-based rootfs bundle because AWS Lambda
MicroVM images are created from Dockerfile application sources, not custom
kernel or disk artifacts.

### Distribution (mkDist)

Expand Down
8 changes: 8 additions & 0 deletions flake/devshell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
pkgs.go_1_25
pkgs.gopls
pkgs.gotools
# Rust toolchain for the Lambda MicroVM lifecycle crate
# (lambda-microvm/lifecycle). Uses the pinned nixpkgs toolchain, the
# same model as go_1_25 above and buildRustPackage in the image build.
pkgs.cargo
pkgs.rustc
pkgs.clippy
pkgs.rustfmt
pkgs.rust-analyzer
pkgs.hurl
pkgs.zstd
inputs.dagger.packages.${system}.dagger
Expand Down
24 changes: 23 additions & 1 deletion flake/images.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

let
stereos-lib = import ../lib/dist.nix { inherit inputs; };
stereos-lambda = import ../lib/lambda-microvm.nix { inherit inputs; };
stereos-main = import ../lib { inherit inputs self; };

# Target architectures to build images for
Expand All @@ -36,6 +37,7 @@ let
buildSystemImages = system:
let
pkgs = inputs.nixpkgs.legacyPackages.${system};
paper-bin = import ../lib/paper-bin.nix { inherit pkgs; };

# Build a nixosConfiguration for each mixtape spec, targeting this system.
configs = builtins.listToAttrs (
Expand Down Expand Up @@ -88,8 +90,28 @@ let
};
}) mixtapeNames
);
# Lambda MicroVM source bundles β€” Dockerfile + Nix-built rootfs tar,
# deliberately separate from VM mixtape artifacts.
lambdaMicrovmPkgs = builtins.listToAttrs (
builtins.map (name: {
name = "${name}-lambda-microvm-source";
value = stereos-lambda.mkLambdaMicrovmSource {
inherit pkgs;
name = name;
version = stereos-main.stereosVersion;
agentPackages =
configs.${name}.config.stereos.agent.basePackages
++ configs.${name}.config.stereos.agent.extraPackages
++ [ paper-bin ];
startPaperd = true;
# Warm Claude Code's native build into the snapshot during image
# creation. A no-op for mixtapes without `claude` on PATH.
warmAgent = true;
};
}) mixtapeNames
);
in
rawPkgs // qcow2Named // kernelArtifactsNamed // distPkgs;
rawPkgs // qcow2Named // kernelArtifactsNamed // distPkgs // lambdaMicrovmPkgs;

# Build packages for all target systems
allPackages = builtins.listToAttrs (
Expand Down
119 changes: 119 additions & 0 deletions lambda-microvm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# stereOS Lambda MicroVM Source Bundle

This bundle is a Dockerfile-based source package for AWS Lambda MicroVM image
creation. It is intentionally separate from stereOS VM mixtapes: it does not
contain `stereos.img`, `stereos.qcow2`, a custom kernel, or direct-kernel boot
artifacts.

## Source Files

The checked-in build logic lives in:

- `lib/lambda-microvm.nix`: assembles the rootfs, generates the Dockerfile,
and zips the source bundle.
- `lambda-microvm/lifecycle/`: the Rust HTTP lifecycle process, built from
source by Nix (`buildRustPackage`) and copied into the rootfs as
`/bin/lambda-microvm-lifecycle`.
- `flake/images.nix`: exposes the generated
`packages.<system>.<mixtape>-lambda-microvm-source` package.

`src/Dockerfile` and `src/stereos-rootfs.tar` are generated files inside the
Nix build output; they are not checked into the repository.

## Build Locally

```bash
nix build .#packages.x86_64-linux.coder-lambda-microvm-source --impure
```

The Nix result contains:

- `src/Dockerfile`
- `src/stereos-rootfs.tar`
- `<mixtape>-lambda-microvm-source.zip`

Upload the zip to S3 and use it as the source object for
`aws lambda-microvms create-microvm-image`.

## Runtime Contract

The image starts `/bin/lambda-microvm-lifecycle` and listens on `HOOK_PORT`,
default `9000`. This follows the AWS Claude managed-agents sample image.

Endpoints:

- `GET /ready`
- `POST /ready`
- `GET /validate`
- `POST /validate`
- `GET /aws/lambda-microvms/runtime/v1/ready`
- `POST /aws/lambda-microvms/runtime/v1/ready`
- `GET /aws/lambda-microvms/runtime/v1/validate`
- `POST /aws/lambda-microvms/runtime/v1/validate`
- `POST /aws/lambda-microvms/runtime/v1/run`
- `POST /aws/lambda-microvms/runtime/v1/suspend`
- `POST /aws/lambda-microvms/runtime/v1/resume`
- `POST /aws/lambda-microvms/runtime/v1/terminate`
- `POST /run`
- `POST /suspend`
- `POST /resume`
- `POST /terminate`
- `GET /health`

`POST /run` executes `STEREOS_RUN_COMMAND` when that environment variable is
set. The request body is passed to the command as `STEREOS_RUN_PAYLOAD`.

`POST /aws/lambda-microvms/runtime/v1/run` accepts the AWS service envelope
shape:

```json
{"microvmId":"...","runHookPayload":"{\"version\":\"1\",\"session\":{...}}"}
```

The lifecycle process records the parsed `session` object as
`last_run_dispatch`, acknowledges immediately, and runs `STEREOS_RUN_COMMAND` in
a background thread with the parsed dispatch JSON as `STEREOS_RUN_PAYLOAD`.

The default stereOS Lambda MicroVM source bundles include the Paper CLI from a
pinned public Paper Compute release artifact. They set `STEREOS_START_PAPERD=1`,
so the lifecycle process starts `paperd` when it finds the binary at
`/usr/local/bin/paperd`, `/bin/paperd`, or `/bin/paper`. Set
`STEREOS_START_PAPERD=0` in a derived image to opt out.

## Build-time Warm-up (`STEREOS_READY_COMMAND`)

When `STEREOS_READY_COMMAND` is set, the lifecycle process runs it **once**, on
the first AWS `ready` build hook, **before** responding 200 β€” so its disk writes
are captured in the image snapshot and every launched MicroVM starts warm. It
runs as the agent user (uid/gid `1000`) with an agent-rooted login environment
(`HOME`/XDG under `/home/agent`), via `/bin/bash -lc`, bounded by
`STEREOS_READY_TIMEOUT_SECONDS` (default `240`, kept under the AWS
`readyTimeoutInSeconds`). The result is logged to CloudWatch, not stored in the
snapshot's `last_run_command`.

Mixtapes built with `warmAgent = true` ship `/usr/local/bin/stereos-warm-agent`
and point `STEREOS_READY_COMMAND` at it. That script runs `claude install` to
pre-cache Claude Code's native build (no auth, no `paper`), then pre-seeds
onboarding so the first interactive run skips the theme prompt.

Warm-up must produce only **shareable** state. The Firecracker snapshot is shared
by every MicroVM created from the image, so the script strips the per-machine
identifiers `claude install` writes (`machineID`, `userID`) so they regenerate
per-VM. Never `paper login` or otherwise bake secrets/unique state during the
build. Warm-up failures (e.g. no network during build) are non-fatal: the image
just ships unwarmed.

## Native Shell / SSH Access

The Lambda MicroVM source bundle includes OpenSSH server binaries and host keys
so it can be reached with the `microvmssh` ProxyCommand pattern:

- The image includes `/usr/sbin/sshd`, `/usr/bin/echo`, and `/usr/bin/stty`
compatibility paths.
- `sshd` is not started as a daemon.
- The local `microvmssh` client opens AWS native shell ingress, runs
`stty raw -echo; exec /usr/sbin/sshd -i ...`, then lets a normal local
`ssh` client speak over that shell WebSocket.

Launch the MicroVM with the AWS-managed `SHELL_INGRESS` connector to enable this
path.
1 change: 1 addition & 0 deletions lambda-microvm/lifecycle/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
Loading
Loading