Getting started
doze-aws is one small static binary that emulates the AWS services a development stack leans on — built from the wire protocol up, verified against both AWS SDK generations. No Docker, no JVM, no cloud.
Run it
Section titled “Run it”curl -fsSL https://raw.githubusercontent.com/doze-dev/doze-aws/main/install.sh | shexec $SHELL # or: export PATH="$HOME/.doze-aws/bin:$PATH"
cd ~/code/harbour && doze-awsThe installer verifies a SHA-256 against the release checksums and puts one
binary in ~/.doze-aws/bin. There is nothing else to install — no runtime, no
daemon, no container. (--uninstall takes it away again and leaves your data
alone.)
The new shell is not optional. The installer adds that directory to your
.zshrc or .bashrc, but it cannot change the PATH of the shell that ran it —
no process can do that to its parent. So doze-aws is found in the next shell,
and exec $SHELL is the shortest way to get one. If your $SHELL is neither
zsh nor bash the installer will not guess at a profile; it prints the export
line for you to put wherever you keep such things.
It prints where it is and what to do next: the endpoint, the console URL, how
many services it is serving in which region and account, and the two commands
worth knowing — doze-aws env and doze-aws doctor. The
README shows the block in full.
That block goes to stdout; the structured log lines go to stderr, so
doze-aws 2>/dev/null leaves just the summary and anything parsing the logs
is unaffected.
That is the whole setup: it answers on 127.0.0.1:4566, and there is nothing
to set up on the machine and no sudo. A second instance, or a container, takes
--listen (below).
The instance is labelled after the directory, and
its state lands in ./data — which doze-aws marks ignored as it creates it,
so git never offers to commit two megabytes of local databases.
Point any AWS SDK at it, or the AWS CLI if you have one (doze-aws does not install or need it — the examples below are just the shortest way to show the thing working):
eval "$(doze-aws env)"
aws s3 mb s3://my-bucketaws s3 cp ./file.txt s3://my-bucket/cli.md has the longer set — DynamoDB, SQS,
the by-hand export block, and the per-service endpoint variables.
Open the console
Section titled “Open the console”http://127.0.0.1:4566/_console/ — on by default, --console=false to turn
it off.
It opens on the wire: every call your app has made, newest first, with the work each one caused nested under it. An S3 upload that fired a notification that invoked a Lambda is drawn as one thing, not three unrelated rows. Each row carries the status, the duration, the request id the SDK printed, and a copy as curl button whose body is the redacted copy, so a repro you paste into a ticket keeps secrets masked.
The calls the console makes on its own behalf never appear there — it talks to the services directly rather than through the recorder — so the tail is your app’s traffic and nothing else.
From there, every service has pages that do real work: browse and upload S3 objects, actually receive (not peek at) SQS messages and redrive from a DLQ, run PartiQL against DynamoDB, watch a Step Functions execution graph, invoke a Lambda and tail its logs, simulate an IAM policy before you trust it.
Two things worth knowing about:
- Connect — the same endpoint written four ways: an AWS CLI profile, the
environment block, a Terraform provider, and the deploy commands. Built from
the address you reached the console on, with a button that runs a real
GetCallerIdentityto prove it works. - The fidelity ledger, on each service page — which operations are functional, which are cosmetic round-trips that accept and return your config without acting on it, and which are honest stubs. Real AWS never has to answer “is this call real here”; an emulator does, and this is where it does.
Queue URLs, invoke URLs and function URLs come back in path shapes at an
address (http://127.0.0.1:4566/000000000000/jobs), so an SDK uses path-style
S3. Run under doze as the aws module, doze-aws has a name and they come back
AWS-shaped under it (http://sqs.us-east-1.aws.shop.doze/000000000000/jobs).
See endpoints.md.
In Docker, if that is where your team already is
Section titled “In Docker, if that is where your team already is”doze-aws ships no image and no Dockerfile, deliberately — the whole argument
against a 1.88 GB emulator container is weakened by shipping one, and a static
binary with no runtime dependencies is a two-line Dockerfile anyone can write
for themselves. If your team’s stack is already docker compose up, this is
enough:
# DockerfileFROM alpine:3COPY doze-aws /usr/local/bin/doze-awsENTRYPOINT ["doze-aws", "--listen", "0.0.0.0:4566", "--data-dir", "/data"]services: aws: build: . ports: ["4566:4566"] volumes: ["aws-data:/data"] # drop this line to start clean every runvolumes: aws-data:Then point the SDKs at http://aws:4566 from sibling services, or
http://localhost:4566 from the host. Two things to know: --listen 0.0.0.0:4566 is required, because the default 127.0.0.1 is unreachable from
outside the container; and the data directory is worth a volume only if you
want state to survive down — a test run usually does not, and starting clean
is one line shorter.
Configure
Section titled “Configure”Everything is a flag or a TOML key (flags win). Print the effective config:
doze-aws config printCopy doze-aws.example.toml to ./doze-aws.toml
— it is auto-loaded from the working directory — to name the instance, pick a
region, enable a subset of services, or set the data directory. Every key is
optional, paths resolve against the file rather than your shell, and
cli.md documents the lot.
Persistence
Section titled “Persistence”Data lives under data/<region>/<service>/ — with IAM and STS under
data/_global/ — and survives restarts. Delete the directory to reset. There is
nothing else to clean up.
Where things run
Section titled “Where things run”Everything is behind one endpoint. The gateway routes each request to the right
service by its wire signals (the X-Amz-Target header, the SigV4 scope, the
request path, or the S3 fallback) — exactly how AWS SDKs address a custom
endpoint. Cross-service features work out of the box: an S3 event notification
lands in SQS, an SNS publish fans out to SQS and Lambda, an EventBridge rule
routes to its targets, a Lambda handler reaches every sibling service.
Lambda
Section titled “Lambda”Functions run as real local processes speaking the AWS Lambda Runtime API.
For quick local iteration, point a function at code in place instead of zipping
it (the _local_ extension — see SUPPORT.md).
Coverage
Section titled “Coverage”Every implemented service documents its operation support in SUPPORT.md: Functional (real local semantics), Cosmetic (config round-trips, no local effect), or an honest Stub for what’s physically meaningless locally.
Standing up a stack
Section titled “Standing up a stack”doze-aws has no file format of its own. Deploy with whatever you already use — the AWS CLI, SAM, CDK or Serverless Framework all work against the endpoint:
aws cloudformation deploy --template-file template.yaml --stack-name shopdoze-aws also applies ./template.yaml at boot if one is present, so “clone
the repo, run doze-aws” is the whole onboarding story. See
cloudformation.md.