Skip to content

Installation

Every route ships the same single static binary — pure Go, no CGO, no runtime dependency.

RouteCommand
Gogo install github.com/calvinchengx/arm-emulator/cmd/arm-emulator@latest
Homebrewbrew install calvinchengx/tap/arm-emulator
wingetwinget install calvinchengx.arm-emulator
Dockerdocker run ghcr.io/calvinchengx/arm-emulator:latest
Composemake up (the pair: entra + arm)
Sourcego build ./cmd/arm-emulator

The image is distroless and runs as nonroot. It has no shell, so the container healthcheck runs the binary’s own healthcheck subcommand.

Terminal window
docker run --rm -p 8445:8445 \
-e ARM_ENTRA_ISSUER=https://host.docker.internal:8443/6f89cf12-978b-4d23-ac18-9ef0c127cf87/v2.0 \
-e ARM_ENTRA_TLS_INSECURE=true \
ghcr.io/calvinchengx/arm-emulator:latest

ARM_DATA_DIR=/data is baked in, and /data is owned by the nonroot uid, so mounting a volume there persists SQLite and the TLS cert across restarts:

Terminal window
docker run --rm -p 8445:8445 -v arm-data:/data \
-e ARM_ENTRA_ISSUER=https://host.docker.internal:8443/6f89cf12-978b-4d23-ac18-9ef0c127cf87/v2.0 \
ghcr.io/calvinchengx/arm-emulator:latest

Mount nothing and the state lands in an anonymous volume that outlives the container. Set ARM_DATA_DIR="" for a throwaway in-memory run that leaves nothing behind, which is what the compose file does.

docker-compose.yml brings up entra + arm with the trust between them pre-wired. Ports take overrides so this stack can coexist with the other emulator families’:

Terminal window
ENTRA_PORT=18443 ARM_PORT=18445 make up
Terminal window
make doctor # what is missing, and how to get it