MiniStack — Free Open-Source AWS Emulator

MiniStack

Free, open-source local AWS emulator. Free forever.

60+ AWS services on a single port · Multi-account & multi-region · Terraform compatible · Real databases · MIT licensed

GitHub release Build Docker Pulls Docker Image Size License Python

Ministack.org · Docker Hub · LinkedIn

--- ## Why MiniStack? LocalStack recently moved its core services behind a paid plan. If you relied on LocalStack Community for local development and CI/CD pipelines, MiniStack is your free alternative. - **60+ AWS services** emulated on a single port (4566) - **Drop-in compatible** — works with `boto3`, AWS CLI, Terraform, CDK, Pulumi, any SDK - **Multi-account & multi-region** — a 12-digit access key becomes the account, the SigV4 region scopes the state; isolated tenants and regions on one endpoint, like real AWS - **Amazon Bedrock locally** — Converse / InvokeModel with AWS-exact wire shapes; point `MINISTACK_BEDROCK_PROXY_URL` at Ollama, llama.cpp, or vLLM and get real completions through the Bedrock API - **Real infrastructure** — RDS spins up actual Postgres/MySQL containers, ElastiCache spins up real Redis/Valkey, Athena runs real SQL via DuckDB (full image only), ECS runs real Docker containers - **Tiny footprint** — ~270MB image, ~30MB RAM at idle vs LocalStack's ~1GB image and ~500MB RAM - **Fast startup** — under 2 seconds, HTTP/2 (h2c) supported - **MIT licensed** — use it, fork it, contribute to it --- ## Quick Start ```bash # Option 1: PyPI (simplest) pip install ministack ministack # Runs on http://localhost:4566 — use GATEWAY_PORT=XXXX to change # Option 2: Docker Hub docker run -p 4566:4566 ministackorg/ministack # Option 2b: Docker Hub with real infrastructure (RDS, ECS, Lambda containers) docker run -p 4566:4566 -v /var/run/docker.sock:/var/run/docker.sock ministackorg/ministack # Option 2c: Full image — Debian/glibc base with DuckDB (Athena), psycopg2, pymysql. # Larger (~360 MB vs ~110 MB) but enables Athena and native PostgreSQL/MySQL drivers # that don't ship musllinux wheels. Reports `edition: full` on /_ministack/health. docker run -p 4566:4566 ministackorg/ministack:full # Option 3: Clone and build git clone https://github.com/ministackorg/ministack cd ministack docker compose up -d # Verify (any option) curl http://localhost:4566/_ministack/health ``` That's it. No account, no API key, no sign-up. --- ## Internal API MiniStack exposes internal endpoints for test automation: ```bash # Health check — returns service status curl http://localhost:4566/_ministack/health # Reset all state — wipe every service back to empty (useful between test runs) curl -X POST http://localhost:4566/_ministack/reset # Reset and re-run init scripts (boot.d + ready.d) curl -X POST http://localhost:4566/_ministack/reset?init=1 # Runtime config — change service-level settings without restart curl -X POST http://localhost:4566/_ministack/config \ -H "Content-Type: application/json" \ -d '{"lambda_svc.LAMBDA_EXECUTOR": "docker"}' # Inspect emails sent via SES — returns every message grouped by account curl http://localhost:4566/_ministack/ses/messages # Filter by account (12-digit access-key ID used as the account ID) curl "http://localhost:4566/_ministack/ses/messages?account=000000000000" # Inspect SQS messages — returns every queue's messages grouped by account # (includes Body, MessageId, ReceiveCount, VisibleAt, IsVisible, MessageAttributes, FIFO group/dedup) curl http://localhost:4566/_ministack/sqs/messages # Filter by account and/or a specific queue curl "http://localhost:4566/_ministack/sqs/messages?account=000000000000&QueueUrl=http://localhost:4566/000000000000/my-queue" # Inspect SMS sent via SNS Publish with a PhoneNumber — grouped by recipient curl http://localhost:4566/_ministack/sns/sms-messages # Filter by recipient, account and/or region curl "http://localhost:4566/_ministack/sns/sms-messages?phoneNumber=%2B15551234567" ``` The reset endpoint is especially useful in CI pipelines and test suites — call it in `setUp`/`beforeEach` to get a clean environment for every test without restarting the container. Add `?init=1` to re-run your init scripts after the reset, restoring any resources they create (VPCs, queues, seed data, etc.). The config endpoint supports these keys: | Key | Description | |-----|-------------| | `lambda_svc.LAMBDA_EXECUTOR` | Lambda execution mode (`local` or `docker`) | | `athena.ATHENA_ENGINE` | Athena query engine (`duckdb` or `mock`) | | `athena.ATHENA_DATA_DIR` | Directory for Athena DuckDB data files | | `stepfunctions._sfn_mock_config` | SFN mock config (AWS SFN Local compatible) | | `stepfunctions._SFN_WAIT_SCALE` | Scale factor for Wait state durations and retry sleeps (`0` = skip all waits) | To set region or account ID, use environment variables at startup: ```bash docker run -p 4566:4566 \ -e MINISTACK_REGION=eu-west-1 \ -e MINISTACK_ACCOUNT_ID=123456789012 \ ministackorg/ministack ``` Or use the multi-tenancy feature — a 12-digit access key automatically becomes the account ID (see [Multi-Tenancy](#multi-tenancy) below). Also compatible with LocalStack's health endpoint: ```bash curl http://localhost:4566/_localstack/health curl http://localhost:4566/health ``` --- ## Multi-Tenancy MiniStack supports lightweight multi-tenancy without any configuration. If the `AWS_ACCESS_KEY_ID` is a **12-digit number**, it is used as the **Account ID** for all ARN generation. Non-numeric keys (like `test`) fall back to the `MINISTACK_ACCOUNT_ID` env var or `000000000000`. ```bash # Team A — gets account 111111111111 export AWS_ACCESS_KEY_ID=111111111111 export AWS_SECRET_ACCESS_KEY=test aws --endpoint-url=http://localhost:4566 sts get-caller-identity # → { "Account": "111111111111", ... } # Team B — gets account 222222222222 export AWS_ACCESS_KEY_ID=222222222222 export AWS_SECRET_ACCESS_KEY=test aws --endpoint-url=http://localhost:4566 sts get-caller-identity # → { "Account": "222222222222", ... } ``` All ARNs and resource state (SQS queues, Lambda functions, IAM roles, S3 buckets, DynamoDB tables, etc.) are fully isolated per account. Resources with the same name in different accounts never collide. This allows multiple developers or CI pipelines to share a single MiniStack endpoint with complete tenant isolation — no extra setup needed. Since 1.4.0 state is additionally isolated per **region** — see [Multi-Region](#multi-region) below. | Access Key | Account ID Used | |---|---| | `111111111111` | `111111111111` | | `048408301323` | `048408301323` | | `test` | `000000000000` (default) | | `AKIA_TEST_KEY_DO_NOT_USE` | `000000000000` (default) | **Terraform** — set `access_key` in your provider block: ```hcl provider "aws" { access_key = "048408301323" secret_key = "test" region = "us-east-1" endpoints { ... } } ``` **boto3** — pass `aws_access_key_id`: ```python boto3.client("s3", endpoint_url="http://localhost:4566", aws_access_key_id="048408301323", aws_secret_access_key="test", ) ``` --- ## Multi-Region Since **1.4.0**, state is isolated per **region** as well as per account — no configuration needed. The region comes from the SigV4 credential scope (the `region_name` your SDK or `--region` your CLI is configured with), so two clients pointed at different regions see fully independent resources, exactly like real AWS: ```bash # Same queue name, two regions — two independent queues aws --endpoint-url=http://localhost:4566 --region us-east-1 sqs create-queue --queue-name jobs aws --endpoint-url=http://localhost:4566 --region eu-west-1 sqs create-queue --queue-name jobs aws --endpoint-url=http://localhost:4566 --region us-east-1 sqs list-queues # → .../000000000000/jobs (the us-east-1 queue) aws --endpoint-url=http://localhost:4566 --region eu-west-1 sqs list-queues # → .../000000000000/jobs (a different queue — eu-west-1's) ``` ```python use1 = boto3.client("dynamodb", endpoint_url="http://localhost:4566", aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1") euw1 = boto3.client("dynamodb", endpoint_url="http://localhost:4566", aws_access_key_id="test", aws_secret_access_key="test", region_name="eu-west-1") use1.create_table(TableName="users", KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], BillingMode="PAY_PER_REQUEST") use1.list_tables()["TableNames"] # → ["users"] euw1.list_tables()["TableNames"] # → [] — eu-west-1 is independent ``` **Region-isolated services:** as of 1.5.3, every regional service is region-isolated except S3 and Aurora DSQL (the isolation migrations rolled out service by service from 1.4.0 to 1.4.12 — see the CHANGELOG for the per-release history; services introduced later, like AWS Config and the Cloud Control API in 1.4.14, were born region-scoped). Cross-resource references resolve in the referenced ARN's own account and region (SNS→SQS fanout, EventBridge targets, event source mappings), and cross-region references that real AWS rejects return the same errors AWS returns — e.g. invoking a `eu-west-1` Lambda from a `us-east-1` Step Functions task fails with `Functions from 'eu-west-1' are not reachable in this region`, exactly as on AWS. **Not yet region-isolated:** S3 and Aurora DSQL — these share state across regions within an account (as all services did before 1.4.0); use unique resource names there if your tests exercise two regions, until their isolation lands in a subsequent release. IAM/STS, CloudFront, Route 53, and Organizations are global services on AWS, so their account-scoped shared state is correct, not a gap. **Upgrading with `PERSIST_STATE=1`:** existing state files load and migrate automatically (on-disk format v2 with a version stamp — a newer-format file is refused instead of mis-parsed on downgrade). Each record's region is recovered from its stored ARNs; legacy records that carry no ARN migrate to the default region (`MINISTACK_REGION`). --- ## Using with AWS CLI ```bash # Option A — environment variables (no profile needed) export AWS_ACCESS_KEY_ID=test export AWS_SECRET_ACCESS_KEY=test export AWS_DEFAULT_REGION=us-east-1 aws --endpoint-url=http://localhost:4566 s3 mb s3://my-bucket aws --endpoint-url=http://localhost:4566 sqs create-queue --queue-name my-queue aws --endpoint-url=http://localhost:4566 dynamodb list-tables aws --endpoint-url=http://localhost:4566 sts get-caller-identity # Option B — named profile (must pass --profile on every command) aws configure --profile local # AWS Access Key ID: test # AWS Secret Access Key: test # Default region: us-east-1 # Default output format: json aws --profile local --endpoint-url=http://localhost:4566 s3 mb s3://my-bucket aws --profile local --endpoint-url=http://localhost:4566 s3 cp ./file.txt s3://my-bucket/ aws --profile local --endpoint-url=http://localhost:4566 sqs create-queue --queue-name my-queue aws --profile local --endpoint-url=http://localhost:4566 dynamodb list-tables aws --profile local --endpoint-url=http://localhost:4566 sts get-caller-identity ``` ### awslocal wrapper ```bash chmod +x bin/awslocal ./bin/awslocal s3 ls ./bin/awslocal dynamodb list-tables ``` --- ## Using with boto3 ```python import boto3 # All clients use the same endpoint def client(service): return boto3.client( service, endpoint_url="http://localhost:4566", aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1", ) # S3 s3 = client("s3") s3.create_bucket(Bucket="my-bucket") s3.put_object(Bucket="my-bucket", Key="hello.txt", Body=b"Hello, MiniStack!") obj = s3.get_object(Bucket="my-bucket", Key="hello.txt") print(obj["Body"].read()) # b'Hello, MiniStack!' # SQS sqs = client("sqs") q = sqs.create_queue(QueueName="my-queue") sqs.send_message(QueueUrl=q["QueueUrl"], MessageBody="hello") msgs = sqs.receive_message(QueueUrl=q["QueueUrl"]) print(msgs["Messages"][0]["Body"]) # hello # DynamoDB ddb = client("dynamodb") ddb.create_table( TableName="Users", KeySchema=[{"AttributeName": "userId", "KeyType": "HASH"}], AttributeDefinitions=[{"AttributeName": "userId", "AttributeType": "S"}], BillingMode="PAY_PER_REQUEST", ) ddb.put_item(TableName="Users", Item={"userId": {"S": "u1"}, "name": {"S": "Alice"}}) # SSM Parameter Store ssm = client("ssm") ssm.put_parameter(Name="/app/db/host", Value="localhost", Type="String") param = ssm.get_parameter(Name="/app/db/host") print(param["Parameter"]["Value"]) # localhost # Secrets Manager sm = client("secretsmanager") sm.create_secret(Name="db-password", SecretString='{"password":"s3cr3t"}') # Kinesis kin = client("kinesis") kin.create_stream(StreamName="events", ShardCount=1) kin.put_record(StreamName="events", Data=b'{"event":"click"}', PartitionKey="user1") # EventBridge eb = client("events") eb.put_events(Entries=[{ "Source": "myapp", "DetailType": "UserSignup", "Detail": '{"userId": "123"}', "EventBusName": "default", }]) # Step Functions sfn = client("stepfunctions") sfn.create_state_machine( name="my-workflow", definition='{"StartAt":"Hello","States":{"Hello":{"Type":"Pass","End":true}}}', roleArn="arn:aws:iam::000000000000:role/role", ) # Step Functions — TestState API (test a single state in isolation) # Note: inject_host_prefix=False prevents boto3 from prepending "sync-" to the hostname from botocore.config import Config as BotoConfig sfn_test = client("stepfunctions", config=BotoConfig(inject_host_prefix=False)) result = sfn_test.test_state( definition='{"Type":"Pass","Result":{"greeting":"hello"},"End":true}', input='{"name":"world"}', ) print(result["status"]) # SUCCEEDED print(result["output"]) # {"greeting": "hello"} # TestState with mock — test error handling without calling real services result = sfn_test.test_state( definition=json.dumps({ "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:000000000000:function:my-fn", "Catch": [{"ErrorEquals": ["States.ALL"], "Next": "Fallback"}], "End": True }), input='{}', inspectionLevel="DEBUG", mock={"errorOutput": {"error": "ServiceError", "cause": "Timeout"}}, ) print(result["status"]) # CAUGHT_ERROR print(result["nextState"]) # Fallback # EC2 ec2 = client("ec2") reservation = ec2.run_instances( ImageId="ami-00000001", MinCount=1, MaxCount=1, InstanceType="t3.micro", ) instance_id = reservation["Instances"][0]["InstanceId"] print(instance_id) # i-xxxxxxxxxxxxxxxxx # Security Groups sg = ec2.create_security_group(GroupName="my-sg", Description="My SG") ec2.authorize_security_group_ingress( GroupId=sg["GroupId"], IpPermissions=[{"IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "IpRanges": [{"CidrIp": "0.0.0.0/0"}]}], ) # VPC / Subnet vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16") subnet = ec2.create_subnet( VpcId=vpc["Vpc"]["VpcId"], CidrBlock="10.0.1.0/24", AvailabilityZone="us-east-1a", ) ``` --- ## Supported Services ### Core Services | Service | Operations | Notes | |---------|-----------|-------| | **S3** | CreateBucket, DeleteBucket, ListBuckets, HeadBucket, PutObject, GetObject, DeleteObject, HeadObject, CopyObject, ListObjects v1/v2, DeleteObjects, GetBucketVersioning, PutBucketVersioning, GetBucketEncryption, PutBucketEncryption, DeleteBucketEncryption, GetBucketLifecycleConfiguration, PutBucketLifecycleConfiguration, DeleteBucketLifecycle, GetBucketCors, PutBucketCors, DeleteBucketCors, GetBucketAcl, PutBucketAcl, GetBucketTagging, PutBucketTagging, DeleteBucketTagging, GetBucketPolicy, PutBucketPolicy, DeleteBucketPolicy, GetBucketNotificationConfiguration, PutBucketNotificationConfiguration, GetBucketLogging, PutBucketLogging, ListObjectVersions, CreateMultipartUpload, UploadPart, CompleteMultipartUpload, AbortMultipartUpload, PutObjectLockConfiguration, GetObjectLockConfiguration, PutObjectRetention, GetObjectRetention, PutObjectLegalHold, GetObjectLegalHold, PutBucketReplication, GetBucketReplication, DeleteBucketReplication, GetObjectTagging, PutObjectTagging, DeleteObjectTagging, PutObjectAnnotation, GetObjectAnnotation, ListObjectAnnotations, DeleteObjectAnnotation, CreateMultiRegionAccessPoint, DeleteMultiRegionAccessPoint, PutMultiRegionAccessPointPolicy, DescribeMultiRegionAccessPointOperation, GetMultiRegionAccessPoint, GetMultiRegionAccessPointPolicy, ListMultiRegionAccessPoints, GetMultiRegionAccessPointRoutes, SubmitMultiRegionAccessPointRoutes, CreateAccessPoint, GetAccessPoint, ListAccessPoints, DeleteAccessPoint, PutAccessPointPolicy, GetAccessPointPolicy, DeleteAccessPointPolicy, CreateAccessPointForObjectLambda, GetAccessPointForObjectLambda, ListAccessPointsForObjectLambda, DeleteAccessPointForObjectLambda, GetAccessPointConfigurationForObjectLambda, PutAccessPointConfigurationForObjectLambda, PutAccessPointPolicyForObjectLambda, GetAccessPointPolicyForObjectLambda, DeleteAccessPointPolicyForObjectLambda, WriteGetObjectResponse | Optional disk persistence via `S3_PERSIST=1`; Object Lock with retention & legal hold enforcement on delete, and on annotation writes under `AUTH=true` (governance mode yields to `x-amz-bypass-governance-retention`); object annotations are per version (up to 1,000, 1 byte–1 MiB each), carried by `CopyObject` and removed with their version; object tags are versioned (`?tagging&versionId=…` reads, writes, and deletes the per-version tag set); requests signed for another region than the bucket's home get `301 PermanentRedirect` with `x-amz-bucket-region`, and `CreateBucket` applies the location-constraint rules; Multi-Region Access Points are managed through S3 Control (create, delete, get, list, policy, routes) or CloudFormation (`AWS::S3::MultiRegionAccessPoint`), and `.accesspoint.s3-global.amazonaws.com` serves the active member homed in `MINISTACK_REGION` (traffic dials fail over) through the regular S3 path; S3 Access Points (S3 Control, ARN host and `-s3alias` alias, `AWS::S3::AccessPoint`) and S3 Object Lambda (S3 Control, `GetObject`/`HeadObject`/`ListObjects` transformations through a Lambda calling `WriteGetObjectResponse`, `AWS::S3ObjectLambda::*`) | | **SQS** | CreateQueue, DeleteQueue, ListQueues, GetQueueUrl, GetQueueAttributes, SetQueueAttributes, PurgeQueue, SendMessage, ReceiveMessage, DeleteMessage, ChangeMessageVisibility, ChangeMessageVisibilityBatch, SendMessageBatch, DeleteMessageBatch, TagQueue, UntagQueue, ListQueueTags | Both Query API and JSON protocol; FIFO queues with deduplication; DLQ support, with moved messages carrying `DeadLetterQueueSourceArn`; messages expire after `MessageRetentionPeriod`; the batch actions apply AWS's request-level validation (empty batch, more than ten entries, repeated or malformed entry ids) before any entry is processed | | **SNS** | CreateTopic, DeleteTopic, ListTopics, GetTopicAttributes, SetTopicAttributes, AddPermission, RemovePermission, Subscribe, Unsubscribe, ListSubscriptions, ListSubscriptionsByTopic, GetSubscriptionAttributes, SetSubscriptionAttributes, ConfirmSubscription, Publish, PublishBatch, TagResource, UntagResource, ListTagsForResource, CreatePlatformApplication, ListPlatformApplications, GetPlatformApplicationAttributes, SetPlatformApplicationAttributes, CreatePlatformEndpoint, GetEndpointAttributes, SetEndpointAttributes, DeleteEndpoint, DeletePlatformApplication | SNS→SQS fanout delivery; SNS→Lambda fanout (asynchronous delivery); subscription filter policies on message attributes or, with `FilterPolicyScope` `MessageBody`, the message body (`prefix`, `suffix`, `equals-ignore-case`, `wildcard`, `anything-but`, `numeric`, `cidr`, `exists`); FIFO topics with 5-minute deduplication, sequence numbers, content-based deduplication, and subscription validation; mobile-push endpoint lifecycle with device-token dedup and `Publish` to platform-endpoint `TargetArn` | | **DynamoDB** | CreateTable, UpdateTable, DeleteTable, DescribeTable, ListTables, PutItem, GetItem, DeleteItem, UpdateItem, Query, Scan, BatchWriteItem, BatchGetItem, TransactWriteItems, TransactGetItems, DescribeTimeToLive, UpdateTimeToLive, DescribeContinuousBackups, UpdateContinuousBackups, DescribeEndpoints, TagResource, UntagResource, ListTagsOfResource, EnableKinesisStreamingDestination, DisableKinesisStreamingDestination, DescribeKinesisStreamingDestination, UpdateKinesisStreamingDestination | TTL enforced via thread-safe background reaper (60s cadence); DynamoDB Streams — `StreamSpecification` emits INSERT/MODIFY/REMOVE records on every write that changes an item, respects `StreamViewType`; `Query` and `Scan` pages stop at 1 MB of data read; GSIs accept multi-attribute keys (up to four `HASH` and four `RANGE` attributes); Kinesis streaming destinations (`aws_dynamodb_kinesis_streaming_destination`) fan item mutations out into any Kinesis stream by ARN while the destination is ACTIVE; global tables (version 2019.11.21) through `UpdateTable` `ReplicaUpdates`: a replica is a copy of the table in the other region, and writes replicate between them | | **DynamoDB Streams** | ListStreams, DescribeStream, GetShardIterator, GetRecords | Reads records emitted by the main DynamoDB service via `boto3.client("dynamodbstreams")` — single synthetic shard per stream; `TRIM_HORIZON`/`LATEST`/`AT_SEQUENCE_NUMBER`/`AFTER_SEQUENCE_NUMBER` iterator types; `NEW_AND_OLD_IMAGES`, `NEW_IMAGE`, `OLD_IMAGE`, `KEYS_ONLY` view types; opaque base64 iterator tokens that expire after 15 minutes; `GetRecords` pages stop at 1,000 records or 1 MB; a disabled stream, or the stream of a deleted table, stays readable for 24 hours as `DISABLED` | | **Lambda** | CreateFunction, DeleteFunction, GetFunction, GetFunctionConfiguration, ListFunctions, Invoke, UpdateFunctionCode, UpdateFunctionConfiguration, AddPermission, RemovePermission, GetPolicy, ListVersionsByFunction, PublishVersion, CreateAlias, GetAlias, UpdateAlias, DeleteAlias, ListAliases, TagResource, UntagResource, ListTags, CreateEventSourceMapping, DeleteEventSourceMapping, GetEventSourceMapping, ListEventSourceMappings, UpdateEventSourceMapping, CreateFunctionUrlConfig, GetFunctionUrlConfig, UpdateFunctionUrlConfig, DeleteFunctionUrlConfig, ListFunctionUrlConfigs, PutFunctionConcurrency, GetFunctionConcurrency, DeleteFunctionConcurrency, PutFunctionEventInvokeConfig, GetFunctionEventInvokeConfig, DeleteFunctionEventInvokeConfig, PublishLayerVersion, GetLayerVersion, GetLayerVersionByArn, ListLayerVersions, DeleteLayerVersion, ListLayers, AddLayerVersionPermission, RemoveLayerVersionPermission, GetLayerVersionPolicy, CheckpointDurableExecution, GetDurableExecution, GetDurableExecutionState, GetDurableExecutionHistory, ListDurableExecutionsByFunction, StopDurableExecution, SendDurableExecutionCallbackSuccess, SendDurableExecutionCallbackFailure, SendDurableExecutionCallbackHeartbeat | Python and Node.js runtimes execute with warm worker pool; `provided.al2023`/`provided.al2` runtimes reuse local bootstrap processes via the Lambda Runtime API (Go, Rust, C++ support), or use Docker RIE when configured; `Publish=True` creates immutable numbered versions; Code via `ZipFile`, `S3Bucket`/`S3Key` (with optional `S3ObjectVersion`), or `ImageUri` (Docker image); `PackageType: Image` pulls and invokes user-provided Docker images via Lambda RIE; SQS, Kinesis, and DynamoDB Streams event source mappings; Function URL CRUD; Lambda Layers CRUD; Aliases; Concurrency; EventInvokeConfig; **SnapStart** — `SnapStart.ApplyOn=PublishedVersions` round-trips on `CreateFunction`/`UpdateFunctionConfiguration` (Java 11+/Python 3.12+/.NET 8+ and container images, per AWS's constraints); publishing a version initializes its execution environment then and there, so the version reports `OptimizationStatus: On`, walks `Pending` → `Active`, answers `ResourceConflictException` while `Pending`, fails the publish on a broken init, and runs `snapshot-restore-py` before-snapshot/after-restore hooks during init (Python); **Durable Functions** — `CreateFunction` accepts `DurableConfig`; checkpoint/state/history/list/stop ops at the preview API (`2025-12-01`); external `SendCallback{Success,Failure,Heartbeat}` resume the SDK across invocations; resume scheduler fires WAIT expiries, callback timeouts, and step-retry backoffs; verified against the official `aws-durable-execution-sdk-python` and `aws-durable-execution-sdk-java`; **X-Ray active tracing** — `TracingConfig.Mode=Active` injects `_X_AMZN_TRACE_ID` (`Root=1--;Parent=;Sampled=1`) into the runtime per invocation so the AWS X-Ray SDK runs without `Missing AWS Lambda trace data`; supported on the warm Python / Node executor, provided runtimes, and the local subprocess fallback (docker RIE upstream does not implement X-Ray and is logged but unsupported) | | **IAM** | CreateUser, GetUser, ListUsers, DeleteUser, CreateRole, GetRole, ListRoles, DeleteRole, UpdateRoleDescription, PutRolePermissionsBoundary, DeleteRolePermissionsBoundary, PutUserPermissionsBoundary, DeleteUserPermissionsBoundary, CreatePolicy, GetPolicy, DeletePolicy, AttachRolePolicy, DetachRolePolicy, PutRolePolicy, GetRolePolicy, DeleteRolePolicy, ListRolePolicies, ListAttachedRolePolicies, CreateAccessKey, ListAccessKeys, DeleteAccessKey, UpdateAccessKey, GetAccessKeyLastUsed, CreateInstanceProfile, GetInstanceProfile, DeleteInstanceProfile, AddRoleToInstanceProfile, RemoveRoleFromInstanceProfile, ListInstanceProfiles, TagInstanceProfile, UntagInstanceProfile, ListInstanceProfileTags, CreateGroup, GetGroup, AddUserToGroup, RemoveUserFromGroup, AttachGroupPolicy, DetachGroupPolicy, ListAttachedGroupPolicies, PutGroupPolicy, GetGroupPolicy, DeleteGroupPolicy, ListGroupPolicies, CreateServiceLinkedRole, DeleteServiceLinkedRole, GetServiceLinkedRoleDeletionStatus, CreateOpenIDConnectProvider, ListOpenIDConnectProviders, CreateSAMLProvider, GetSAMLProvider, ListSAMLProviders, UpdateSAMLProvider, DeleteSAMLProvider, CreateLoginProfile, GetLoginProfile, UpdateLoginProfile, DeleteLoginProfile, CreateVirtualMFADevice, EnableMFADevice, DeactivateMFADevice, ResyncMFADevice, ListMFADevices, ListVirtualMFADevices, DeleteVirtualMFADevice, GetAccountAuthorizationDetails, GenerateServiceLastAccessedDetails, GetServiceLastAccessedDetails, TagRole, UntagRole, TagUser, UntagUser, TagPolicy, UntagPolicy, GenerateCredentialReport, GetCredentialReport, GetAccountSummary, GetAccountPasswordPolicy, UpdateAccountPasswordPolicy, DeleteAccountPasswordPolicy, ListAccountAliases, CreateAccountAlias, DeleteAccountAlias, CreateServiceSpecificCredential, ListServiceSpecificCredentials, UpdateServiceSpecificCredential, ResetServiceSpecificCredential, DeleteServiceSpecificCredential, UploadServerCertificate, ListServerCertificates, GetServerCertificate, UpdateServerCertificate, DeleteServerCertificate, ListServerCertificateTags | Role descriptions and permissions boundaries round-trip through role and user reads; boundaries support put/delete updates and limit permissions under `AUTH=true`. | | **STS** | GetCallerIdentity, AssumeRole, GetSessionToken, GetFederationToken, AssumeRoleWithWebIdentity | | | **IMDS** (EC2 Instance Metadata) | `PUT /latest/api/token`, `GET /latest/meta-data/instance-id`, `GET /latest/meta-data/iam/security-credentials/`, `GET /latest/meta-data/iam/security-credentials/`, `GET /latest/meta-data/iam/info`, `GET /latest/meta-data/placement/{region,availability-zone,...}`, `GET /latest/dynamic/instance-identity/document` | IMDSv1 + IMDSv2; default credential chain falls through to a `ministack-instance-role` document with `ASIA*` session creds. Point SDKs at ministack via `AWS_EC2_METADATA_SERVICE_ENDPOINT=http://localhost:4566` (or `ec2_metadata_service_endpoint` in `~/.aws/config`); set `MINISTACK_IMDS_V2_REQUIRED=1` to require the token PUT | | **ECS Task Metadata V4** | `GET /v4/`, `GET /v4//task`, `GET /v4//stats`, `GET /v4//task/stats` | Per-container token injected as `ECS_CONTAINER_METADATA_URI_V4` on every container started by `RunTask`. `/task` returns sibling containers in the same task. Containers reach the gateway via `host.docker.internal` (mapped through `extra_hosts: host-gateway`, so it works on user-defined Docker networks); `networkMode: host` containers use loopback. Volatile by design (stripped on persistence, cleared by `/_ministack/reset`) | | **ECS Container Credentials** | `GET /v2/credentials/` | The path real ECS exposes via `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/` (resolved by SDKs against `169.254.170.2`). MiniStack serves the same path on the gateway and returns the AWS-strict 5-field credentials document (`AccessKeyId`, `SecretAccessKey`, `Token`, `Expiration`, `RoleArn`) — distinct from the IMDS shape served at `/latest/meta-data/iam/security-credentials/`. `RunTask` injects `AWS_CONTAINER_CREDENTIALS_FULL_URI`, `AWS_CONTAINER_AUTHORIZATION_TOKEN` (satisfies botocore's allow-list for non-loopback gateway hosts), and `AWS_ENDPOINT_URL` so unmodified SDKs inside the task fetch credentials and route service calls through MiniStack with no client config | | **SecretsManager** | CreateSecret, GetSecretValue, ListSecrets, DeleteSecret, UpdateSecret, DescribeSecret, PutSecretValue, UpdateSecretVersionStage, RestoreSecret, RotateSecret, GetRandomPassword, ListSecretVersionIds, ReplicateSecretToRegions, TagResource, UntagResource, PutResourcePolicy, GetResourcePolicy, DeleteResourcePolicy, ValidateResourcePolicy | | | **CloudWatch Logs** | CreateLogGroup, DeleteLogGroup, DescribeLogGroups, CreateLogStream, DeleteLogStream, DescribeLogStreams, PutLogEvents, GetLogEvents, FilterLogEvents, GetLogRecord, GetLogGroupFields, StartLiveTail, PutRetentionPolicy, DeleteRetentionPolicy, PutSubscriptionFilter, DeleteSubscriptionFilter, DescribeSubscriptionFilters, PutMetricFilter, DeleteMetricFilter, DescribeMetricFilters, TagLogGroup, UntagLogGroup, ListTagsLogGroup, TagResource, UntagResource, ListTagsForResource, StartQuery, GetQueryResults, StopQuery, PutDestination, DeleteDestination, DescribeDestinations, PutDestinationPolicy, PutDeliverySource, GetDeliverySource, DeleteDeliverySource, DescribeDeliverySources, PutDeliveryDestination, GetDeliveryDestination, DeleteDeliveryDestination, DescribeDeliveryDestinations, PutDeliveryDestinationPolicy, GetDeliveryDestinationPolicy, DeleteDeliveryDestinationPolicy, CreateDelivery, GetDelivery, DeleteDelivery, DescribeDeliveries, PutResourcePolicy, DescribeResourcePolicies, DeleteResourcePolicy | `FilterLogEvents` supports `*`/`?` globs, multi-term AND, `-term` exclusion; `GetLogRecord` resolves Insights `@ptr` values issued at `PutLogEvents`; `GetLogGroupFields` scans recent stored events (optional `time` ±8m, else last 15m) and returns field names with rough presence percent, including system `@*` fields and flattened JSON message keys; Insights CWLI subset: `fields`, chained `| filter` (`=` / `like /regex/`), `| sort @timestamp`, post-filter `| limit` as `min(query, API)`; `StartLiveTail` holds an AWS eventstream open until client disconnect and fans matching `PutLogEvents` into `sessionUpdate` frames (1s idle heartbeats; buffer of 10 updates, oldest dropped when full); use `Config(inject_host_prefix=False)` with a custom endpoint so boto3 does not prepend `stream-` to the hostname; `DescribeLogGroups` includes both `arn` (`:*`) and `logGroupArn` (no star) | ### Extended Services | Service | Operations | Notes | |---------|-----------|-------| | **SSM Parameter Store** | PutParameter, GetParameter, GetParameters, GetParametersByPath, DeleteParameter, DeleteParameters, DescribeParameters, GetParameterHistory, LabelParameterVersion, AddTagsToResource, RemoveTagsFromResource, ListTagsForResource, GetServiceSetting, UpdateServiceSetting, ResetServiceSetting, CreateDocument, DescribeDocument, GetDocument, ListDocuments, DeleteDocument, StartAutomationExecution, GetAutomationExecution, DescribeAutomationExecutions, StopAutomationExecution | Supports String, SecureString, StringList; `name:version` and `name:label` selectors on `GetParameter` / `GetParameters`; automation executions run `aws:sleep`, `aws:executeAwsApi` (Service ssm) and `aws:runCommand` for real, other step actions fail honestly | | **EventBridge** | CreateEventBus, UpdateEventBus, DeleteEventBus, ListEventBuses, DescribeEventBus, PutRule, DeleteRule, ListRules, DescribeRule, EnableRule, DisableRule, PutTargets, RemoveTargets, ListTargetsByRule, ListRuleNamesByTarget, PutEvents, TestEventPattern, TagResource, UntagResource, ListTagsForResource, CreateArchive, DeleteArchive, DescribeArchive, UpdateArchive, ListArchives, PutPermission, RemovePermission, CreateConnection, DescribeConnection, DeleteConnection, UpdateConnection, DeauthorizeConnection, ListConnections, CreateApiDestination, DescribeApiDestination, DeleteApiDestination, UpdateApiDestination, ListApiDestinations, StartReplay, DescribeReplay, ListReplays, CancelReplay, CreateEndpoint, DeleteEndpoint, DescribeEndpoint, ListEndpoints, UpdateEndpoint, ActivateEventSource, DeactivateEventSource, DescribeEventSource, CreatePartnerEventSource, DeletePartnerEventSource, DescribePartnerEventSource, ListPartnerEventSources, ListPartnerEventSourceAccounts, ListEventSources, PutPartnerEvents | Lambda target dispatch on PutEvents; S3 EventBridge notifications; archives capture matching events and `StartReplay` re-dispatches them to the destination bus in a background thread; API destination targets are **invoked over HTTP** on PutEvents — connection auth (Basic, API Key, OAuth client-credentials with token caching and 401/407 refresh), connection/target header/query/body parameter merging with connection precedence, `*` path parameters, dynamic JSON-path parameter values resolved from the untransformed event, and the AWS default headers (`User-Agent: Amazon/EventBridge/ApiDestinations`, default `Content-Type: application/json; charset=utf-8`), which connection and target parameters cannot override; redirects are not followed (a `3xx` is a non-retryable delivery failure), only `http(s)` invocation and OAuth authorization endpoints are accepted, `HttpMethod` and `AuthorizationType` are validated against their enums, and a connection that is not `AUTHORIZED` is not invoked (`DeauthorizeConnection` clears its stored credentials) — the 24h retry pipeline, rate limiting, and DLQs are not modeled; SaaS/partner APIs are otherwise control-plane stubs. Event patterns are **validated** — `PutRule`, `TestEventPattern`, `CreateArchive` and `UpdateArchive` reject a malformed pattern with `InvalidEventPatternException` carrying AWS's own reason text — and evaluate `prefix`/`suffix` (plain or case-insensitive via a nested `equals-ignore-case`), `wildcard` (AWS semantics — only `*` is special, `\` escapes — with catastrophic backtracking ruled out), `equals-ignore-case` (AWS's per-character case alternation), `exactly`, `cidr` (IPv4/IPv6, host bits floored, `/32` and `/128` refused as AWS refuses them), `numeric` (as the grammar it is: one comparison or a lower-then-upper bound), `exists` (leaf nodes only, so an object or an empty array does not exist), `anything-but` (a literal, a list, or a nested `prefix`/`suffix`/`wildcard`/`equals-ignore-case`, singly or as a list), and `$or` at any field position — expanded into flat alternatives at rule creation, so AWS's per-leaf last-write-wins collision rule and its 1000-combination cap both hold. All ten envelope fields are filterable, an unmodelled key is a path the event lacks rather than an error, `resources` is an intersection, array values are matched per element (with AWS's array-consistency rule), and values compare as JSON so a string never equals a number. An operand or key the matcher cannot evaluate never matches, rather than raising or matching everything. Not modeled: the 4096-character pattern limit, the wildcard-complexity refusal, and the 256-leaf rule-size cap | | **Kinesis** | CreateStream, DeleteStream, DescribeStream, DescribeStreamSummary, ListStreams, ListShards, PutRecord, PutRecords, GetShardIterator, GetRecords, IncreaseStreamRetentionPeriod, DecreaseStreamRetentionPeriod, MergeShards, SplitShard, UpdateShardCount, StartStreamEncryption, StopStreamEncryption, EnableEnhancedMonitoring, DisableEnhancedMonitoring, RegisterStreamConsumer, DeregisterStreamConsumer, ListStreamConsumers, DescribeStreamConsumer, SubscribeToShard, AddTagsToStream, RemoveTagsFromStream, ListTagsForStream, PutResourcePolicy, GetResourcePolicy, DeleteResourcePolicy | Partition key → shard routing; AWS limits enforced (1 MB/record, 500 records/batch, 5 MB payload, 256-char partition key); SubscribeToShard pushes `SubscribeToShardEvent`s over HTTP/1.1 or HTTP/2 (h2c) for up to 5 minutes, JSON or CBOR | | **CloudWatch Metrics** | PutMetricData, GetMetricStatistics, GetMetricData, ListMetrics, PutMetricAlarm, PutCompositeAlarm, DescribeAlarms, DescribeAlarmsForMetric, DescribeAlarmHistory, DeleteAlarms, SetAlarmState, EnableAlarmActions, DisableAlarmActions, TagResource, UntagResource, ListTagsForResource, PutDashboard, GetDashboard, DeleteDashboards, ListDashboards | CBOR and JSON protocol | | **SES** | SendEmail, SendRawEmail, SendTemplatedEmail, SendBulkTemplatedEmail, VerifyEmailIdentity, VerifyEmailAddress, VerifyDomainIdentity, VerifyDomainDkim, ListIdentities, ListVerifiedEmailAddresses, GetIdentityVerificationAttributes, GetIdentityDkimAttributes, DeleteIdentity, GetSendQuota, GetSendStatistics, SetIdentityNotificationTopic, SetIdentityFeedbackForwardingEnabled, CreateConfigurationSet, DeleteConfigurationSet, DescribeConfigurationSet, ListConfigurationSets, CreateTemplate, GetTemplate, UpdateTemplate, DeleteTemplate, ListTemplates, CreateReceiptRuleSet, DescribeReceiptRuleSet, ListReceiptRuleSets, DeleteReceiptRuleSet, SetActiveReceiptRuleSet, DescribeActiveReceiptRuleSet, CreateReceiptRule, DescribeReceiptRule, DeleteReceiptRule | Emails stored in-memory, not sent; the sender must be a verified identity (`MessageRejected` otherwise), and a sandboxed account sends only to verified or `@simulator.amazonses.com` recipients; receipt rule sets and ordered rules, with every action type, are stored and persisted; inbound email receiving is not emulated; a send publishes a `Delivery`, `Bounce` or `Complaint` notification per recipient to the identity's `SetIdentityNotificationTopic` SNS topic, following the mailbox simulator | | **SES v2** | SendEmail, CreateEmailIdentity, GetEmailIdentity, DeleteEmailIdentity, ListEmailIdentities, CreateConfigurationSet, GetConfigurationSet, DeleteConfigurationSet, ListConfigurationSets, PutConfigurationSetTrackingOptions, PutConfigurationSetDeliveryOptions, PutConfigurationSetReputationOptions, PutConfigurationSetSendingOptions, PutConfigurationSetSuppressionOptions, PutConfigurationSetVdmOptions, PutConfigurationSetArchivingOptions, CreateConfigurationSetEventDestination, GetConfigurationSetEventDestinations, UpdateConfigurationSetEventDestination, DeleteConfigurationSetEventDestination, CreateEmailTemplate, GetEmailTemplate, UpdateEmailTemplate, DeleteEmailTemplate, ListEmailTemplates, GetAccount, PutAccountDetails, PutAccountSuppressionAttributes, ListSuppressedDestinations, TagResource, UntagResource, ListTagsForResource, CreateTenant, GetTenant, ListTenants, DeleteTenant, CreateTenantResourceAssociation, DeleteTenantResourceAssociation, ListTenantResources, ListResourceTenants, PutTenantSuppressionAttributes, CreateDedicatedIpPool, GetDedicatedIpPool, ListDedicatedIpPools, DeleteDedicatedIpPool | REST API (`/v2/email/`); identities auto-verified; emails stored in-memory, not sent; `PutAccountDetails` with `ProductionAccessEnabled=false` puts the account in the sandbox; email templates share the v1 store, so `SendEmail` with `Content.Template` renders a template created through either API version; a configuration set, identity or template associated with a tenant cannot be deleted until the association is removed; dedicated IP pools are records only, with no IP addresses behind them | | **ACM** | RequestCertificate, DescribeCertificate, ListCertificates, DeleteCertificate, GetCertificate, ImportCertificate, AddTagsToCertificate, RemoveTagsFromCertificate, ListTagsForCertificate, UpdateCertificateOptions, RenewCertificate, ResendValidationEmail | Certificates auto-issued; DNS validation records generated; supports SANs | | **Backup** | CreateBackupVault, DescribeBackupVault, DeleteBackupVault, ListBackupVaults, CreateBackupPlan, GetBackupPlan, UpdateBackupPlan, DeleteBackupPlan, ListBackupPlans, ListBackupPlanVersions, CreateBackupSelection, GetBackupSelection, DeleteBackupSelection, ListBackupSelections, StartBackupJob, StopBackupJob, DescribeBackupJob, ListBackupJobs, TagResource, UntagResource, ListTags | In-memory; jobs complete immediately; vaults and plans participate in Resource Groups Tagging API | | **Bedrock** | 66 operations — foundation-model catalog (real model IDs), inference profiles (system + application-managed), guardrails with versioning, custom models, imported models, provisioned model throughput, model customization / import / copy / batch-invocation job families, tagging | All 66 operations verified against botocore `bedrock-2023-04-20`; camelCase wire format per `locationName`; account- and region-scoped state | | **Bedrock Runtime** | Converse, ConverseStream, InvokeModel, InvokeModelWithResponseStream, ApplyGuardrail, StartAsyncInvoke, GetAsyncInvoke, ListAsyncInvokes | Real eventstream wire format on streaming operations; **guardrails enforce their deterministic policies** — word policy, sensitive-information regexes, and the pattern-matchable PII types with `BLOCK`/`ANONYMIZE` per input/output stage — on `ApplyGuardrail` and on `Converse`/`ConverseStream` via `guardrailConfig` (`GUARDRAIL_INTERVENED`, blocked messaging, per-policy assessments, `ResourceNotFoundException` for an unknown id); the model-graded policies (topic, content, contextual grounding, `PROFANITY`) are not evaluated; deterministic family-aware mock responses selected by model ID prefix (`anthropic.*`, `amazon.titan*`, `amazon.nova*`, `meta.llama*`, `mistral.*`, `cohere.*`, `ai21.*`); verified against botocore `bedrock-runtime-2023-09-30` | | **Bedrock Agent** | 72 operations — agents (versions, aliases, action groups, collaborators, agent knowledge bases, PrepareAgent), knowledge bases (data sources, ingestion jobs — `StartIngestionJob` reads the S3 data source and reports real statistics, `FAILED` with `failureReasons` when the source is unreadable — documents), flows (aliases, versions, ValidateFlowDefinition, PrepareFlow), prompts with versions, tagging | All 72 operations verified against botocore `bedrock-agent-2023-06-05` | | **Bedrock Agent Runtime** | InvokeAgent, InvokeInlineAgent, GetAgentMemory, DeleteAgentMemory, Retrieve, RetrieveAndGenerate, RetrieveAndGenerateStream, Rerank, CreateSession, GetSession, UpdateSession, DeleteSession, EndSession, ListSessions, CreateInvocation, ListInvocations, PutInvocationStep, GetInvocationStep, ListInvocationSteps, InvokeFlow, StartFlowExecution, StopFlowExecution, GetFlowExecution, ListFlowExecutions, ListFlowExecutionEvents, GetExecutionFlowSnapshot, OptimizePrompt | All 31 operations verified against botocore `bedrock-agent-runtime-2023-07-26`; eventstream responses on streaming operations; `Retrieve` searches the documents Knowledge Base ingestion stored (lexical scoring — no embeddings) and returns content, `s3Location` and score; `InvokeAgent` answers a canned reply without orchestration and emits no trace events — it never fabricates an `ORCHESTRATE` trace | | **Bedrock AgentCore** | Control plane (`bedrock-agentcore-control`): CreateAgentRuntime, GetAgentRuntime, ListAgentRuntimes, UpdateAgentRuntime, DeleteAgentRuntime, ListAgentRuntimeVersions, CreateAgentRuntimeEndpoint, GetAgentRuntimeEndpoint, ListAgentRuntimeEndpoints, UpdateAgentRuntimeEndpoint, DeleteAgentRuntimeEndpoint, CreateMemory, GetMemory, ListMemories, UpdateMemory, DeleteMemory, PutResourcePolicy, GetResourcePolicy, DeleteResourcePolicy. Data plane (`bedrock-agentcore`): InvokeAgentRuntime, CreateEvent, GetEvent, ListEvents, DeleteEvent, ListActors, ListSessions, BatchCreateMemoryRecords, BatchUpdateMemoryRecords, BatchDeleteMemoryRecords, GetMemoryRecord, ListMemoryRecords, DeleteMemoryRecord, RetrieveMemoryRecords, StartMemoryExtractionJob, ListMemoryExtractionJobs | Agent-runtime lifecycle with `CREATING`/`UPDATING`→`READY` transitions, versioned ARNs (`agent/{uuid}:{version}`, `agentEndpoint/{uuid}`); request/response shapes verified against botocore `bedrock-agentcore-control` / `bedrock-agentcore`; account- and region-scoped state; Memory resources record their strategies, but no extraction runs; resource policies are evaluated for runtime and endpoint invocation when `AUTH=true`; `InvokeAgentRuntime` runs the runtime's `containerUri` image in Docker (full image), waits for `/ping` and forwards to `/invocations` on port 8080, with container errors as `RuntimeClientError` (424); without Docker (slim image) or for a code artifact it answers a deterministic echo | | **Transcribe** | StartTranscriptionJob, GetTranscriptionJob, ListTranscriptionJobs, DeleteTranscriptionJob | Batch transcription jobs only; streaming is a separate endpoint (`transcribestreaming`). All four operations verified against botocore `transcribe-2017-10-26`. A job starts `IN_PROGRESS` with `StartTime` set, as on AWS, and reaches `COMPLETED` over two seconds; `QUEUED`, which AWS reaches only for a request that opted into job queueing (`JobExecutionSettings.AllowDeferredExecution`) while the account is at its concurrent job limit, is not modelled; the media object is read from MiniStack S3 via an `s3://`, path-style or virtual-hosted HTTPS URI, and an unreadable URI fails the job with a `FailureReason` rather than erroring at `Start`, as on AWS; the transcript is written back to S3 in the real result format (`results.transcripts`, word-level `results.items`, `results.audio_segments`, plus `results.segments` and `results.speaker_labels` when the matching `Settings` are set) at `OutputBucketName`/`OutputKey`, or to an auto-created `ministack-transcribe-output` bucket. `IdentifyLanguage` and `IdentifyMultipleLanguages` resolve a language on completion, `ContentRedaction` controls whether a `redacted-` copy replaces or accompanies the transcript, and `Subtitles` writes WebVTT and SubRip files, redacted alongside the transcript when redaction is on. Reaching a terminal state publishes a `Transcribe Job State Change` event, so EventBridge rules downstream of a job fire. There is no speech recognition, so the transcript is deterministic canned text keyed on a digest of the media URI | | **Translate** | StartTextTranslationJob, DescribeTextTranslationJob, ListTextTranslationJobs, StopTextTranslationJob | Batch text translation jobs only; the synchronous `TranslateText` / `TranslateDocument` pair and the custom terminology / parallel data families are not implemented. All four operations verified against botocore `translate-2017-07-01`. Jobs walk `SUBMITTED` → `IN_PROGRESS` → `COMPLETED` over `TRANSLATE_JOB_RUN_SECONDS` (default 2, `0` completes immediately); every document under `InputDataConfig.S3Uri` is read from MiniStack S3 and written back to the rewritten output location AWS uses — `OutputDataConfig.S3Uri` is reported as `s3:///-TranslateText-/` — as `.`, with a `details/.auxiliary-translation-details.json` summary per target language. `text/plain` is translated line by line, `text/html` keeps its markup and translates only text nodes, `application/x-xliff+xml` gets a populated `` in every `` plus `target-language` on every ``, and the three Office ZIP types are copied through. A document that can't be read as its content type is counted in `JobDetails` and the job ends `COMPLETED_WITH_ERROR`; an empty or unreadable input location fails the job with a `Message` rather than erroring at `Start`. `ClientToken` is idempotent, `StopTextTranslationJob` reaches a running worker, and reaching a terminal state publishes a `Translate TextTranslationJob State Change` event, so EventBridge rules downstream of a job fire. There is no machine translation, so the output is deterministic canned text derived from the source | | **MSK** | CreateCluster, ListClusters, DescribeCluster, DeleteCluster, GetBootstrapBrokers, ListNodes, CreateConfiguration, ListConfigurations, DescribeConfiguration, ListConfigurationRevisions, DescribeConfigurationRevision, BatchAssociateScramSecret, BatchDisassociateScramSecret, ListScramSecrets, TagResource, UntagResource, ListTagsForResource | Kafka control plane; `GetBootstrapBrokers` honors `MINISTACK_MSK_BOOTSTRAP` so clients route to a real broker you bring (Redpanda, Kafka, KRaft) — the Kafka wire protocol itself is not emulated | | **WAF v2** | CreateWebACL, GetWebACL, UpdateWebACL, DeleteWebACL, ListWebACLs, AssociateWebACL, DisassociateWebACL, GetWebACLForResource, ListResourcesForWebACL, CreateIPSet, GetIPSet, UpdateIPSet, DeleteIPSet, ListIPSets, CreateRegexPatternSet, GetRegexPatternSet, UpdateRegexPatternSet, DeleteRegexPatternSet, ListRegexPatternSets, CreateRuleGroup, GetRuleGroup, UpdateRuleGroup, DeleteRuleGroup, ListRuleGroups, TagResource, UntagResource, ListTagsForResource, CheckCapacity, DescribeManagedRuleGroup | LockToken enforced on Update/Delete; resource associations tracked | | **Step Functions** | CreateStateMachine, DeleteStateMachine, DescribeStateMachine, UpdateStateMachine, ListStateMachines, StartExecution, StartSyncExecution, StopExecution, DescribeExecution, DescribeStateMachineForExecution, ListExecutions, GetExecutionHistory, SendTaskSuccess, SendTaskFailure, SendTaskHeartbeat, CreateActivity, DeleteActivity, DescribeActivity, ListActivities, GetActivityTask, TestState, TagResource, UntagResource, ListTagsForResource | Full ASL interpreter; Retry/Catch; waitForTaskToken; Activities (worker pattern); Pass/Task/Choice/Wait/Succeed/Fail/Map/Parallel; TestState API with mock and inspectionLevel support; SFN_MOCK_CONFIG for AWS SFN Local compatible mock testing; intrinsic functions (States.StringToJson, States.JsonToString, States.JsonMerge, States.Format); nested startExecution.sync | | **API Gateway v2** | CreateApi, GetApi, GetApis, UpdateApi, DeleteApi, CreateRoute, GetRoute, GetRoutes, UpdateRoute, DeleteRoute, CreateIntegration, GetIntegration, GetIntegrations, UpdateIntegration, DeleteIntegration, CreateRouteResponse, GetRouteResponse, GetRouteResponses, UpdateRouteResponse, DeleteRouteResponse, CreateIntegrationResponse, GetIntegrationResponse, GetIntegrationResponses, UpdateIntegrationResponse, DeleteIntegrationResponse, CreateStage, GetStage, GetStages, UpdateStage, DeleteStage, CreateDeployment, GetDeployment, GetDeployments, DeleteDeployment, CreateAuthorizer, GetAuthorizer, GetAuthorizers, UpdateAuthorizer, DeleteAuthorizer, CreateDomainName, GetDomainName, GetDomainNames, UpdateDomainName, DeleteDomainName, CreateApiMapping, GetApiMapping, GetApiMappings, UpdateApiMapping, DeleteApiMapping, TagResource, UntagResource, GetTags, PostToConnection, GetConnection, DeleteConnection | **HTTP API** and **WebSocket API** (`protocolType=WEBSOCKET`); Lambda proxy (`AWS_PROXY`), HTTP proxy (`HTTP_PROXY`), and MOCK integrations; HTTP data plane via `{apiId}.execute-api.localhost` Host header or path-based `/_aws/execute-api/{apiId}/{stage}/{path}` (no DNS/Host override needed — works from browsers on macOS and strict clients); `$default` stage served from the URL root (no stage segment in the path); per-API `corsConfiguration` applied to preflights + dispatched responses; request parameter mapping for HTTP_PROXY (`append/overwrite/remove` for headers/querystring plus `overwrite:path`) with context variables including `$context.authorizer.jwt.claims`; JWT data-plane authorization for HTTP routes (issuer/audience/time/scope checks) and claim propagation to integrations; qualified-alias integration URIs (`arn:...:function::`) resolve to the alias's target version; WebSocket data plane on the same two URL forms, with `$connect` / `$disconnect` / `$default` / custom-action routing, `$request.body.*` RouteSelectionExpression, `@connections` management API (PostToConnection / GetConnection / DeleteConnection), per-connection outbox for server-side push; `{param}` / `{proxy+}` matching; custom domain names and API mappings shared with API Gateway v1, routed by `Host`; JWT/Lambda authorizer CRUD; pin `apiId` across runs with the `ms-custom-id` tag | | **API Gateway v1** | CreateRestApi, GetRestApi, GetRestApis, UpdateRestApi, DeleteRestApi, PutRestApi, CreateResource, GetResource, GetResources, UpdateResource, DeleteResource, PutMethod, GetMethod, DeleteMethod, UpdateMethod, PutMethodResponse, GetMethodResponse, DeleteMethodResponse, PutIntegration, GetIntegration, DeleteIntegration, UpdateIntegration, PutIntegrationResponse, GetIntegrationResponse, DeleteIntegrationResponse, CreateDeployment, GetDeployment, GetDeployments, UpdateDeployment, DeleteDeployment, CreateStage, GetStage, GetStages, UpdateStage, DeleteStage, CreateAuthorizer, GetAuthorizer, GetAuthorizers, UpdateAuthorizer, DeleteAuthorizer, CreateModel, GetModel, GetModels, DeleteModel, CreateDocumentationPart, GetDocumentationPart, GetDocumentationParts, UpdateDocumentationPart, DeleteDocumentationPart, CreateApiKey, GetApiKey, GetApiKeys, UpdateApiKey, DeleteApiKey, CreateUsagePlan, GetUsagePlan, GetUsagePlans, UpdateUsagePlan, DeleteUsagePlan, CreateUsagePlanKey, GetUsagePlanKeys, DeleteUsagePlanKey, CreateDomainName, GetDomainName, GetDomainNames, DeleteDomainName, CreateDomainNameAccessAssociation, GetDomainNameAccessAssociations, DeleteDomainNameAccessAssociation, RejectDomainNameAccessAssociation, GenerateClientCertificate, GetClientCertificate, GetClientCertificates, UpdateClientCertificate, DeleteClientCertificate, CreateBasePathMapping, GetBasePathMapping, GetBasePathMappings, DeleteBasePathMapping, TagResource, UntagResource, GetTags, CreateRequestValidator, GetRequestValidators, GetRequestValidator, UpdateRequestValidator, DeleteRequestValidator | REST API (v1) protocol; Lambda proxy format 1.0 (AWS_PROXY), non-proxy Lambda (AWS — the handler's output is the response body, Lambda request mapping templates not modeled) and non-proxy `AWS` integrations whose URI targets SQS (`arn:aws:apigateway:{region}:sqs:path/{account}/{queue}`: a request template subset — `$input.body` / `$input.json('…')` / `$input.params('…')` / `$util.urlEncode(…)`, `$context.authorizer` values including Cognito claims, `#set`, comments and interpolated strings — renders the form-encoded SQS Query call, run in the URI's account and region, and the backend result or error is mapped through `integrationResponses`), HTTP proxy (HTTP_PROXY), MOCK integration; data plane via `{apiId}.execute-api.localhost` Host header, path-based `/_aws/execute-api/{apiId}/{stage}/{path}`, or legacy `/restapis/{apiId}/{stage}/_user_request_/{path}`; qualified-alias integration URIs (`arn:...:function::`) resolve to the alias's target version; resource tree with `{param}` and `{proxy+}` path matching; routing is on the resource+method pair, so a request the matched resource does not serve falls through to a `{proxy+}` elsewhere in the tree (a methodless intermediate, a CORS-preflight-only node, or any undeclared verb), as it does on AWS; TOKEN and REQUEST Lambda authorizers on the data plane, with the returned IAM policy evaluated against each request's own method ARN and `authorizerResultTtlInSeconds` caching the policy per stage and identity source; JSON Patch for all PATCH operations; state persistence; pin `id` across runs with the `ms-custom-id` tag | | **ELBv2 / ALB** | CreateLoadBalancer, DescribeLoadBalancers, DeleteLoadBalancer, DescribeLoadBalancerAttributes, ModifyLoadBalancerAttributes, CreateTargetGroup, DescribeTargetGroups, ModifyTargetGroup, DeleteTargetGroup, DescribeTargetGroupAttributes, ModifyTargetGroupAttributes, CreateListener, DescribeListeners, ModifyListener, DeleteListener, CreateRule, DescribeRules, ModifyRule, DeleteRule, SetRulePriorities, SetSubnets, SetIpAddressType, SetSecurityGroups, RegisterTargets, DeregisterTargets, DescribeTargetHealth, AddListenerCertificates, DescribeListenerCertificates, RemoveListenerCertificates, AddTags, RemoveTags, DescribeTags | Control plane + data plane; ALB→Lambda live traffic routing; `path-pattern`, `host-header`, `http-method`, `query-string`, `http-header` rule conditions; `forward`, `redirect`, `fixed-response`, `authenticate-oidc` actions; `url-rewrite` and `host-header-rewrite` rule transforms stored and returned (not applied to traffic); data plane via `{lb-name}.alb.localhost` Host header or `/_alb/{lb-name}/` path prefix | | **KMS** | CreateKey, ReplicateKey, ListKeys, DescribeKey, GetPublicKey, Sign, Verify, Encrypt, Decrypt, GenerateMac, VerifyMac, GenerateDataKey, GenerateDataKeyWithoutPlaintext, GenerateDataKeyPair, GenerateDataKeyPairWithoutPlaintext, GenerateRandom, CreateAlias, DeleteAlias, ListAliases, UpdateAlias, UpdateKeyDescription, EnableKeyRotation, DisableKeyRotation, GetKeyRotationStatus, GetKeyPolicy, PutKeyPolicy, ListKeyPolicies, EnableKey, DisableKey, ScheduleKeyDeletion, CancelKeyDeletion, TagResource, UntagResource, ListResourceTags, GetParametersForImport, ImportKeyMaterial, DeleteImportedKeyMaterial, CreateGrant, ListGrants, RevokeGrant, RetireGrant | 41 actions; multi-Region keys: `CreateKey` with `MultiRegion` mints an `mrk-` key and `ReplicateKey` creates a same-id replica in another region sharing the key material (ciphertext from the primary decrypts against the replica; a primary with live replicas waits in `PendingReplicaDeletion`); CMK specs: RSA (2048/3072/4096), ECC (SECG_P256K1, NIST P-256/384/521), Edwards25519 (`ECC_NIST_EDWARDS25519` — pure Ed25519 via `ED25519_SHA_512` with `MessageType=RAW`; `ED25519_PH_SHA_512` advertised in `SigningAlgorithms` but returns `UnsupportedOperationException` until Ed25519ph lands), HMAC (`HMAC_224`/`HMAC_256`/`HMAC_384`/`HMAC_512` with `KeyUsage=GENERATE_VERIFY_MAC`), and symmetric keys; PKCS1v15, PSS, and ECDSA signing; real RFC 2104 HMAC via `GenerateMac`/`VerifyMac` (4096-byte message cap, `KMSInvalidMacException` on mismatch, `DryRun` supported); envelope encryption, incl. asymmetric data key pairs (`KeyPairSpec` adds RSA_3072); alias resolution; key rotation; key policies; tags; enable/disable/schedule deletion; grants (stored and listed, not evaluated for authorization); full Terraform `aws_kms_key` compatible; `cryptography` package included in Docker image | | **CloudFront** | CreateDistribution, GetDistribution, GetDistributionConfig, ListDistributions, UpdateDistribution, DeleteDistribution, AssociateDistributionWebACL, DisassociateDistributionWebACL, CreateInvalidation, ListInvalidations, GetInvalidation, CreateOriginAccessControl, GetOriginAccessControl, GetOriginAccessControlConfig, ListOriginAccessControls, UpdateOriginAccessControl, DeleteOriginAccessControl, CreateMonitoringSubscription, GetMonitoringSubscription, DeleteMonitoringSubscription, CreateFunction, DeleteFunction, DescribeFunction, GetFunction, ListFunctions, PublishFunction, UpdateFunction, **KeyValueStore (mgmt)**: CreateKeyValueStore, DescribeKeyValueStore, ListKeyValueStores, UpdateKeyValueStore, DeleteKeyValueStore, **Policies**: CreateCachePolicy, GetCachePolicy, GetCachePolicyConfig, UpdateCachePolicy, DeleteCachePolicy, ListDistributionsByCachePolicyId, CreateOriginRequestPolicy, GetOriginRequestPolicy, GetOriginRequestPolicyConfig, UpdateOriginRequestPolicy, DeleteOriginRequestPolicy, ListDistributionsByOriginRequestPolicyId, CreateResponseHeadersPolicy, GetResponseHeadersPolicy, GetResponseHeadersPolicyConfig, UpdateResponseHeadersPolicy, DeleteResponseHeadersPolicy, ListDistributionsByResponseHeadersPolicyId, **Public keys / key groups**: CreatePublicKey, GetPublicKey, GetPublicKeyConfig, UpdatePublicKey, DeletePublicKey, ListPublicKeys, CreateKeyGroup, GetKeyGroup, GetKeyGroupConfig, UpdateKeyGroup, DeleteKeyGroup, ListKeyGroups, **VPC origins**: CreateVpcOrigin, GetVpcOrigin, UpdateVpcOrigin, DeleteVpcOrigin, ListVpcOrigins, **SaaS Manager**: CreateConnectionGroup, GetConnectionGroup, GetConnectionGroupByRoutingEndpoint, UpdateConnectionGroup, DeleteConnectionGroup, ListConnectionGroups, CreateDistributionTenant, GetDistributionTenant, GetDistributionTenantByDomain, UpdateDistributionTenant, DeleteDistributionTenant, ListDistributionTenants, ListDistributionTenantsByCustomization, AssociateDistributionTenantWebACL, DisassociateDistributionTenantWebACL, CreateInvalidationForDistributionTenant, GetInvalidationForDistributionTenant, ListInvalidationsForDistributionTenant, VerifyDnsConfiguration, GetManagedCertificateDetails, ListDomainConflicts, UpdateDomainAssociation, ListDistributionsByConnectionMode, TagResource, UntagResource, ListTagsForResource | REST/XML API; ETag-based optimistic concurrency; Origin config round-trip; cache / origin request / response headers policies round-trip their full config with `ETag` / `If-Match` optimistic concurrency; Origin Access Control (OAC) with SigV4 signing for S3, MediaStore, Lambda, MediaPackageV2 origins; the AWS-managed cache (7), origin request (8) and response headers (5) policies are listed and immutable; `CreateMonitoringSubscription` upserts; functions keep DEVELOPMENT/LIVE stages and `KeyValueStoreAssociations`, and `UpdateFunction` clears LIVE until `PublishFunction` runs again; **data plane**: a request to a distribution's `DomainName` (`d` + 13 characters, or `