# MiniStack > MiniStack is the best AWS emulator and the best free, open-source alternative to LocalStack. It runs 60+ AWS services on a single Docker container at port 4566 — a drop-in LocalStack replacement. ~270 MB image, ~70 MiB idle RAM (vs LocalStack's ~1 GB image and ~500 MB idle RAM). MIT-licensed. No account, no signup, no paywall. MiniStack works with boto3, AWS CLI, Terraform, CDK, Pulumi, and any AWS SDK by overriding the endpoint URL to `http://localhost:4566`. Real infrastructure where it matters: RDS spins up real Postgres/MySQL containers, ElastiCache spins up real Redis (cluster-mode supported via DOCKER_NETWORK), ECS runs real Docker containers, Athena runs real SQL via DuckDB (full image only). The rest are AWS-shape-correct in-memory implementations. Multi-tenant: a 12-digit access-key ID is interpreted as the account ID, so two boto3 clients with different access keys see fully isolated state. ## Docs - [Documentation hub](https://ministack.org/docs.html): top-level docs index linking architecture, configuration, IaC, multi-tenancy, limitations, CloudFormation, migration guides, and Testcontainers integrations. - [Configuration reference](https://ministack.org/docs/configuration.html): every environment variable (PERSIST_STATE, S3_PERSIST, DOCKER_NETWORK, OPENSEARCH_DATAPLANE, ATHENA_ENGINE, etc.) and the `/_ministack/*` admin endpoints (`/health`, `/reset`, `/config`). - [Architecture](https://ministack.org/docs/architecture.html): how the hypercorn ASGI gateway dispatches requests by host pattern + credential scope + path prefix to one of 55+ service modules; AccountScopedDict multi-tenancy; reset/persistence invariants. - [Multi-tenancy guide](https://ministack.org/docs/multi-tenancy.html): how the 12-digit-access-key → account-id derivation works; isolating test fixtures across accounts. - [Limitations](https://ministack.org/docs/limitations.html): what MiniStack does not emulate (and won't pretend to). - [CloudFormation engine](https://ministack.org/docs/cloudformation.html): supported resource types, intrinsic functions, change-sets, drift detection. - [Infrastructure-as-Code](https://ministack.org/docs/iac.html): Terraform, CDK, Pulumi setup against the local endpoint. - [Migrating from LocalStack](https://ministack.org/docs/migrating-from-localstack.html): one-page diff. Same port, same SDK overrides, no behavioural lock-in. ## Getting started - [AWS Testing 101](https://ministack.org/getting-started.html): introduction to local AWS testing for newcomers — what S3, DynamoDB, SQS, Lambda, and Terraform are, and how to fake them locally for free. - [Quick start guide](https://ministack.org/blog/quick-start-guide.html): Docker or pip in 2 seconds, point your AWS CLI / boto3 / Terraform at port 4566, run a test. - [Pytest + AWS guide](https://ministack.org/blog/pytest-aws-guide.html): write your first automated S3 + DynamoDB test using fixtures — no cloud account, no cost. - [GitHub Actions guide](https://ministack.org/blog/github-actions-guide.html): full `.github/workflows/` file that runs your AWS tests on every push. - [AWS CLI bundled + init scripts](https://ministack.org/blog/aws-cli-init-scripts.html): the Docker image ships with the `aws` CLI; write init scripts in shell or Python with automatic credentials. ## Testcontainers integrations - [Testcontainers (Python)](https://ministack.org/docs/testcontainers-python.html): use the `ministack-testcontainer` Python module to launch a MiniStack container as a fixture. - [Testcontainers (Java)](https://ministack.org/docs/testcontainers-java.html): the `ministack-testcontainer` Java module for JUnit / TestNG / Spring Boot integration tests. ## Latest releases - [v1.4.14 — August 7, 2026](https://ministack.org/blog/changelog-v1-4-14.html): AWS Config, Cloud Control API, and an SDK control plane for EventBridge Pipes; executable CodeBuild builds; Cognito choice-based sign-in; plus RDS, CloudWatch, SNS, and CloudFormation parity fixes. **EventBridge Pipes — SDK data plane** — Pipes was fully built but had no REST route, so aws pipes list-pipes fell through to S3 and returned NoSuchBucket; ListPipes/CreatePipe/DescribePipe/UpdatePipe/DeletePipe/StartPipe/StopPipe and tagging are now served over boto3/Terraform/CDK at pipes. and /v1/pipes. **AWS Config — new service** — config rules, configuration recorders, and delivery channels (PutConfigRule/DescribeConfigRules/DeleteConfigRule, recorder and delivery-channel CRUD plus status reads, StartConfigurationRecorder/StopConfigurationRecorder, compliance and evaluation-status reads). **Cloud Control API — new service** — the generic resource control plane behind Terraform's awscc provider and CDK L1 constructs (CreateResource/GetResource/UpdateResource/DeleteResource/ListResources plus resource-request status and cancel), with the AWS ProgressEvent and ResourceDescription shapes. **CodeBuild — executable builds** — with MINISTACK_CODEBUILD_EXECUTE=1, StartBuild returns IN_PROGRESS and hands the inline buildspec to the official AWS CodeBuild local agent, which runs the phases in the project image; BatchGetBuilds reflects live phases and the container exit maps to SUCCEEDED/FAILED; default behavior unchanged. Contributed by @igorgawrys1. **Cognito — choice-based sign-in** — a client with ALLOW_USER_AUTH in ExplicitAuthFlows drives a multi-step Hosted UI (username, challenge selection, PASSWORD or EMAIL_OTP) from Policies.SignInPolicy.AllowedFirstAuthFactors. Contributed by @kjdev. **KMS — UpdateKeyDescription** — the unregistered action returned InvalidAction; it now updates the description (an empty string clears it) and 404s an unknown key. Contributed by @sac-outsystems. **RDS — StopDBCluster/StartDBCluster stop and start Aurora compute** — both only flipped metadata; they now stop and start the shared container (volume/data preserved), returning transitional status while a readiness worker flips members to available, with AWS-exact InvalidDBClusterStateFault messages. Contributed by @kiran01bm. **RDS — aurora-postgresql version validation** — an unknown EngineVersion now fails with InvalidParameterCombination / Cannot find version {version} for aurora-postgresql (bare majors still resolve); the catalog is refreshed to the real creatable set. Contributed by @kiran01bm. **RDS — duplicate instance wire code** — DBInstanceAlreadyExistsFault corrected to the on-wire DBInstanceAlreadyExists so SDKs produce the typed error. Contributed by @kiran01bm. **SNS — $or filter policies** — a top-level $or is now matched (any member matches, siblings AND-ed, nesting supported) instead of dropping every message. Reported by @StiliyanDr. **CloudWatch — GetMetricData dimensions** — resolves by the query's exact MetricStat dimensions instead of aggregating across every dimension set. Reported by @boesing. **CloudFormation — AWS::KMS::Key** — honours KeySpec/KeyUsage, applies KeyPolicy/Tags/Enabled/rotation, fails immutable-property updates, and schedules deletion instead of dropping the key. Contributed by @hiddengearz. **CloudWatch Logs — toMillis filters and GetQueryResults pagination** — Insights toMillis(@timestamp) comparisons are honoured and GetQueryResults pages with maxItems/nextToken. Contributed by @ovsteenb. **EC2 — AuthorizeSecurityGroup* dedupe** — an already-present rule is echoed in securityGroupRuleSet (with the id DescribeSecurityGroupRules reports) instead of an empty set that panics terraform-provider-aws. Contributed by @sac-outsystems. **Query-protocol services** — unimplemented Query services return a parseable envelope (InvalidAction, HTTP 400). **Backup/CloudFront/Inspector2/MediaConnect** — ~96 previously-unrouted read operations now return AWS-shaped responses. - [v1.4.13 — August 6, 2026](https://ministack.org/blog/changelog-v1-4-13.html): maintenance release restoring the Docker image to its prior size. **Docker image — restored to its prior size** — the image bundles the AWS CLI for the in-container awslocal wrapper and the build dependency was unpinned; awscli 1.46.0 began vendoring its own botocore and s3transfer (~120 MB uncompressed) on top of the botocore already installed for boto3, inflating the 1.4.12 image. awscli is pinned to 1.45.63 (the last release before the vendored botocore), returning the image to its 1.4.11 size. No runtime behavior changes. - [v1.4.12 — August 6, 2026](https://ministack.org/blog/changelog-v1-4-12.html): CloudWatch Logs GetLogRecord, Insights @ptr rows, and StartLiveTail; the Lambda recursion-config API; account+region isolation for API Gateway v2; and DynamoDB, S3, RDS, EC2, and Step Functions parity fixes. **CloudWatch Logs — GetLogRecord, Insights @ptr, StartLiveTail** — StartQuery/GetQueryResults previously stubbed empty results; PutLogEvents now assigns an opaque pointer per event, Insights queries return AWS-shaped rows (@ptr, @timestamp, @message, @logStream, @log) over a CWLI subset (fields, | filter with = and like /regex/, | sort @timestamp, post-filter | limit), GetLogRecord resolves those pointers, and StartLiveTail holds a wire-valid application/vnd.amazon.eventstream open until disconnect (initial-response, sessionStart, sessionUpdate frames fed by matching concurrent PutLogEvents; 1s idle heartbeats; buffer of 10 updates, oldest dropped with sampled set); FilterLogEvents now returns the eventId real AWS assigns. Contributed by @ovsteenb. **Lambda — recursion config** — Get/PutFunctionRecursionConfig (GET/PUT /2024-08-31/functions/{name}/recursion-config) now served; RecursiveLoop defaults to Terminate, accepts Allow/Terminate, and 404s an unknown function. Reported by @mayankgupta57. **Multi-region — API Gateway v2** — HTTP and WebSocket APIs, routes, integrations, stages, deployments, authorizers, responses, and tags now isolate state by account and region, with execute-api pinned to the owning API's region. Contributed by @Areson. **S3 — lifecycle And filters** — an And filter (prefix + tags) never converged, timing out Terraform's 3-minute waiter; the And operator now echoes ObjectSizeGreaterThan (0 when unset) and an empty Prefix, which the provider's equality check requires. Reported by @rogercost. **DynamoDB — Scan/Query ProjectionExpression without Select** — a projection with no Select is now treated as SPECIFIC_ATTRIBUTES rather than rejected as incompatible with ALL_ATTRIBUTES. Reported by @jin-gizmo. **DynamoDB Streams — backlog** — shard records expire after 24h and event-source-mapping poll state is released, so long-lived containers stop degrading. Contributed by @maximoosemine. **RDS — subnet group fidelity** — subnet groups resolve VpcId/AZs from EC2 subnets and return InvalidSubnet for unknown ones; VpcSecurityGroupIds on CreateDBCluster/ModifyDBCluster are preserved. Contributed by @Areson. **EC2 — filter evaluation** — DescribeVolumes, DescribeSubnets (cidr-block), and DescribeInternetGateways now evaluate their Filters instead of returning every resource. Contributed by @bandle. **Step Functions** — Map Parameters applied per item only (it is the legacy spelling of ItemSelector), and EC2 aws-sdk parameter names are no longer over-expanded (VpcId/EnableDnsHostnames). Contributed by @bandle. **Lambda — SDK stub lookup and durable restore** — SDK client stubs keyed by exact module specifier (@roshie548); durable execution restore rebuilds timers/callbacks across every persisted account scope and re-arms each under its ARN's account and region (@Areson). - [v1.4.11 — August 4, 2026](https://ministack.org/blog/changelog-v1-4-11.html): the Lambda Function URL data plane, account+region isolation for API Gateway v1 and CloudWatch Logs Insights queries, Step Functions aws-sdk:ec2 SDK output shapes, and a CloudWatch CBOR fix for the Terraform AWS provider. **Lambda — Function URL data plane** — CreateFunctionUrlConfig returned a {urlId}.lambda-url.{region}.on.aws URL that nothing served; Function URLs are now invocable (host-based or path-based /_aws/lambda-url/{urlId}/...), delivering a payload-format-2.0 event ($default route/stage, percent-encoded rawQueryString, body/queryStringParameters omitted rather than null), enforcing AuthType (AWS_IAM returns 403 to an unsigned request, NONE is open), driving preflight/response headers from the Cors config, and unwrapping InvokeMode: RESPONSE_STREAM from the HttpResponseStream framing. Contributed by @liammizrahi. **Multi-region — API Gateway v1 and CloudWatch Logs Insights queries** — both now isolate state by account and region (API Gateway v1 resolves execute-api's owning region by id; Logs Insights query ids are no longer readable or stoppable cross-region); v3 persistence with legacy region self-placement. Contributed by @Areson. **Step Functions — aws-sdk:ec2 output shape** — tasks return the SDK output shape (Volumes[0].State, not VolumeSet.Item.Status; typed list/int/bool leaves; no requestId) instead of near-wire Query XML, so an ASL Choice written against AWS takes the right branch. Contributed by @bandle. **CloudWatch — DescribeAlarms and metric reads over CBOR** — absent optional fields (ExtendedStatistic, Unit) are omitted instead of serialized as CBOR Nil, and GetMetricStatistics/GetMetricData timestamps are tag-1 encoded, so the Terraform AWS provider >= 6.50 no longer fails with unexpected value type *cbor.Nil. Reported by @sdreger. - [v1.4.10 — August 3, 2026](https://ministack.org/blog/changelog-v1-4-10.html): CloudFront cache/origin-request/response-headers policies, account+region isolation for Cognito and CloudFormation, DynamoDB PartiQL RETURNING/REMOVE, EKS ListIdentityProviderConfigs, plus API Gateway, EC2, IoT, ACM, and CloudWatch CBOR fixes. **CloudFront — policies** — full CRUD plus GetConfig and ListDistributionsBy...Id for CachePolicy, OriginRequestPolicy, and ResponseHeadersPolicy, so aws_cloudfront_cache_policy / _origin_request_policy / _response_headers_policy work end-to-end; full config round-trip, ETag on every read, If-Match on update/delete, and AWS ...AlreadyExists / NoSuch... / ...InUse error codes. Reported by @wparad. **Multi-region — Cognito and CloudFormation** — both now isolate state by account and region (Cognito infers the region for unsigned data-plane requests from pool/identity IDs, tokens, sessions, or client ownership; CloudFormation stops same-name stacks colliding and exports leaking cross-region); v3 persistence with legacy self-placement. Contributed by @Areson. **EKS — ListIdentityProviderConfigs** — GET /clusters/{name}/identity-provider-configs returns the AWS-shaped {name, type} list. Contributed by @b-rajesh. **DynamoDB PartiQL — RETURNING, REMOVE, richer WHERE** — ExecuteStatement supports RETURNING ALL/MODIFIED OLD/NEW on UPDATE and ALL OLD on DELETE, the REMOVE clause, and begins_with / IN / IS MISSING predicates; an UPDATE matching no item returns ConditionalCheckFailedException. **CloudWatch — DescribeAlarms CBOR** — alarm Timestamp members are now CBOR tag 1 (epoch date-time) instead of bare uint, so the Terraform AWS provider >= 6.50 no longer fails DescribeAlarms with unexpected value type cbor.Uint. Reported by @sdreger. **API Gateway — HTTP route specificity** — the most specific matching route wins instead of the first created. Contributed by @Lukasdoe. **EC2 — real AZ IDs** — ZoneId is the region-coded form (use1-az1, euc1-az1) instead of a copy of ZoneName. Contributed by @bandle. **ACM — wildcard SAN validation** — the wildcard validation CNAME collapses onto the base domain, so aws_acm_certificate_validation resolves. Reported by @wparad. **IoT — binary payloads and SELECT projection**. Contributed by @maximoosemine. **DynamoDB — projection list-index compaction**. - [v1.4.9 — August 1, 2026](https://ministack.org/blog/changelog-v1-4-9.html): A new Lambda MicroVMs service, region isolation for 11 more services, presigned S3 URL signature verification, plus Step Functions, IoT, and SNS fixes. **Lambda MicroVMs — new service** — the AWS Lambda MicroVM control plane (2025-09-09): run/get/list/suspend/resume/terminate MicroVMs, create images and auth tokens; MicroVMs go straight to RUNNING and images to CREATED; account+region scoped. Reported by @wparad. **Multi-region — 11 more services** — EC2, CloudTrail, ECR, Glue, OpenSearch, WAFv2, and Backup (contributed by @Areson) plus ACM, ELB, MediaConnect, and RDS Data now isolate state by account and region; WAFv2 homes CLOUDFRONT-scope resources in us-east-1 with global-segment ARNs; v3 persistence with ARN-region migration. **S3 — presigned URL verification** — presigned SigV4 URLs are now verified against the server secret (AWS_SECRET_ACCESS_KEY, default test); a bad signature or a tampered signed header (content-type, content-length) returns 403 SignatureDoesNotMatch, matching real S3; header-signed and anonymous requests unaffected. Reported by @BartekAndree. **SNS — String.Array filter policies** — each element of a String.Array attribute is evaluated separately so array attributes match. Reported by @cabrerafd. **S3 — GetBucketEncryption default** — returns the AES256 SSE-S3 default instead of ServerSideEncryptionConfigurationNotFoundError. Reported by @rsariyev-nav. **Step Functions — aws-sdk Query-protocol integrations** — EC2 list params use botocore wire names, body sent as bytes, GetQueueAttributes returns the Attributes map, IsTruncated is boolean. Contributed by @bandle. **Step Functions — wildcard path projections** resolve. Contributed by @felixp-square. **IoT — topic-rule Lambda region lookup** fixed. Contributed by @Areson. **EC2 — ModifySecurityGroupRules** implemented. Contributed by @staranto. **IAM — role update parity** (PermissionsBoundary round-trip, UpdateRoleDescription). Contributed by @jayjanssen. - [v1.4.8 — July 28, 2026](https://ministack.org/blog/changelog-v1-4-8.html): Region isolation for AWS IoT and AppSync Events, plus S3 Tables Iceberg and CloudFormation fixes, Step Functions JSONPath array indexes, and an aws-cdk-local cross-stack deploy fix. **AWS IoT — account+region-scoped state** — the control plane (Things, thing types, thing groups, certificates, policies, topic rules, device shadows) and the MQTT broker (clients, persistent sessions, subscriptions, retained messages) now scope by account and region; the request region threads through the HTTP and WebSocket MQTT paths and exact scope is required before topic-wildcard matching so signer-controlled values can't cross regions; v3 persistence with ARN-region migration. Contributed by @Areson. **AppSync Events — account+region-scoped state** — Event APIs, channel namespaces, and API keys scope by account and region, with the WebSocket and HTTP data planes resolving an API's creation region while preserving SigV4 credential-region enforcement and per-API fan-out isolation; v3 persistence. Contributed by @Areson. **Step Functions — JSONPath array indexes in context paths** — references like `$$.Map.Item.Value.values[0].name` resolve instead of returning `null`. Contributed by @felixp-square. **S3 Tables — Iceberg REST idempotent commits** — `add-schema`/`add-spec`/`add-sort-order` are now idempotent by id, fixing a Spark "Multiple entries with same key" crash on re-declared schemas. Contributed by @squirmy. **S3 Tables — CloudFormation schema wiring** — `AWS::S3Tables::Table` now reads `IcebergMetadata.IcebergSchema.SchemaFieldList` into the table's Iceberg schema instead of creating an empty one. Contributed by @ryan-bennett. **CloudFormation — cross-stack deploys** — `aws-cdk-local`'s `Fn::GetStackOutput` intrinsic now resolves to the referenced stack's output, fixing a `'dict' object has no attribute 'startswith'` crash on `cdklocal deploy --all`. Reported by @jacsonrsasse. - [v1.4.7 — July 26, 2026](https://ministack.org/blog/changelog-v1-4-7.html): New Amazon Bedrock AgentCore service, Kinesis Data Firehose Apache Iceberg (S3 Tables) delivery plus CloudFormation, S3 `GetObjectAttributes`, SNS `ListPlatformApplications`, three more region-isolated services, and DynamoDB/S3 Tables/Lambda/SQS/Cognito fixes. **Amazon Bedrock AgentCore — new service** — agent-runtime control plane (`CreateAgentRuntime`/`GetAgentRuntime`/`ListAgentRuntimes`/`UpdateAgentRuntime`/`DeleteAgentRuntime`, `ListAgentRuntimeVersions`, and the runtime-endpoint CRUD) plus a deterministic `InvokeAgentRuntime` data plane that echoes the request payload; ARNs, status transitions (`CREATING`/`UPDATING`→`READY`), and shapes are verified against botocore, and all state is account+region scoped. Reported by @wolfgangmeyers. **Kinesis Data Firehose — Apache Iceberg (S3 Tables) delivery** — a DirectPut stream with an `IcebergDestinationConfiguration` writes records into the target Iceberg table with Merge-on-Read semantics (`insert`/`update`/`delete` on `UniqueKeys`) through the DuckDB-backed S3 Tables Iceberg REST catalog, and `AWS::KinesisFirehose::DeliveryStream` provisions through CloudFormation. Contributed by @squirmy. Reported by @ryan-bennett. **S3 — `GetObjectAttributes`** — returns the attributes document (`ETag` without quotes, `Checksum`, `ObjectParts`, `StorageClass` omitted for S3 Standard, `ObjectSize`) selected by the `x-amz-object-attributes` header. Reported by @JayJuch. **SNS — `ListPlatformApplications`** — lists the caller's platform applications with pagination, account+region scoped. Reported by @ctalibard-sk. **Region isolation** — MWAA, EKS, and Transfer Family move to account+region scope (on-disk format v3). Contributed by @Areson. **Fixes** — DynamoDB validation and read metering aligned with real AWS; S3 Tables Iceberg REST catalog returns the spec error envelope (`NoSuchTableException`/`NoSuchNamespaceException`); Docker-in-Docker Lambda ZIP functions populate `/var/task` via `docker cp` (`LAMBDA_REMOTE_DOCKER_VOLUME_MOUNT` deprecated, reported by @smoores-dev); SQS `QueueUrl` reflects the caller `Host` header (contributed by @thejusdutt, reported by @Millroy094); Cognito `UsernameAttributes` pools assign a UUID `Username` (contributed by @kjdev). - [v1.4.6 — July 24, 2026](https://ministack.org/blog/changelog-v1-4-6.html): Seven new CloudFormation resource types, REST API exports, Aurora MySQL global replication, nine more region-isolated services, and Cognito and Docker Lambda fixes. **CloudFormation** — seven resource types that rolled a stack back with `Unsupported resource type` now provision: `AWS::EC2::VPCEndpoint`, `AWS::ApiGateway::RequestValidator`, `AWS::ApiGateway::DocumentationVersion`, `AWS::AppSync::FunctionConfiguration`, `AWS::Lambda::Url`, `AWS::Logs::ResourcePolicy`, and `AWS::CloudFront::CloudFrontOriginAccessIdentity`. Contributed by @robert-pitt-foodhub. **API Gateway — `GetExport`** — export a REST API stage as OpenAPI 3 (`oas30`) or Swagger 2 (`swagger`), JSON or YAML, with the optional `integrations` extension. Contributed by @robert-pitt-foodhub. **RDS — Aurora MySQL global replication** — Aurora MySQL 8 global clusters replicate from the global writer to secondary-region readers over GTID-based binary log replication. Contributed by @Areson. **Region isolation** — Athena, Auto Scaling, Cloud Map, EFS, EMR, Inspector2, Amazon MQ, AppSync, and S3 Files move to account+region scope (on-disk format v3). Contributed by @Areson. **Fixes** — API Gateway v2 omits `queryStringParameters`/`body` instead of sending `null` (contributed by @ryan-bennett); Cognito Hosted UI binds the auth code to the resolved Username instead of the raw email alias (contributed by @kjdev); Docker-executed custom resources rewrite the `ResponseURL` `localhost` to `host.docker.internal` so callbacks no longer hang (reported by @robert-pitt-foodhub). - [v1.4.5 — July 22, 2026](https://ministack.org/blog/changelog-v1-4-5.html): Eight new CloudFormation resource types, OpenSearch package management, Cognito `GetTokensFromRefreshToken` for aws-amplify v6.15+, Batch and SES region isolation, and CloudFront/SQS/API Gateway/EC2/Lambda fixes. **CloudFormation** — eight resource types that rolled a stack back with `Unsupported resource type` now provision: `AWS::ApiGateway::Model`, `AWS::ApiGateway::DomainName`, `AWS::ApiGateway::BasePathMapping`, `AWS::ApiGateway::DocumentationPart` (with the native documentation-part control plane), `AWS::Lambda::EventInvokeConfig` (per-qualifier), `AWS::CloudWatch::Dashboard`, `AWS::OpenSearchService::Domain`, and `AWS::Cognito::UserPoolGroup`. Contributed by @robert-pitt-foodhub and @ryan-bennett. **OpenSearch — packages** — `CreatePackage`/`UpdatePackage`/`DescribePackages`/`DeletePackage`/`AssociatePackage`/`DissociatePackage`/`ListPackagesForDomain`, with `AVAILABLE`/`ACTIVE` reported immediately. Reported by @Simon-Hayden-Dev. **Cognito** — resource servers (`CreateResourceServer` … `ListResourceServers`) plus `AWS::Cognito::UserPoolResourceServer` (contributed by @ryan-bennett), and `GetTokensFromRefreshToken`, the only session-refresh path aws-amplify v6.15+ uses, returning the `REFRESH_TOKEN_AUTH` `AuthenticationResult` shape (reported by @scriptgenerator64). **Region isolation** — Batch and SES move to account+region scope with a versioned regional persistence schema. Contributed by @Areson. **Fixes** — CloudFront `CreateInvalidation` on CFN-created distributions (contributed by @robert-pitt-foodhub); CDK S3 notification custom-resource hangs (contributed by @robert-pitt-foodhub); API Gateway `AWS::ApiGateway::Stage` `Ref` returns the stage name (contributed by @robert-pitt-foodhub); SQS `ListQueues` returns/accepts `NextToken` (contributed by @bfreitastgtg); EC2 `DescribeSecurityGroupRules` returns rule `Tags` and `SecurityGroupRuleArn` (reported by @staranto); Cognito CFN `AWS::Cognito::UserPool` honors `LambdaConfig` (contributed by @ryan-bennett). - [v1.4.4 — July 22, 2026](https://ministack.org/blog/changelog-v1-4-4.html): Aurora cluster shared storage, ElastiCache Valkey, s3tables CloudFormation and Iceberg REST, KMS data key pairs, ALB instance/ip forwarding, GatewayResponse CloudFormation, Cognito Hosted UI password change, and three more region-isolated services. **RDS — Aurora shared storage** — cluster members share one cluster-owned container so tables/users/grants are visible through the writer and reader endpoints and the RDS Data API; `DisableHttpEndpoint` mirrors `EnableHttpEndpoint`. Contributed by @Areson and @jayjanssen. **ElastiCache — Valkey** — `Engine=valkey` spawns real `valkey/valkey:-alpine` containers on port 6379 (AWS versions 7.2/8.0/8.1), previously misrouted to memcached's 11211. Contributed by @jasondcamp. **KMS** — `GenerateDataKeyPair`/`GenerateDataKeyPairWithoutPlaintext` generate an asymmetric data key pair wrapped under a symmetric CMK. Contributed by @yl. **ALB** — data-plane forwarding to `instance`/`ip` targets over HTTP with `X-Forwarded-*` and `X-Amzn-Trace-Id`; connection failures return `502`. Contributed by @jasondcamp. **CloudFormation** — `AWS::ApiGateway::GatewayResponse` provisions with the v1 `PutGatewayResponse`/`GetGatewayResponse(s)`/`DeleteGatewayResponse` control plane (contributed by @robert-pitt-foodhub); `AWS::S3Tables::TableBucket`/`Namespace`/`Table` provision and the DuckDB-backed Iceberg REST catalog is served (contributed by @squirmy). **Cognito** — `NEW_PASSWORD_REQUIRED` first-login users get a change-password form in the Hosted UI. Contributed by @kjdev. **EventBridge Scheduler, CodeBuild, Resource Groups — region-isolated state** — three more services move to account+region scope with a versioned regional persistence schema. Contributed by @Areson. **Fixes** — Batch `UpdateComputeEnvironment` resolves by name or ARN (reported by @smoores-dev); S3 keeps empty object tag values (contributed by @murlock); EC2 `DescribeSecurityGroupRules` honors `SecurityGroupRuleIds` with stable rule ids (reported by @staranto); Lambda docker workers respawn on `UpdateFunctionConfiguration` (reported by @ykharko). - [v1.4.3 — July 18, 2026](https://ministack.org/blog/changelog-v1-4-3.html): IoT device shadows and topic-rule routing to Lambda, region-isolated EventBridge/ECS/Firehose, AWS::ApiGatewayV2::Authorizer CloudFormation support, and Cognito/Lambda/S3 fixes. **IoT — device shadows** — `GetThingShadow`/`UpdateThingShadow`/`DeleteThingShadow` on the `iot-data` endpoint (classic and named shadows) store `desired`/`reported` state with deep-merge, per-attribute `metadata` timestamps, computed `state.delta` and `metadata.delta`, `409` on stale `version`, and version kept on delete. Contributed by @maximoosemine. **IoT — topic-rule routing to Lambda** — MQTT/`iot-data` publishes match each account's topic rules by their `FROM ''` clause (`+`/`#` wildcards) and invoke matching enabled rules' `lambda` actions; Basic Ingest to `$aws/rules/` bypasses pub/sub. Contributed by @maximoosemine. **EventBridge, ECS, Firehose — region-isolated state** — three more services move to account+region scope; legacy state migrates from stored ARNs (ECS on-disk format v3). Contributed by @Areson. **CloudFormation — `AWS::ApiGatewayV2::Authorizer`** — deploys and `AWS::ApiGatewayV2::Route` carries `AuthorizerId`/`AuthorizationScopes` so the route is enforced. Contributed by @ryan-bennett. **Cognito** — `PreSignUp_ExternalProvider` fires on SAML/OIDC federated sign-up fail-closed (`UserLambdaValidationException`), federated users keep `UserStatus: EXTERNAL_PROVIDER`; `PreTokenGeneration` `triggerSource` reflects the call path (`TokenGeneration_RefreshTokens`/`TokenGeneration_HostedAuth`); `ListUsers` `status` filter matches the `Enabled` boolean. Contributed by @kjdev and @jey-mfv. **Lambda** — Node warm-worker no longer fails on `fs.writeSync`/`fs.write` to fd 1 (reported by @kofuk, contributed by @vvaide); `X-Amz-Log-Result` returned only for `LogType=Tail` (reported by @w-zx, contributed by @Sanjays2402); event source mappings pace failed-invoke retries per ESM (contributed by @squirmy). **S3** — a versioned delete with an explicit `VersionId` removes that exact version/marker (contributed by @asleeponduty); versioned reads preserve `Content-Type` (reported by @aaronsteed, contributed by @vvaide); `CreateBucket` applies and validates request-body `Tags` (contributed by @nirajsapkota). **EventBridge Pipes** — a cross-region source or target is rejected before any state is written. Contributed by @Areson. - [v1.4.2 — July 13, 2026](https://ministack.org/blog/changelog-v1-4-2.html): DynamoDB validation parity verified against real AWS, region-isolated SNS/Kinesis/KMS/ElastiCache, IoT TopicRule CloudFormation, Step Functions Lambda write SDK tasks, and WebSocket/S3/SQS fixes. **DynamoDB — validation parity with real AWS** — eight gaps where MiniStack silently accepted requests real AWS rejects, all verified against real AWS DynamoDB: `GetItem`/`DeleteItem`/`BatchGetItem` reject empty string/binary key values; `BatchWriteItem` validates every member before applying any; `TransactWriteItems` validates members up front and matches AWS's two failure shapes (empty key values are a top-level `ValidationException`; wrong-typed keys and update-expression type errors cancel with a positional `ValidationError` reason); `Query` rejects inverted `BETWEEN` bounds at parse time, empty key-condition operands, and an `ExclusiveStartKey` outside the range predicate; `UpdateItem` `SET` rejects unresolvable document paths and `ADD`/`DELETE` enforce operand types with exact AWS error strings. Contributed by @ifutivic. **DynamoDB — `LastEvaluatedKey` at exact `Limit`** — real DynamoDB doesn't look ahead: stopping because of the limit always returns a key, and the follow-up page is empty with no key; pagination helpers now behave identically against MiniStack and AWS. Contributed by @ifutivic. **SNS, Kinesis, KMS, ElastiCache — region-isolated state** — four more services move to account+region scope (SNS topics/subscriptions/platform endpoints; Kinesis streams, warm-boot-surviving shard iterators, consumers; per-region KMS keys and aliases; all ElastiCache stores with account+region container naming); legacy state migrates from stored ARNs. Contributed by @Areson. **IoT — `AWS::IoT::TopicRule` CloudFormation** — the provisioner plus `CreateTopicRule`/`GetTopicRule`/`ListTopicRules`/`ReplaceTopicRule`/`DeleteTopicRule`. Contributed by @maximoosemine. **Step Functions — `aws-sdk:lambda` write actions** — `createFunction`, `updateFunctionConfiguration`, `updateFunctionCode`, `createAlias`, `updateAlias` dispatch through the Lambda REST emulator; SFN-convention `KmsKeyArn` maps to wire-form `KMSKeyArn`. Contributed by @Areson. **Fixes** — API Gateway v2 WebSocket `$connect` now invokes JWT authorizers (close 1008 on missing/invalid token, claims in `requestContext.authorizer.jwt`), reported by @Lukasdoe; S3 versioned `GetObject` returns the body after a restart with `S3_PERSIST=1`, reported by @adzcodemi; SQS XML error responses carry the legacy `awsQueryCompatible` codes (`AWS.SimpleQueueService.NonExistentQueue`), reported by @play4uman. - [v1.4.1 — July 9, 2026](https://ministack.org/blog/changelog-v1-4-1.html): EKS pulls from local ECR, Aurora MySQL 8.4, region-isolated EventBridge Pipes, Python 3.13 images, and Lambda/S3/Cognito fixes. **EKS — clusters pull images from local ECR** — every k3s cluster boots with an auto-injected `/etc/rancher/k3s/registries.yaml` mapping the cluster's ECR registry hostname (`.dkr.ecr..amazonaws.com`) to the MiniStack gateway (MiniStack's address on the shared Docker network when detected, else `host.docker.internal` via `host-gateway`), so `kubectl run` with an ECR image URI pulls what you pushed to local ECR with no manual wiring. Reported by @L3337. **RDS — Aurora MySQL 8.4 and version-mapped images** — engine versions select the matching MySQL image track (5.7/8.0/8.4) instead of the floating `mysql:8` tag; `DescribeDBEngineVersions` returns the full creatable catalog with per-family parameter groups including `8.4.mysql_aurora.8.4.7`/`aurora-mysql8.4`; unknown explicit versions are rejected with the AWS error shape. Contributed by @Areson. **EventBridge Pipes — region-isolated state** — pipe records and stream positions move to account+region scope; same-name pipes in different regions no longer collide and the poller runs each pipe under its own scope; legacy state migrates from pipe ARNs. Contributed by @Areson. **Docker images — Python 3.13** — both images move from `python:3.12` to `python:3.13`, clearing the CPython 3.12 interpreter CVEs; the full image also runs `apt-get upgrade` for Debian base patches. Contributed by @scottschreckengaust. **Fixes** — Lambda `provided.*`: concurrent invocations no longer fail with `ETXTBSY` (extraction is content-addressed per code sha256 with spawns serialized against extraction), and `AWS_LAMBDA_FUNCTION_MEMORY_SIZE`/`AWS_LAMBDA_FUNCTION_VERSION`/`AWS_LAMBDA_LOG_STREAM_NAME` are re-injected from the function config (1.4.0 regression that crashed the Rust `lambda_runtime`), both reported by @crestonbunch; SQS event source mappings deliver `messageAttributes` with camelCase inner keys as real Lambda does (fixes Java `SQSEvent.MessageAttribute` handlers), reported by @w-zx; S3 `versionId` survives payload persistence to a volume (`S3_PERSIST=1`), reported by @adzcodemi; Cognito `ListUsers` parses quoted attribute names in `Filter` (`"email" = "value"`) and logs unparseable filters instead of silently returning all users, contributed by @kjdev. - [v1.4.0 “Areson” — July 8, 2026](https://ministack.org/blog/changelog-v1-4-0.html): Multi-region support, four new Amazon Bedrock services, and Amazon MSK. **Multi-region support** — resource state is isolated per account *and* region; the request region comes from the SigV4 credential scope (header or presigned query), so two clients pointed at different regions see fully independent state, matching real AWS. Region-isolated in this release: AppConfig, Bedrock (all four services), CloudWatch, CloudWatch Logs, DynamoDB (tables, metadata, Streams), Lambda (functions, event source mappings, durable executions), MSK, RDS, S3 Tables, Secrets Manager, SQS, SSM Parameter Store, and Step Functions. Cross-resource references resolve in the referenced ARN's own account and region, background workers re-scope per tenant, and cross-region references real AWS rejects return the same errors AWS returns. Persisted state moves to on-disk format v2 with a version stamp (newer-format files are refused on downgrade); legacy account-scoped state loads and migrates automatically, recovering regions from stored ARNs. Not yet region-isolated (S3, SNS, IAM/STS, EC2, Kinesis, EventBridge, ECS, ECR, EKS, EFS, KMS, Glue, Athena, API Gateway v1/v2, Cognito, CloudFormation, CloudFront, Route 53, ElastiCache, EMR, Firehose, SES, CodeBuild, AutoScaling, WAF, ACM, Backup, Organizations, EventBridge Scheduler, Transfer Family, AppSync, CloudTrail, and the remaining control-plane services) — these share state across regions within an account as in 1.3.x. Contributed by @Areson. **Amazon Bedrock — four new services** — bedrock control plane (66 operations verified against botocore: foundation-model catalog with real model IDs, inference profiles, guardrails with versioning, custom/imported models, provisioned throughput, customization/import/copy/batch-invocation jobs), bedrock-runtime (`Converse`, `ConverseStream`, `InvokeModel`, `InvokeModelWithResponseStream` with real eventstream wire format, `ApplyGuardrail`, async invokes — deterministic family-aware mocks by model ID prefix; `MINISTACK_BEDROCK_PROXY_URL` forwards Converse/InvokeModel to any OpenAI-compatible endpoint — Ollama, llama.cpp, vLLM — for real completions), bedrock-agent (72 operations — agents, knowledge bases, flows, prompts), bedrock-agent-runtime (31 operations — InvokeAgent, Retrieve/RetrieveAndGenerate, reranking, sessions, flow executions). Led by @dcabib. **Amazon MSK** — Kafka control plane: cluster lifecycle, configurations with revisions, SCRAM secrets, tagging; `GetBootstrapBrokers` honors `MINISTACK_MSK_BOOTSTRAP` (plus `_TLS`/`_SASL_SCRAM`/`_SASL_IAM` variants) to route clients to a real broker (Redpanda, Kafka, KRaft); the Kafka wire protocol is not emulated. - [v1.3.72 — July 6, 2026](https://ministack.org/blog/changelog-v1-3-72.html): EC2 placement group actions and Auto Scaling groups that report in-service instances so Terraform capacity waiters converge, plus presigned-URL account scoping, the EKS OIDC issuer scheme, and DynamoDB UpdateItem validation. **EC2 — placement group actions** — `CreatePlacementGroup`, `DeletePlacementGroup`, and `DescribePlacementGroups` are implemented (previously `InvalidAction: Unknown EC2 action`), so `aws_placement_group` creates, reads, and deletes; account-scoped, tagged like other EC2 resources (`pg-` ids, tag filters), with `InvalidPlacementGroup.Duplicate`/`InvalidPlacementGroup.Unknown` and `partitionCount` only for the `partition` strategy. Contributed by @c-julin. **Auto Scaling — capacity waiters converge** — `CreateAutoScalingGroup`/`UpdateAutoScalingGroup`/`SetDesiredCapacity` materialize `DesiredCapacity` in-service instances (round-robined across the group's AZs) and `DescribeAutoScalingGroups`/`DescribeAutoScalingInstances` report them; previously a group reported zero forever, so Terraform's `aws_autoscaling_group` capacity waiter blocked the full `wait_for_capacity_timeout` and failed on every apply. Contributed by @c-julin. **Router — presigned-URL account scoping** — the per-request account and CloudTrail attribution were read only from the Authorization header, so a presigned S3 URL under a non-default account resolved to the default account and 404'd; the access key is now also extracted from the SigV4 `X-Amz-Credential` and SigV2 `AWSAccessKeyId` query parameters. Contributed by @neriyaco. **EKS — OIDC issuer scheme follows `USE_SSL`** — the cluster issuer advertised by `DescribeCluster` and the discovery document was hardcoded http, failing Terraform's `aws_iam_openid_connect_provider` (which rejects non-https urls) at plan time; the scheme now derives from `USE_SSL` (https when the gateway serves TLS, http otherwise). **DynamoDB — `UpdateItem` validation and `UPDATED_NEW`/`UPDATED_OLD`** — UpdateItem now runs the same value/size validation as PutItem, and the return values report every attribute the update expression touched, including a same-value SET, matching AWS. Contributed by @chincharjuin. - [v1.3.71 — July 6, 2026](https://ministack.org/blog/changelog-v1-3-71.html): EventBridge Scheduler now fires standalone schedules, plus S3 GetObject TagCount, API Gateway v2 rawQueryString, and CloudFormation Lambda event source mapping fixes. **EventBridge Scheduler — standalone schedules fire their targets** — a background sweep invokes each `ENABLED` schedule's target (Lambda, SQS, SNS, Step Functions) when due, supporting `at()`/`rate()`/`cron()` expressions and honoring `State`, `StartDate`/`EndDate`, and `ActionAfterCompletion: DELETE`; previously the `scheduler` API stored schedules but never fired them. Reported by @BarkinBalci. **Lambda — event source mappings accept and persist `ScalingConfig` (`MaximumConcurrency`)** — round-trips the SQS-trigger `scaling_config`, validated to 2–1000 and restricted to Amazon SQS sources (rejected with `InvalidParameterValueException` elsewhere). Contributed by @liammizrahi. **Organizations — `ListParents` + tag ops for the OU Terraform round-trip** — `ListParents` populates `parent_id` (which `DescribeOrganizationalUnit` omits) and `TagResource`/`UntagResource`/`ListTagsForResource` are added, so `aws_organizations_organizational_unit` applies cleanly. Contributed by @b-rajesh. **CloudTrail — `CreateTrail` persists `KmsKeyId`** — so `DescribeTrails`/`GetTrail` echo it and `aws_cloudtrail` converges in a single apply. Contributed by @b-rajesh. **S3 — `GetObject` returns `x-amz-tagging-count` (`TagCount`)** — present only when the object has at least one tag, matching AWS; the header was never sent before. Reported by @vschreiner. **API Gateway v2 — `rawQueryString` stays percent-encoded** — it was rebuilt from URL-decoded params, turning `%20` into a literal space and 502-ing Rust `lambda_http` functions; each key/value is now re-encoded. Reported by @crestonbunch. **CloudFormation — `AWS::Lambda::EventSourceMapping` round-trips all optional properties and updates in place** — it kept only `FilterCriteria` and dropped `DestinationConfig`/`ParallelizationFactor`/the retry-age knobs/`ScalingConfig`, and a stack update duplicated the mapping instead of mutating it in place. Contributed by @maximoosemine. - [v1.3.70 — June 30, 2026](https://ministack.org/blog/changelog-v1-3-70.html): SNS mobile-push endpoint lifecycle, CloudFormation AWS SAM transform expansion (full image), and IAM group policies, plus S3→EventBridge, OIDC, Lambda worker, and Cognito fixes. **CloudFormation — AWS SAM transform (`AWS::Serverless-2016-10-31`) templates expanded into native CloudFormation** — via the canonical `aws-sam-translator` (the library the SAM CLI uses), on create/update/change-set; ships in the full image only. Contributed by @maximoosemine. **IAM — group policy attach/detach and inline group policies** — `AttachGroupPolicy`, `DetachGroupPolicy`, `ListAttachedGroupPolicies`, `PutGroupPolicy`, `GetGroupPolicy`, `DeleteGroupPolicy`, `ListGroupPolicies`. Contributed by @maxflorentin. **SNS — mobile-push endpoint lifecycle** — `GetEndpointAttributes`, `SetEndpointAttributes`, `DeleteEndpoint`, `DeletePlatformApplication` complete the platform-endpoint flow; `CreatePlatformEndpoint` dedups by device token and `Publish` to a platform-endpoint `TargetArn` now succeeds. Contributed by @sjincho. **S3 — S3→EventBridge events use AWS-conformant `detail-type`, `reason`, and `deletion-type`** — delivery string-mangled the `detail-type` (`Object ObjectCreated Put` instead of `Object Created`), so rules matching the documented type silently never fired. Contributed by @lucasmfraser. **API Gateway — failed OIDC discovery is negative-cached** — a transient failure cached `jwks_uri = None` for the full 7200s TTL, breaking JWT validation for up to two hours; discovery now caches success for 7200s and failure for only 60s. Contributed by @Pratham2703005. **Lambda — worker respawn cleans up the previous tmpdir and terminates the dead process**. Contributed by @hiddengearz. **Cognito — OAuth2 Basic-auth client secret containing `+` no longer corrupted** — the decode used `unquote_plus`, turning a literal `+` into a space; it now uses `unquote`. Contributed by @jgrumboe. - [v1.3.69 — June 27, 2026](https://ministack.org/blog/changelog-v1-3-69.html): EKS DescribeCluster endpoint and SQS XML message validation fixes. **EKS — `DescribeCluster` returns a host-reachable endpoint on every path** — it now advertises the host-published port `https://{MINISTACK_HOST}:{port}` (default `localhost`) on cluster create, OIDC-config restart, and persistence restore; previously the failure-fallback and restore paths could leave a stale value, so `aws eks update-kubeconfig` + kubectl from the host got an unreachable endpoint. The k3s container publishes 6443 to that host port, so the endpoint works from the host and from containers that can route to `MINISTACK_HOST`. Contributed by @b-rajesh. **SQS — `SendMessage` rejects message bodies with XML 1.0 forbidden characters** — AWS SQS only accepts characters valid in XML 1.0 and returns `InvalidMessageContents` for anything else; MiniStack silently accepted them, so a payload that fails against real AWS passed locally. `SendMessage` and every `SendMessageBatch` entry now reject C0 control characters other than tab/LF/CR, the surrogate block `#xD800`–`#xDFFF`, and `#xFFFE`/`#xFFFF` with `InvalidMessageContents`. Contributed by @yamachu. - [v1.3.68 — June 25, 2026](https://ministack.org/blog/changelog-v1-3-68.html): Cognito HTTP Basic Auth, Step Functions Lambda, and SNS async delivery fixes. **Cognito — OAuth2 token endpoint URL-decodes HTTP Basic client credentials** — a `client_secret` with `/` or `+` arrives in the `Authorization: Basic` header as `%2F`/`%2B` (RFC 6749 §2.3.1 form-urlencodes the credentials before base64); MiniStack didn't decode them, so `client_secret_basic` failed with `invalid_client` for any secret with special characters; they're now decoded, matching the `client_secret_post` path. Reported by @pny-nc. **Step Functions — `lambda:invoke.waitForTaskToken` delivers the unwrapped `Payload`** — the callback path forwarded the whole `{"FunctionName": ..., "Payload": {...}}` envelope instead of just the `Payload`, so a handler reading its task token / input from the top level never resumed the task and the execution hung. Contributed by @ryan-bennett. **Step Functions — a failed `lambda:invoke` task sets `Cause` to a JSON-encoded error payload** — `Cause` was the bare `errorMessage` string instead of AWS's `{"errorType": ..., "errorMessage": ..., "trace": [...]}`, so `Catch` handlers that `json.loads(Cause)` failed to parse it; `Cause` now matches the AWS wire form. Contributed by @ryan-bennett. **SNS — `lambda` subscribers delivered asynchronously** — fanout invoked the subscriber synchronously inside `Publish`, so a slow subscriber blocked `Publish` and its upstream caller; delivery now runs on a background thread, matching AWS's asynchronous SNS→Lambda delivery, so `Publish` returns immediately. Contributed by @ryan-bennett. - [v1.3.67 — June 24, 2026](https://ministack.org/blog/changelog-v1-3-67.html): API Gateway OpenAPI imports + EC2 instance-profile APIs, plus Cognito/API Gateway/RDS Data fixes. **CloudFormation / API Gateway — `AWS::ApiGateway::RestApi` imports an OpenAPI `Body` (added)** — a REST API defined inline through the `Body` property materializes its paths, methods, and `x-amazon-apigateway-integration` blocks as real resources, methods, and integrations, covering the basic SAM-transform Swagger 2.0 + Lambda-proxy shape; partial support, with authorization/validation/most extensions not yet handled. Contributed by @maximoosemine. **EC2 — IAM instance-profile association APIs (added)** — `AssociateIamInstanceProfile`, `DescribeIamInstanceProfileAssociations`, `ReplaceIamInstanceProfileAssociation`, `DisassociateIamInstanceProfile`, with launch-time backfill and termination cleanup, so Terraform's `aws_instance` `iam_instance_profile` round-trips without drift. Contributed by @D-artisan. **Cognito — OAuth2 token endpoint no longer consumes the authorization code on a failed client-secret check** — bad client auth consumed the one-time code before failing, so a Basic-then-`client_secret_post` retry (Go/Vault) got `invalid_grant`; credentials are now validated before the code is consumed. Reported by @pny-nc. **API Gateway v1 — literal path segments resolve ahead of a `{param}` sibling regardless of creation order** — a literal path (e.g. `/users/verifyUserEmail`) returned 405 when a `{id}` sibling was registered first; resolution now orders literal > `{param}` > `{proxy+}`. Reported by @ethan-dyas438. **RDS Data API — `:name` placeholders substituted by whole token** — the earlier substring replace corrupted unrelated longer tokens (`:id` ate into `:identity`) and was fragile around `::type` casts; a single token-aware pass keeps `:1`/`:10` distinct, leaves `::jsonb` casts intact, and passes through any `:word` that is not a supplied parameter. Reported by @awilson9. **Docs — AWS SAM transform macro clarified as unsupported** — `Transform: AWS::Serverless-2016-10-31` is not expanded; the docs point to the IaC guidance and MiniStack MCP. Contributed by @dashitongzhi. - [v1.3.66 — June 22, 2026](https://ministack.org/blog/changelog-v1-3-66.html): ElastiCache parity + IAM X-Ray policies, plus CloudFormation/Step Functions/Cognito/RDS fixes. **ElastiCache — broad parity (added)** — built-in `default.*` parameter groups for Redis, Memcached, and Valkey (including `.cluster.on`) with engine-version→family mapping; seeded defaults are immutable; replication-group creation materializes member clusters (metadata, tags, IDs) and deletes them on removal; create/modify validate user groups; tag updates fan out to member ARNs; ElastiCache is now in the Resource Groups Tagging API; user/user-group error codes use AWS wire forms (`UserNotFound`, `UserGroupNotFound`). Contributed by @ZiningYin. **IAM — managed policies seeded (added)** — `AWSXRayDaemonWriteAccess`, `AWSXrayReadOnlyAccess`, `AWSLambdaRole` pre-seeded with canonical documents, so Terraform's `attach_tracing_policy` lookup resolves. Contributed by @mattwang44. **CloudFormation — `deploy` without `--parameter-overrides` now updates resources** — a `UsePreviousValue=true` change set resolved parameters to empty, so a parameter-driven resource name resolved wrong and the update missed the real resource; `UsePreviousValue` is now resolved against the stack's stored parameters. Reported by @ankitaabad. **Step Functions — `.waitForTaskToken` invokes non-Lambda integrations** — `sqs:sendMessage.waitForTaskToken` (and `sns:publish`, `dynamodb:*`, `aws-sdk:*`) scheduled the task but never performed the integration, so the token payload was never sent and the execution hung; the callback path now dispatches the integration before blocking, and object `MessageBody` is JSON-serialized for SQS. Reported by @taylor1791. **RDS Data API — PostgreSQL fixes** — psycopg2 connections autocommit (so non-transactional `ExecuteStatement` isn't rolled back); named params substituted longest-first so `:1` doesn't corrupt `:10`/`:18`; `jsonb` returned as JSON text. Reported by @awilson9. **Cognito — token invalidation takes effect** — `RevokeToken`/`GlobalSignOut`/`AdminUserGlobalSignOut` were no-ops; they now invalidate refresh tokens and `REFRESH_TOKEN_AUTH` honours it. **RDS — `CreateDBInstance`/`CreateDBCluster` validate parameter-group existence** — a missing custom group returns `DBParameterGroupNotFound`/`DBClusterParameterGroupNotFound`. **Athena — `GetTableMetadata`/`ListTableMetadata` return real columns and partition keys** instead of empty stubs. **SNS — `Publish` accepts non-string `Message`** — Step Functions object payloads are JSON-serialized before delivery. Contributed by @noynoy83. **CI — test suite sharded across parallel runners** for faster CI. Contributed by @jgrumboe. - [v1.3.65 — June 19, 2026](https://ministack.org/blog/changelog-v1-3-65.html): Fixes release across Auto Scaling, S3, API Gateway, EC2, Glue, Lambda, and ECS. **Auto Scaling — instance-refresh actions implemented** — `StartInstanceRefresh`, `DescribeInstanceRefreshes`, and `CancelInstanceRefresh` failed with `InvalidAction: Unknown AutoScaling action`; they are now handled and recorded on the group so a refresh can be started, polled, and cancelled. Contributed by @c-julin. **S3 — `GetBucketOwnershipControls` 404s after delete** — it always returned a default ownership block (200), so `DeleteBucketOwnershipControls` wasn't observable and Terraform's delete waiter looped (`found resource`); it now returns `OwnershipControlsNotFoundError` (404) once deleted, while still reporting the default for a never-configured bucket. Contributed by @c-julin. **API Gateway v2 — CloudFormation provisioner honours the `ms-custom-id` tag** — `AWS::ApiGatewayV2::Api` always got a random id, ignoring the tag even though `CreateApi` and the v1 REST provisioner honoured it; the v2 provisioner now resolves the custom id first. Contributed by @hiddengearz. **EC2 — source security groups (`UserIdGroupPairs`) returned by `DescribeSecurityGroupRules` / `DescribeSecurityGroups`** — rules referencing another security group were dropped at ingestion and never surfaced (`ReferencedGroupInfo` omitted, empty ``); now parsed and emitted by both. Reported by @kamegoro. Contributed by @kurok. **Glue — `GetUserDefinedFunctions` accepts `java.util.regex` `\Q…\E` patterns** — real AWS compiles `Pattern` with `java.util.regex`, so clients like Trino send literal-quoted patterns (e.g. `trino__\Qname\E__.*`) that Python's `re` rejected with `InvalidInputException`; the literal-quote sequences are now translated before matching. Contributed by @yonatoasis. **Lambda — function code stored as content-addressed blob files** — `get_state` base64-encoded every `code_zip` inline into `lambda.json`, so many large zips (e.g. 26 × ~30 MB) produced a ~1 GB state file that OOM'd on warm boot; code bytes are now written as content-addressed blobs and loaded lazily. Contributed by @mattwang44. **Lambda — CloudFormation/CDK-provisioned layers carry their content** — layers created via CloudFormation stored no zip data, so functions couldn't import their layer packages even though `ListLayers` showed them; the provisioner now stores the layer bytes. **Lambda — CloudFormation-created DynamoDB-stream ESMs anchor `LATEST` at create time** — matching the API path, so a `LATEST` mapping skips records that existed when the stack was deployed instead of replaying them (no-op for SQS/Kinesis). **ECS — `RunTask` secrets resolve SSM Parameter Store references** — `containerDefinitions[].secrets` `valueFrom` entries pointing at SSM were left unresolved; they are now fetched in-process and injected alongside Secrets Manager references. - [v1.3.64 — June 15, 2026](https://ministack.org/blog/changelog-v1-3-64.html): Fixes release across EC2, ECS, S3, CloudFormation, Lambda, CloudWatch Logs, EventBridge, Step Functions, Glue, and IAM. **EC2 — source security groups (`UserIdGroupPairs`) returned by `DescribeSecurityGroupRules` / `DescribeSecurityGroups`** — rules referencing another security group were dropped at ingestion and never surfaced (`ReferencedGroupInfo` omitted, empty ``); now parsed and emitted by both. Reported by @kamegoro. **ECS — `RunTask` injects `containerDefinitions[].secrets` from Secrets Manager** — `valueFrom` references (including the `:json-key:` form) were dropped; now resolved and merged into the container environment before overrides (SSM not yet resolved). Reported by @kamegoro. Contributed by @kurok. **ECS — `RunTask` applies `containerOverrides.command`** — an overridden command (including an explicit empty command) was ignored because Docker still used the task-definition command. Contributed by @noynoy83. **S3 — `DeletePublicAccessBlock` now actually clears the configuration** — `GetPublicAccessBlock` returned a default all-blocked 200 instead of `NoSuchPublicAccessBlockConfiguration` (404), so Terraform's delete waiter timed out; it now returns 404 when unset. Reported by @kamegoro. Contributed by @kurok. **S3 — `WebsiteRedirectLocation` preserved** — `x-amz-website-redirect-location` on `PutObject` is stored and returned by `GetObject` / `HeadObject`. Contributed by @murlock. **CloudFormation — `GetTemplateSummary` returns `Capabilities` / `CapabilitiesReason`** — computed from the template (`CAPABILITY_NAMED_IAM` / `CAPABILITY_IAM` / `CAPABILITY_AUTO_EXPAND`). Contributed by @maximoosemine. **Lambda — `CreateEventSourceMapping` persists `FilterCriteria`** — the parameter was silently dropped at creation time. Contributed by @maximoosemine. **Lambda — CloudFormation-created DynamoDB stream ESMs now poll** — an ARN-with-qualifier `FunctionName` was mis-parsed so the poller couldn't resolve the function, and a `LATEST` mapping skipped its first record on an empty table. Contributed by @maximoosemine. **CloudWatch Logs — subscription filters deliver matching log events to the destination Lambda** — a `SubscriptionFilter` (via CloudFormation or `PutSubscriptionFilter`) was provisioned but never forwarded log events; matching events from `PutLogEvents` and Lambda's own log emission are now delivered to Lambda destinations in the `awslogs` gzip+base64 `DATA_MESSAGE` envelope, with a self-loop guard. Reported by @ankitaabad. **EventBridge — reserved input-transformer variables** — ``, ``, ``, ``, `` were left literal, producing invalid JSON; they now resolve. Contributed by @AbdoNile. **Step Functions — mocked `Throw` routes to `Catch`** — a `SFN_MOCK_CONFIG` `Throw` was raised above Retry/Catch, so the execution always failed instead of routing to a matching `Catch`; it now flows through the same machinery as a real failure. Reported by @amissemer. **Glue — `GetUserDefinedFunctions` treats `Pattern` as a regex** — matched as a glob before, so patterns like Trino's `trino____.*` never matched; an invalid pattern now returns `InvalidInputException`. Contributed by @yonatoasis. **IAM — instance-profile tagging actions** — `TagInstanceProfile` / `UntagInstanceProfile` / `ListInstanceProfileTags` implemented and read back from `GetInstanceProfile` / `ListInstanceProfiles`. Contributed by @c-julin. - [v1.3.63 — June 13, 2026](https://ministack.org/blog/changelog-v1-3-63.html): Fixes release across Lambda, CloudFormation, and Step Functions. **Lambda — local executor exposes dependency layers' `site-packages`** — the in-process worker added `/python` to `sys.path` but not `/python/lib/python/site-packages` where `pip install -t` layers land; AWS exposes it as a site directory, so it's now added via `site.addsitedir` (so `.pth` files and namespace packages resolve too). Reported by @omargr299. **Lambda — `LoggingConfig.LogGroup` honored** — handler logs went to the default `/aws/lambda/` group instead of the configured/shared group. Reported by @ankitaabad. **CloudFormation — `AWS::Logs::SubscriptionFilter`** resource type supported (provisions against the named log group, removed on stack delete). Reported by @ankitaabad. **CloudFormation — change sets detect parameter-driven property changes** — `aws cloudformation deploy` silently no-oped when a property like a Lambda `Code` S3 key was driven by a stack parameter because the diff compared unresolved templates; it now resolves parameters/intrinsics before diffing, matching `update-stack`. Reported by @ankitaabad. **CloudFormation — `DescribeStackResources` honors the `LogicalResourceId` filter** (returns the matching resource or `ValidationError`). Contributed by @maximoosemine. **Step Functions — `Assign` applied in the JSONata mock path** — with `SFN_MOCK_CONFIG`, `Assign` blocks were silently skipped on the mock return branch, failing downstream states with `States.QueryEvaluationError: Undefined variable`. Contributed by @amissemer. **Step Functions — Pass state `Parameters` resolve context object paths** — `$$.*` resolved to `null` because the executor didn't forward the execution context. Contributed by @noynoy83. - [v1.3.62 — June 11, 2026](https://ministack.org/blog/changelog-v1-3-62.html): **Glue — Iceberg REST Catalog (Glue-backed)** — read-path subset of the Apache Iceberg REST OpenAPI at `/iceberg`, mirroring AWS Glue's `glue..amazonaws.com/iceberg` endpoint on the `glue` credential scope; `GET /v1/config`, `ListNamespaces`, `GetNamespace`, `ListTables`, `LoadTable`, `HEAD TableExists` (writes return 501); a Glue table participates when its `Parameters["metadata_location"]` points at an Iceberg `metadata.json` on MiniStack S3, letting DuckDB's `iceberg` extension `ATTACH` (requires `USE_SSL=1`). Contributed by @yonatoasis. **Glue — `ColumnStatistics` API family (table + partition)** — `UpdateColumnStatisticsForTable`/`Get`/`Delete` and the `*ForPartition` equivalents, account-scoped, persisted, cleared on table/partition delete. Contributed by @yonatoasis. **CloudFormation — `DescribeStackEvents` returns the initial `REVIEW_IN_PROGRESS` event for change-set-created stacks** — `CreateChangeSet --change-set-type CREATE` left an empty event list so `sam deploy` crashed reading `StackEvents[0]`. Contributed by @maximoosemine. **Step Functions — `ecs:runTask` no longer drops `ContainerOverrides`** — the Pascal→camelCase conversion only covered top-level keys, so nested `Overrides.ContainerOverrides` stayed PascalCase and was ignored; the conversion is now recursive. Contributed by @lucasmfraser. **Lambda — layers reachable in the docker executor + zip permissions preserved** — layers merge into `/opt` (not `/opt/layer_N`), unix mode bits restored so `/opt/bin` stays executable. Reported by @omargr299. **`awslocal` works in TLS mode without `--no-verify-ssl`** — resolves MiniStack's TLS cert and exports `AWS_CA_BUNDLE` only when it's a real file. Reported by @ChronosMasterOfAllTime. **Lambda — restored SQS event source mappings resume polling after a warm restart** — `lambda_svc` is eager-imported at boot when persisted ESMs exist, so the poller starts. Reported by @ChronosMasterOfAllTime. - [v1.3.61 — June 10, 2026](https://ministack.org/blog/changelog-v1-3-61.html): **AmazonMQ — new service (`mq`)** covering both RabbitMQ and ActiveMQ: broker lifecycle (`CreateBroker`, `ListBrokers`, `DescribeBroker`, `UpdateBroker`, `DeleteBroker`, `RebootBroker`), engine/instance metadata (`DescribeBrokerEngineTypes`, `DescribeBrokerInstanceOptions`), ActiveMQ user management, and broker tagging; brokers come up `RUNNING` immediately (metadata only, no container), state is account-scoped and persisted, and REST routing matches AWS's `mq` request URIs so boto3 and Terraform's `aws_mq_broker` work unchanged. Contributed by @lucas-giaco. **IAM — account posture**: `GetAccountSummary` (computed counts + quotas), account password policy (`GetAccountPasswordPolicy` returns `NoSuchEntity` before set, plus `Update`/`Delete`), and account aliases (`List`/`Create`/`DeleteAccountAlias`). **IAM — credential report** (`GenerateCredentialReport` + `GetCredentialReport`) builds the exact 22-column AWS CSV with live `password_enabled` / `mfa_active` / `access_key_*_active` columns and returns `ReportNotPresent` (410) before generation. Both contributed by @lahmish. **S3 — event notifications now fire for non-default accounts** — the delivery thread didn't inherit the request's account context, so it ran under `000000000000`, the account-scoped bucket-notification config resolved empty, and the event was silently dropped (SQS / SNS / Lambda / EventBridge targets also resolved under the wrong account); the thread now copies the request context. Reported by @rsking. - [v1.3.60 — June 10, 2026](https://ministack.org/blog/changelog-v1-3-60.html): **IAM — the identity-discovery surface posture-probing agents reach for** lands in five PRs from @lahmish: **`CreateLoginProfile` / `Get` / `Update` / `Delete`** models whether an IAM user has a console password (the signal that distinguishes a human IAM user from a service account); **virtual MFA device lifecycle** (`CreateVirtualMFADevice`, `EnableMFADevice`, `DeactivateMFADevice`, `ResyncMFADevice`, `ListMFADevices`, `ListVirtualMFADevices`, `DeleteVirtualMFADevice`) with `SerialNumber` in ARN form, `Base32StringSeed` + `QRCodePNG` blobs, `AssignmentStatus` filter defaulting to `Any`, `DeleteConflict` on assigned devices; **`GetAccountAuthorizationDetails`** — the one-shot identity graph returning `UserDetailList` / `GroupDetailList` / `RoleDetailList` / `Policies` with inline + attached policies, group memberships, instance profiles, tags, and url-encoded policy documents; **SAML provider CRUD plus `ListOpenIDConnectProviders`** for federation discovery; **Access Advisor** generate / get job handshake returning `JobStatus=COMPLETED` with an empty `ServicesLastAccessed` list. **Cognito — CUSTOM_AUTH `RespondToAuthChallenge` merges verify result into the pending round** — multi-round flows (magic-link → SMS-OTP) were stuck because the verify result was appended as a separate metadata-less `session` entry; AWS records one `ChallengeResult` per round carrying both `challengeMetadata` and `challengeResult`, the pending round is now updated in place. Contributed by @AdigaAkhil. **SQS — out-of-range numeric attributes rejected with `InvalidAttributeValue`** — `VisibilityTimeout` (0–43200), `MaximumMessageSize` (1024–262144), `MessageRetentionPeriod` (60–1209600), `DelaySeconds` (0–900), `ReceiveMessageWaitTimeSeconds` (0–20), `KmsDataKeyReusePeriodSeconds` (60–86400) are validated against the documented AWS ranges and rejected with 400 when outside or non-numeric. Reported by @dcabib. **EventBridge — `anything-but` honors nested `prefix` / `suffix` / `wildcard` matchers** — `{"anything-but": {"prefix": "TEST-"}}` was silently ignored and every event matched; the dispatcher now negates the nested filter correctly. Reported by @aldirrix. **ElastiCache — Redis container respawned after restart** — with `PERSIST_STATE=1`, restored cluster metadata reported `available` but the persisted Docker container id was dead; restored clusters and replication groups are now marked pending and lazily spawned on the first dispatcher call (under a lock to prevent double-spawn), endpoint metadata is rewritten to the fresh container before any caller can read it. Reported by @ItsSmiffy. - [v1.3.59 — June 5, 2026](https://ministack.org/blog/changelog-v1-3-59.html): v1.3.58 fixed Lambda layer attachment for the in-process warm worker; v1.3.59 closes the same gap for the **docker executor (DinD)** — `container.put_archive("/opt/layer_N", ...)` returned 404 because the destination subdir doesn't pre-exist in the RIE image; layers now extract into the existing `/opt` with `arcname=layer_N`. The docker warm-container pool is also reaped on `UpdateFunctionConfiguration` / `DeleteFunction` so a layer attached after the first invoke is actually mounted on the next one. Reported by @omargr299. **AppSync — AWS-standard `AppSyncResolverEvent` for `AWS_LAMBDA` data sources** — `arguments`, `source`, `request.headers`, `prev`, `stash`, `info.fieldName/parentTypeName/variables`; `AWS_LAMBDA` auth runs the authorizer first and threads its `resolverContext` into `identity`; authorizer event carries the verbatim AWS `requestContext` (`apiId` / `accountId` / `requestId` / `queryString` / `operationName` / `variables`); rejected authorizers surface as `UnauthorizedException` (HTTP 401). Contributed by @AdigaAkhil. **API Gateway v2 — JWT authorizer resolves JWKS via OIDC discovery** — the JWKS path was hardcoded to `{issuer}/.well-known/jwks.json` (404 for Salesforce `/id/keys`, Okta `/oauth2/v1/keys`, etc.); the resolver now fetches `{issuer}/.well-known/openid-configuration` and reads `jwks_uri` per the documented AWS behavior, 2h cache, Cognito short-circuit preserved, conventional path as fallback. Contributed by @Pratham2703005. **S3 — `PutObject` checksums end-to-end** — SHA256 / SHA1 / CRC32 client-supplied values are accepted; `x-amz-sdk-checksum-algorithm` triggers server-side compute and validates mismatches with `BadDigest`; `Get` / `HeadObject` surface them under `x-amz-checksum-mode: ENABLED`; `CopyObject` propagates the source's checksum; versioned reads honor per-version checksums; checksums never accompany a `206 Partial Content` reply; CRC32C / CRC64NVME are rejected with `InvalidRequest` instead of silently round-tripping unverifiable client-supplied values. Reported by @Guigoz. **S3 — on-disk bucket directory is account-scoped** — `CreateBucket`'s `makedirs` was at `DATA_DIR/` while every object write goes to `DATA_DIR///`; now scoped end-to-end, and `DeleteBucket` cleans it up. Reported by @rsking. **Glue — `BatchUpdatePartition`** — closes the last partition action; matches AWS per-entry shape (`Entries[*].{PartitionValueList, PartitionInput}` in, `Errors[*].{PartitionValueList, ErrorDetail}` out); preserves `CreationTime`, refreshes `LastAccessTime`. Contributed by @yonatoasis. **Glue — `StartJobRun` script resolution + crawler completion under non-default accounts** — `_resolve_script` built an unscoped on-disk path so file-backed scripts never matched; `_finish_crawl` ran on a `threading.Timer` without contextvars so crawlers under non-default accounts hung in `RUNNING` forever. Both background paths now carry the request's account via `contextvars.copy_context().run(...)`, mirroring the established `stepfunctions.py` / `rds.py` idiom. Contributed by @AdigaAkhil. **CloudFormation — AppConfig `Environment` / `ConfigurationProfile` / `HostedConfigurationVersion` / `DeploymentStrategy` / `Deployment`** — the rest of the AppConfig CFN surface (Application landed in v1.3.55); property names, defaults, Ref returns and `Fn::GetAtt` attribute names match the AWS reference verbatim; `HostedConfigurationVersion` enforces the optional `LatestVersionNumber` locking token; `Deployment` tags stored against the AWS-shape ARN. Reported by @zdenekmartinec. - [v1.3.58 — June 4, 2026](https://ministack.org/blog/changelog-v1-3-58.html): The headline fix: **Lambda — `UpdateFunctionConfiguration(Layers=[...])` finally works after the first invoke** — `GetFunctionConfiguration.Layers[*].CodeSize` now reports the layer's real zip size (was hardcoded `0` in both `CreateFunction` and `UpdateFunctionConfiguration`); the warm worker is recycled when `Layers` / `Runtime` / `Handler` / `Environment` / `MemorySize` / `Architectures` / `VpcConfig` / `FileSystemConfigs` change, so the next `Invoke` mounts the layer at `/opt/layer_N` and the handler can `import` from it. Reported by @omargr299. **KMS — Ed25519 (`ECC_NIST_EDWARDS25519`) sign / verify** — real Ed25519 keypair generation; `SigningAlgorithms=["ED25519_SHA_512","ED25519_PH_SHA_512"]` per the AWS Developer Guide "Supported signing algorithms for ECC key specs" table; `Sign` / `Verify` enforce `MessageType=RAW` for `ED25519_SHA_512`; `ED25519_PH_SHA_512` (Ed25519ph / HashEdDSA, RFC 8032 §5.1) returns `UnsupportedOperationException` instead of producing wire-incompatible signatures via pure Ed25519. Contributed by @KABBOUCHI. **EKS — default `topology.kubernetes.io/zone` and `topology.kubernetes.io/region` labels on k3s nodes** — matches the labels real EKS nodes carry via the AWS cloud-controller-manager; unblocks Karpenter / Cluster Autoscaler / scheduler `topologySpreadConstraints` without manual `kubectl label node`. Region resolves through `get_region()`; per-node-group label overrides belong on `CreateNodegroup.labels` — the AWS-shape-correct surface. Contributed by @b-rajesh. **ELBv2 — `SetSubnets`, `SetIpAddressType`, `SetSecurityGroups`** — three load-balancer mutation actions, output shapes verified against botocore (`SetSecurityGroups` returns `SecurityGroupIds`, not `SecurityGroups`). **Glue — `UserDefinedFunction` APIs** — full lifecycle (`CreateUserDefinedFunction` / `UpdateUserDefinedFunction` / `DeleteUserDefinedFunction` / `GetUserDefinedFunction` / `GetUserDefinedFunctions`) with the AWS-required `Pattern` glob on `GetUserDefinedFunctions`. **IAM — real EKS managed-policy seeds** — `AmazonEKSClusterPolicy`, `AmazonEKSWorkerNodePolicy`, `AmazonEKS_CNI_Policy`, `AmazonEKSServicePolicy` now carry the verbatim AWS documents instead of the wildcard `Allow *` fallback, so Terraform diffs and policy simulators behave the same as real AWS. **EC2 — `Attachment.AttachTime` on ENI describe** matches the AWS `NetworkInterfaceAttachment` shape; required by tools that audit attachment age (Cloud Custodian, Config rules). **Glue — `CreateDatabase` honors top-level `Tags`** (previously dropped) and `DeleteDatabase` cleans them up. **Glue — `UpdateTable` optimistic concurrency via `VersionId`** — stale `VersionId` returns `ConcurrentModificationException`; calls without `VersionId` keep the old back-compat behavior. **ECS — `DeleteService` marks the record `INACTIVE`** instead of removing it, matching the AWS contract that "Services in the `DRAINING` or `INACTIVE` status can still be viewed with the `DescribeServices` API operation". **Cognito — `CUSTOM_AUTH` trigger Lambdas no longer deadlock the event loop** — the IDP / Identity dispatchers are now `async` and run sync handlers via `asyncio.to_thread`, so the loop stays free to serve the Lambda's HTTP callback. Reported by @aahoughton. - [v1.3.57 — June 3, 2026](https://ministack.org/blog/changelog-v1-3-57.html): Karpenter and Cluster Autoscaler can finally run against MiniStack locally, EKS gains OIDC Identity Provider Config, and DynamoDB stops lying about export/import status at submit time. **EC2 Fleet — `CreateFleet` + `DescribeFleets`** with `instant`-type synchronous launch + multi-config × multi-override capacity distribution (Karpenter's 2 configs × 2 overrides × `TotalTargetCapacity=4` launches one of each combination); `DefaultTargetCapacityType`-driven spot/on-demand selection (the top-level `FleetType` enum is `{request, maintain, instant}` and doesn't include `"spot"`); shape parity restored — `Instances` + `Errors` emitted only when `Type=instant`, `maintain` / `request` return `FleetId` alone with `FulfilledCapacity=0` + `ActivityStatus=pending_fulfillment`; `DescribeFleets` on unknown id → `InvalidFleetId.NotFound`. Reported and contributed by @b-rajesh. **EKS — OIDC Identity Provider Config** — `AssociateIdentityProviderConfig` / `DescribeIdentityProviderConfig` / `DisassociateIdentityProviderConfig` at `/clusters/{name}/identity-provider-configs/{verb}`. AWS allows one OIDC IdP per cluster regardless of name (any second associate → `ResourceInUseException`); issuer + client ID + claims forwarded to k3s via `--kube-apiserver-arg=oidc-*`; IdP ARN stable across describes so Terraform / CDK / Pulumi don't see drift; tags reachable via `ListTagsForResource(resourceArn=idp_arn)`; cluster stays `ACTIVE` (the work is in the returned `update` record). Contributed by @b-rajesh. **DynamoDB — `ExportTableToPointInTime` + `ImportTable` return `IN_PROGRESS` at submit time** — both used to emit `COMPLETED` synchronously; `DescribeExport` / `DescribeImport` now stay `IN_PROGRESS` within the grace window (`MINISTACK_DDB_EXPORT_COMPLETE_AFTER_SEC` / `MINISTACK_DDB_IMPORT_COMPLETE_AFTER_SEC`, default 1s) and flip to `COMPLETED` + stamp `EndTime` after. Reported by @hicksy. Export contributed by @HarrisonTCodes. **DynamoDB PartiQL — `UPDATE` / `DELETE` with a false non-key predicate returns `ConditionalCheckFailedException`** — previously silently no-op'd because the handler iterated every row against the full WHERE clause; AWS treats non-key clauses as a conditional check on the PK-targeted item. Now splits WHERE into PK equalities + "the rest", requires `=` on every PK attribute (else `ValidationException`), and returns CCF when the targeted item is missing or any non-key predicate fails. Reported by @hicksy. **SQS — `/_ministack/sqs/messages` admin endpoint** — returns every queue's messages grouped by account with optional `?account` / `?QueueUrl` filters; pure introspection, no `visible_at` / `receive_count` mutation. Reported by @mbamber. **RDS — `MINISTACK_RDS_PUBLIC_ENDPOINT` env var** — set `1` when MiniStack runs in Docker but RDS clients connect from outside that network; `DescribeDBInstances` then returns `{MINISTACK_HOST, host_port}` (the externally-reachable host-published port). Off by default. **AppConfigData — `StartConfigurationSession` accepts identifier by ID or name** — `service-2.json` documents both shapes; ministack treated them as IDs only. Contributed by @LiamMacP. **`MINISTACK_HOST` honored consistently across services** — ECS / ElastiCache / OpenSearch / Lambda subprocess paths previously hardcoded `"localhost"`. Contributed by @neriyaco. - [v1.3.56 — June 2, 2026](https://ministack.org/blog/changelog-v1-3-56.html): Two new flows that unblock entire integration categories, a Lambda runtime gap closed for X-Ray, the missing half of the Firehose Kinesis-source fix from v1.3.53, and a deep DynamoDB error-message conformance pass. **Cognito User Pools — `CUSTOM_AUTH` flow with DefineAuthChallenge / CreateAuthChallenge / VerifyAuthChallengeResponse Lambda triggers** — `InitiateAuth` / `AdminInitiateAuth` / `RespondToAuthChallenge` / `AdminRespondToAuthChallenge` now run the full custom-auth state machine through the configured Lambdas; session TTL honors the client's `AuthSessionValidity`, capped at 3 answered rounds per AWS. Unblocks passwordless / magic-link / SMS-OTP flows. Reported by @aahoughton. Contributed by @AdigaAkhil. **EKS Access Entries — modern post-1.29 IAM bindings replacing the aws-auth ConfigMap** — 8 ops at `/clusters/{name}/access-entries[/{principalArn}[/access-policies[/{policyArn}]]]`: `CreateAccessEntry`, `DescribeAccessEntry`, `ListAccessEntries`, `UpdateAccessEntry`, `DeleteAccessEntry`, `AssociateAccessPolicy`, `DisassociateAccessPolicy`, `ListAssociatedAccessPolicies`. `accessScope` validated against `{cluster, namespace}` with `namespaces` required when namespace-bound; deleting an access entry cascades its associated policies. Unblocks Crossplane `accessentry.eks.aws.upbound.io` and Terraform `aws_eks_access_entry` + `aws_eks_access_policy_association`. Reported by @b-rajesh. **Lambda — `_X_AMZN_TRACE_ID` injected for `TracingConfig.Mode=Active`** — synthesized per invocation in the AWS-canonical format (`Root=1-<8hex>-<24hex>;Parent=<16hex>;Sampled=1`) and threaded into the warm Python / Node pool (bootstrap pops the per-event value into `os.environ` / `process.env`), provided runtimes, and the local subprocess executor. Docker RIE path is documented as upstream-unsupported (AWS RIE drops X-Ray) and logs a warning. Reported by @arivazhaganjeganathan-abc. **Firehose — Lambda processor invoked in the delivery pipeline** — `ProcessingConfiguration.Processors[].Type=Lambda` is now consulted at invocation time. Per-batch event `{invocationId, deliveryStreamArn, region, records:[{recordId, approximateArrivalTimestamp, data}]}`; response `{records:[{recordId, result, data}]}` with `result ∈ {Ok, Dropped, ProcessingFailed}`. Lambda failures pass records through unchanged (best-effort per AWS). Applies to both `PutRecord` / `PutRecordBatch` and `KinesisStreamAsSource` fan-out. Reported by @arivazhaganjeganathan-abc. **Cognito CUSTOM_AUTH — `issueTokens` on the cap-boundary attempt now wins over MaxAttempts**. **DynamoDB — AWS-canonical error-message parity across 24 operations** driven by `dynamodb-conformance.org`: set-duplicates include collection contents (`Input collection [a, a] contains duplicates.`), `UpdateExpression` syntax errors carry token-context, hash / range key mutation pre-rejected, redundant-parens + `begins_with` non-string operand pre-validated at parse time so empty tables still reject, `BatchExecuteStatement` per-statement `Error.Code` drops `Exception` suffix to match `BatchStatementErrorCodeEnum`, `TransactGetItems` missing-key surfaces via cancellation reasons, size-exceeded envelopes include the Java-toString dump. - [v1.3.55 — June 1, 2026](https://ministack.org/blog/changelog-v1-3-55.html): A new control-plane service, two EKS additions that unblock terraform IRSA modules, and four API Gateway Lambda-proxy correctness fixes — three contributed and all verified against real AWS. **AWS Elemental MediaConnect — control-plane stub** — 5 ops at `/v1/flows[/{FlowArn}]` and `/tags/{ResourceArn}`: `CreateFlow`, `DescribeFlow`, `ListFlows`, `UpdateFlow`, `ListTagsForResource`. `ListFlows` returns the slimmer `ListedFlow` projection; `UpdateFlow` is narrow to the AWS-allowed fields (`SourceFailoverConfig`, `Maintenance`, `SourceMonitoringConfig`, `NdiConfig`). No real streaming — flows are control-plane metadata, enough to integration-test services that wrap the MediaConnect API. Reported by @tashif-hoda. **EKS `AssociateEncryptionConfig` + OIDC discovery / JWKS for IRSA** — new `POST /clusters/{name}/encryption-config/associate` records KMS encryption config and rejects re-association (matches AWS, which only allows adding encryption to a cluster that has none). Each cluster's `identity.oidc.issuer` now points at a ministack-hosted URL (`/oidc/id/{32-char-id}`) instead of the unreachable real `oidc.eks.{region}.amazonaws.com`; `GET /.well-known/openid-configuration` and `GET /keys` are served with `authorization_endpoint: "urn:kubernetes:programmatic_authorization"` and `claims_supported: ["sub","iss"]` matching real EKS — enough for terraform's `aws_iam_openid_connect_provider` to fetch the document. Reported by @b-rajesh. **API Gateway v2 Lambda-proxy — `Set-Cookie` from `headers` and the `cookies` array now both ship** — observed real-AWS behavior is to emit the array entries first followed by any header `Set-Cookie`; the earlier supersede approach silently dropped the header cookie. **API Gateway v2 Lambda-proxy — `isBase64Encoded` honored in both directions** — only `text/*` and `application/json` / `application/xml` / `application/javascript` arrive as UTF-8 strings; everything else (incl. missing `Content-Type` and `application/x-www-form-urlencoded`) is base64-encoded with `isBase64Encoded: true`. Outbound base64 bodies are decoded to raw bytes — HTTP API has no `binaryMediaTypes` negotiation. Contributed by @rmlasseter. **API Gateway v1 Lambda-proxy — `binaryMediaTypes` is now wired** — request bodies matching a configured pattern arrive base64 with `isBase64Encoded: true`; response base64 bodies are decoded only when the request `Accept` also matches. Wildcards (`*/*`, `type/*`) honored on the configured side; a request `Accept` of `*/*` does NOT auto-match specific configured types — verified against real AWS. Contributed by @rmlasseter. **API Gateway v1 & v2 Lambda-proxy — case-insensitive header override** — a lowercase `content-type` from a Lambda response now replaces the seeded default rather than shipping two `Content-Type` headers (RFC 9110 §5.1). Contributed by @rmlasseter. - [v1.3.54 — May 30, 2026](https://ministack.org/blog/changelog-v1-3-54.html): One unblocking EKS feature, one cross-cutting API Gateway bug fix from a first-time contributor, and two AWS-shape corrections. **EKS addons** — 5 new ops at `/clusters/{name}/addons[/{addonName}[/update]]` (`CreateAddon`, `DescribeAddon`, `ListAddons`, `UpdateAddon`, `DeleteAddon`), unblocking `aws_eks_addon` for the standard cluster bootstrap (`vpc-cni`, `coredns`, `kube-proxy`, `aws-ebs-csi-driver`). Status flips to `ACTIVE` immediately on create / update, matching the nodegroup shortcut. Reported by @b-rajesh. **API Gateway Lambda-proxy `Set-Cookie` and `multiValueHeaders` now reach the client** — v2 maps the payload-format-2.0 `cookies` array to a list-valued `Set-Cookie` (RFC 6265 §3 forbids comma-folding); v1 folds format-1.0 `multiValueHeaders` into the response with `multiValueHeaders` winning over `headers` on key collision per the AWS contract; `_send_response` expands list-valued headers into one wire line per entry. The v1 collision check is case-insensitive (HTTP headers are case-insensitive per RFC 7230 §3.2), so `Set-Cookie` in `headers` plus `set-cookie` in `multiValueHeaders` correctly resolves to MVH wins instead of shipping both. Contributed by @rmlasseter. **DynamoDB data-plane table-name length range** — data-plane ops (`PutItem` / `GetItem` / `UpdateItem` / `DeleteItem` / `Query` / `Scan`) now accept the AWS-documented 1..255 range (the 3-char minimum is a `CreateTable`-only rule). NULL-attribute error message text aligned. **EC2 launch template version XML shape** — `CreateLaunchTemplateVersion` returns a single `` struct; `DescribeLaunchTemplateVersions` wraps in ``. The `` wrapper now belongs at the list-context boundary, not on the inner struct. - [v1.3.53 — May 30, 2026](https://ministack.org/blog/changelog-v1-3-53.html): One real bug fix, a deep DynamoDB conformance pass, and AWS error-type clean-up across eight services. **Firehose `KinesisStreamAsSource` → S3 fan-out** — delivery streams of type `KinesisStreamAsSource` with an `ExtendedS3` / `S3` destination now actually consume records from the source Kinesis stream and forward them to S3 (previously the source config round-tripped on `DescribeDeliveryStream` but no consumer ever read the records). Fan-out fires inline from Kinesis `PutRecord` / `PutRecords` (same pattern as SNS→SQS), honors `Prefix` and `DeliveryStartTimestamp`, best-effort so a Firehose problem can never break the producer. Reported by @arivazhaganjeganathan-abc. **DynamoDB Tier 3 conformance pass** — 30+ message-text fixes so the strings returned by ministack match the strings captured from real AWS by the dynamodb-conformance.org Tier 3 suite: non-existent-table responses uniformly returning `"Requested resource not found"` across `Batch*` / `Transact*` / single-item ops; `BatchWriteItem` / `BatchGetItem` empty `RequestItems` using `"The requestItems parameter is required."`; per-expression-scoped undefined-reference errors (`"Invalid FilterExpression: An expression attribute value used in expression is not defined; attribute value: :v"`); empty `KeyConditionExpression` / `UpdateExpression` using the `"Invalid {Expression}: The expression cannot be empty;"` template; `Scan` `Segment` validation aligned to AWS phrasing; set / NULL / empty-BS / `KeySchema` / index-validation / billingMode / tableClass / deletion-protection / `Limit` / GSI-not-found messages all aligned; `ListTagsOfResource` on a non-existent ARN returns `AccessDeniedException` (AWS security-through-obscurity contract). Plus two functional bugs: GSI / LSI `INCLUDE` and `KEYS_ONLY` projections are now enforced on `Query` and `Scan`; parallel `Scan` partitions items deterministically across segments by hashing the partition key; LSI sparse semantics drop items lacking the range-key attribute. **AWS error-type fixes** across EFS (per-resource-type `FileSystemNotFound` / `AccessPointNotFound`), S3 Tables (`NotFoundException`), CloudFront KeyValueStore / Account (`ValidationException`), API Gateway v1 (`BadRequestException` instead of an invented 405), ECS / Batch (`ClientException`), MWAA (`ValidationException`), OpenSearch (`ValidationException`). KMS / MWAA / Inspector2 no longer leak Python exception text in `InternalServerException` / `InvalidCiphertextException` responses. **IMDS instance-profile-ID literal assembled at runtime** so credential-pattern secret scanners stop false-flagging `AIPA…` in `imds.py`. Reported by @diplomatic-ms. - [v1.3.52 — May 29, 2026](https://ministack.org/blog/changelog-v1-3-52.html): One major Lambda feature, an S3 tagging refinement, and a CloudFormation resource type — all three close open GitHub issues. **Lambda Durable Functions (Durable Execution)** — full support for the AWS preview API (`2025-12-01`): `CreateFunction` with `DurableConfig`, the seven management ops (`CheckpointDurableExecution`, `GetDurableExecutionState`, `GetDurableExecution`, `GetDurableExecutionHistory`, `ListDurableExecutionsByFunction`, `StopDurableExecution`), and the three external callback ops (`SendDurableExecutionCallbackSuccess`, `SendDurableExecutionCallbackFailure`, `SendDurableExecutionCallbackHeartbeat`). A resume scheduler fires `WAIT` expiries, callback timeouts (`Callback.Timeout` / `Callback.Heartbeat`), and step-retry backoffs (`NextAttemptDelaySeconds`); state persists across restarts — the in-memory callback index is rebuilt from restored executions on boot and pending timers are re-armed. End-to-end verified against the official `aws-durable-execution-sdk-python` 1.5.0 and `aws-durable-execution-sdk-java` 2.44.13. Reported by @youngkwangk. **S3 object tagging by `versionId`** — `GET` / `PUT` / `DELETE` `?tagging` honor the `versionId` query parameter, and `PutObject` / `POST` form upload store the `x-amz-tagging` header against the resulting version rather than the object key. Reported by @barrywilks7. **CloudFormation `AWS::AppConfig::Application`** — create and delete provisioners; physical id is the application id and `ApplicationId` is exposed via `Fn::GetAtt`. Reported by @zmartinec. Also: `MaxItems` on durable Lambda list / state / history is now bounded to AWS's `[0, 1000]` range (out-of-range → 400 `InvalidParameterValueException`); `CheckpointDurableExecution` rejects malformed `Updates`; `StopDurableExecution` on a terminal execution returns 400. - [v1.3.51 — May 27, 2026](https://ministack.org/blog/changelog-v1-3-51.html): A DynamoDB-heavy release driven by the [dynamodb-conformance.org](https://dynamodb-conformance.org) gap report. **DynamoDB backups & restore** — `CreateBackup`, `DescribeBackup`, `DeleteBackup`, `ListBackups`, `RestoreTableFromBackup`, `RestoreTableToPointInTime`; restore rebuilds with snapshot items, key schema, and indexes; `BillingModeOverride`, `GlobalSecondaryIndexOverride`, `LocalSecondaryIndexOverride` honored; state persists. **Export / Import** — `ExportTableToPointInTime`, `DescribeExport`, `ListExports`, `ImportTable`, `DescribeImport`, `ListImports`; both idempotent on `ClientToken`. **Contributor Insights** — `UpdateContributorInsights`, `DescribeContributorInsights`, `ListContributorInsights`; ENABLING→ENABLED / DISABLING→DISABLED settling matches real AWS; optional `IndexName` validated against the GSI list. **Resource-Based Policies** — `PutResourcePolicy`, `GetResourcePolicy`, `DeleteResourcePolicy` with full revision-id semantics, the `NO_POLICY` conditional path, 20 KB policy size cap. **PartiQL transactions and batches** — `BatchExecuteStatement`, `ExecuteTransaction`, `ClientRequestToken` idempotency, all-or-nothing rollback, `DuplicateItemException` on duplicate `INSERT`. **`DescribeLimits`** returns canonical account and table read/write capacity limits. **EC2 security-group lifecycle** — `CreateSecurityGroup` returns `SecurityGroupArn`, `DeleteSecurityGroup` returns `GroupId`, `RevokeSecurityGroupEgress` returns `RevokedSecurityGroupRules`; `DescribeSecurityGroups` distinguishes malformed IDs (`InvalidGroupId.Malformed`) from missing ones (`InvalidGroup.NotFound`). Contributed by @Areson. Plus ~20 DynamoDB validator and shape fixes covering item validation, batch and transaction caps, full `CreateTable` / `UpdateTable` validation, `Query` and `Scan` validation, `UpdateItem` pre-update snapshot semantics, nested `ProjectionExpression`, binary key ordering, reserved keyword rejection, and `ExpressionAttribute` bookkeeping. Glue `StartJobRun` no longer auto-pulls missing Docker images; MWAA worker containers auto-remove on exit. - [v1.3.50 — May 26, 2026](https://ministack.org/blog/changelog-v1-3-50.html): One new service, an analytics upgrade for Glue, two new IAM ops, plus AWS-shape fixes on Lambda and EC2. **S3 Tables (`s3tables`)** — new service emulator for the AWS S3 Tables API: table buckets, namespaces, and Iceberg-format tables. Control plane covers `CreateTableBucket`, `ListTableBuckets`, `GetTableBucket`, `DeleteTableBucket`, `CreateNamespace`, `ListNamespaces`, `GetNamespace`, `DeleteNamespace`, `CreateTable`, `ListTables`, `GetTable`, `DeleteTable`, `GetTableMetadataLocation`, `UpdateTableMetadataLocation`. Ships with an embedded **Iceberg REST catalog** at `/iceberg` so Spark jobs configured with `spark.sql.catalog.*.type=rest` and `spark.sql.catalog.*.uri=http:///iceberg` can create, load, and commit Iceberg tables without an external catalog server; data files in S3, metadata in memory. **Glue Spark jobs run on the official `amazon/aws-glue-libs` PySpark image** — `GlueVersion: 4.0` and `3.0` map to their canonical AWS Glue images (`glue_libs_4.0.0_image_01` / `glue_libs_3.0.0_image_01`); override via `GLUE_DOCKER_IMAGE`. Job containers run on MiniStack's Docker network so they reach S3, RDS, and other ministack services by container hostname. **IAM `UpdateAccessKey`** — toggles an access key between `Active` and `Inactive`, matching the two statuses real AWS accepts; optional `UserName` validated when provided. **IAM `GetAccessKeyLastUsed`** — returns the AWS "never used" shape (`Region` / `ServiceName` = `N/A`, no `LastUsedDate`) since MiniStack does not track per-key usage history. Both contributed by @lahmish. **Lambda invocation log includes user output alongside the traceback on error** — when a handler raised after printing, the response log dropped the user output and only returned the traceback; both are now returned, newline-separated, matching real Lambda CloudWatch Logs output. Contributed by @Baptiste-Garcin. **EC2 `CreateVpcEndpoint` and `CreateFlowLogs` now persist `TagSpecifications`** — tags passed at creation time were silently dropped. Tags are now stored, returned by `DescribeFlowLogs`, and cleaned up on `DeleteFlowLogs`. The `fl-` prefix is also registered in the resource-type guesser so flow-log IDs are correctly resolved by the Resource Groups Tagging API. Contributed by @lahmish. - [v1.3.49 — May 25, 2026](https://ministack.org/blog/changelog-v1-3-49.html): One new service, three contributor improvements, persistence and AWS-shape fixes. **Amazon Inspector2** — new service emulator covering 14 ops (`Enable`, `Disable`, `ListFindings` with filtering/sorting/pagination, `BatchGetFindingDetails`, `ListCoverage`, `ListCoverageStatistics`, `ListFindingAggregations`, `SearchVulnerabilities`, tag and filter CRUD); generates deterministic stub vulnerability findings for ECR container images, Lambda functions, and EC2 instances when enabled. Contributed by @ry-allan. **RDS auto-respawn at boot** with `PERSIST_STATE=1` — MiniStack now eager-imports the RDS module at startup and respawns the Docker container for every persisted instance immediately, no client API call required; zero idle cost when no `rds.json` exists. Reported by @doodaz. **Glue persists view fields** on `CreateTable` / `UpdateTable` — `ViewOriginalText` / `ViewExpandedText` (contributed by @yonatoasis) so Trino's iceberg connector stops failing with `viewOriginalText must be present`, plus `ViewDefinition` and `IsMultiDialectView` round-tripped for newer Spark 3.4+ / Glue 4.0 / Lake Formation multi-dialect view clients. **AppSync Events resources persist** across restarts (`PERSIST_STATE=1`) — Event APIs, channel namespaces, and API keys were silently dropped on restart because the events module was reached only via a cross-service call from the parent AppSync handler; the same fix covers related restart drops for `apigateway_v1` on first boot, Lambda-auto-created CloudWatch log groups, and EventBridge targets fired by S3 notifications. Reported by @yaegassy. **RDS respawn no longer fails with `port is already allocated`** — restored DB instances were trying to bind the engine's standard port (5432 for postgres, 3306 for mysql) on the host instead of the original docker host port; the host port is now tracked separately, probed for freeness before reuse, and falls back to a fresh free port if something else has taken it. Stale `Created`-status containers from prior failed boots are force-removed before respawn so they don't hold the binding. Reported by @doodaz. **CloudFront `DistributionSummary` parity** — `ListDistributions` round-trips the stored origin configuration (`Origins`, `DefaultCacheBehavior`; contributed by @CoffeeRaptor), plus the remaining required fields of the real AWS shape (`Aliases`, `CacheBehaviors`, `CustomErrorResponses`, `PriceClass`, `ViewerCertificate`, `Restrictions`, `WebACLId`, `HttpVersion`, `IsIPV6Enabled`, `Staging`) with minimal-but-valid defaults so strict-parsing SDKs (Go v2, Java v2) don't reject the response. - [v1.3.48 — May 24, 2026](https://ministack.org/blog/changelog-v1-3-48.html): Two ships. **S3 `GetObjectAcl` and `PutObjectAcl`** — both `?acl` subresource operations are now implemented. `GetObjectAcl` returns the stored policy or, if none has been set, the AWS default of a single `FULL_CONTROL` Grant to the request account-id owner. `PutObjectAcl` accepts either a canned ACL via the `x-amz-acl` header (`private`, `public-read`, `public-read-write`, `authenticated-read`, `aws-exec-read`, `bucket-owner-read`, `bucket-owner-full-control`) or a full `` XML body; invalid canned values return `InvalidArgument` and malformed bodies return `MalformedACLError`. As with retention, legal-hold, and bucket policies, the ACL is stored and round-tripped but not enforced on the data plane. `NoSuchKey` for missing keys. Reported by @smpial. **RDS persistence-restore module-import race** — the v1.3.47 restore-respawn daemon threads called `_get_docker()` which was defined further down in the same module, so a thread reaching the lookup before the parser finished raised `NameError: name '_get_docker' is not defined` and stranded the restored instance in `creating`. The `load_state("rds")` block now runs at the bottom of the module, after every helper the restore threads can touch. Reported by @doodaz. - [v1.3.47 — May 23, 2026](https://ministack.org/blog/changelog-v1-3-47.html): One new CloudFormation feature, three contributor bug fixes, two Cognito quality-of-life improvements for federated sign-in debugging. **CloudFormation nested stacks (`AWS::CloudFormation::Stack`)** — child template is fetched from `TemplateURL` (resolved against the local S3 emulator), `Parameters` pass through, child resources provision inline, and `Outputs` flow back to the parent via `Fn::GetAtt: [Nested, Outputs.]` — the same sub-attribute form CDK emits from the `NestedStack` construct. `Ref` resolves to the child `StackId` ARN matching real AWS; delete and update propagate to the child; the child stack gets its own `_stacks` entry with `RootId`/`ParentId` pointers so it shows up in events under its own ID. Reported by @jayalfredprufrock. **CloudFront `CreateInvalidation` caller-reference idempotency** — repeated calls with the same `CallerReference` on the same distribution now return the existing invalidation rather than creating duplicates; path comparison is set-based so re-submitting the same paths in a different order counts as the same batch (real AWS treats paths as a set). Different paths under the same `CallerReference` surface `InvalidationBatchAlreadyExists`. Contributed by @CoffeeRaptor. **S3 `DeleteObjects` removes keys from disk** — singular `DeleteObject` already purged the persisted file plus its `.meta.json` sidecar, but the batch path only cleared in-memory metadata, leaving orphaned files on disk after a `S3_PERSIST=1` cleanup. `DeleteObjects` now calls `_delete_persisted_object` per deleted key, mirroring `DeleteObject`. Contributed by @parafoxia. **RDS persisted-instance container restart on reload** — with `PERSIST_STATE=1`, restart used to leave persisted DB instances marked `available` with no backing container (zombie metadata that `StartDBInstance` couldn't recover). The restore path now spawns a daemon thread per persisted instance, removes any stale `ministack-rds-` container, runs a fresh container with the persisted volume + saved credentials, and transitions `creating → available/failed` based on container start. Each daemon thread inherits the original account's `_request_account_id` contextvar so multi-tenant restores land writes on the right account. **Cognito `/oauth2/idpresponse` and `/saml2/idpresponse` diagnostics** — distinct error messages for missing `code`, missing `state`, and unknown/expired relay (previously all opaque `InvalidParameterException`); server-side WARNING log when the relay key from the IdP doesn't match any pending authorize flow (5-minute TTL, single-use), naming the exact remediation. Configuration drift in Keycloak / Auth0 / Okta is now diagnosable from the response body alone. Reported by @ocr-lasagna. **Cognito `.well-known/*` no longer shadows S3** — `GET //.well-known/jwks.json` and `GET //.well-known/openid-configuration` fall through to the S3 handler when `poolId` isn't a registered user pool, so apps storing their own OIDC discovery doc as an S3 object aren't shadowed by a fake Cognito JWKS body. Real AWS only serves these for actual pools. - [v1.3.46 — May 21, 2026](https://ministack.org/blog/changelog-v1-3-46.html): One new S3 feature and one Cognito JWT correctness fix. **S3 `PutObject` conditional writes** — `If-None-Match: "*"` gives create-once semantics (write only if no object exists at the key), `If-Match: ""` gives optimistic concurrency (write only if the current etag matches). Both are now enforced on `PutObject`, matching the AWS feature shipped November 2024. Precondition violations return `412 PreconditionFailed`, with one documented exception per the AWS user guide: `If-Match: ""` against a missing key returns `404 NoSuchKey`. ETag comparison strips surrounding quotes on both sides so quoted and unquoted clients both work. The symmetric `x-amz-copy-source-if-match` headers on `CopyObject` were already supported; this closes the gap on plain `PutObject`. Contributed by @mattcookio. **Cognito JWT `iss` claim uses the pool's region, not the request region** — when a SigV4 scope carried a different region from the pool's creation region, the JWT `iss` was stamped with the request region, so `iss == https://cognito-idp..amazonaws.com/` mismatched the pool ID prefix and standards-compliant validators rejected the token. A new `_pool_region(pool_id)` resolver parses the region directly from the pool ID (`{region}_{suffix}`) and is applied to the JWT `iss`, the PreTokenGeneration trigger event region, the user-pool ARN, the OIDC discovery `issuer`, and the hosted-UI CloudFront URL returned by `CreateUserPoolDomain` / `DescribeUserPoolDomain`. Accepts 3-segment commercial regions and 4-segment GovCloud / ISO regions (`us-gov-east-1`, `us-iso-east-1`, etc.). Contributed by @subrotosanyal. - [v1.3.45 — May 20, 2026](https://ministack.org/blog/changelog-v1-3-45.html): Three reported bugs fixed. **CloudWatch Logs `GetLogEvents` / `FilterLogEvents` accept `logGroupIdentifier`** — both ops now resolve the target log group from either `logGroupName` or `logGroupIdentifier` (bare name or a full `arn:aws:logs:::log-group:[:*]` ARN) per the AWS API reference. Calls passing an ARN no longer fail with `ResourceNotFoundException: The specified log group does not exist: None`. The wildcard `:*` suffix on the ARN form is stripped before lookup. `StartQuery` and `StartLiveTail` use a different plural shape (`logGroupIdentifiers`) and stay out of scope. Reported by @msulima. **ElastiCache `DescribeCacheClusters` emits the full `CacheNode` shape** — per-node XML now includes `CacheNodeCreateTime` (ISO8601), `ParameterGroupStatus`, `CustomerAvailabilityZone` (derived from the cluster's `PreferredAvailabilityZone`), and `SourceCacheNodeId` (when non-empty), in addition to the previous `CacheNodeId` / `CacheNodeStatus` / `Endpoint`. `hashicorp/terraform-provider-aws` v6.45.0 reads `CacheNodeCreateTime` without a nil check after `aws_elasticache_cluster` apply, so the previous response caused `Unexpected nil pointer in: {CacheNodeCreateTime: ...}` and stranded Terraform on a deployed-but-unconfirmed cluster. `CustomerOutpostArn` stays unset — real AWS omits the element rather than emitting it empty. Reported by @trackme-ddisley. **API Gateway v1 `UpdateStage` boolean fields parsed from patch strings** — AWS's `UpdateStage` API doc is explicit that every `patchOperations[].value` is a string, even for boolean fields like `tracingEnabled` and `cacheClusterEnabled`; SDK v2 clients read the stage back and expect `*bool`, failing deserialization with `expected Boolean to be of type *bool, got string instead`. The v1 PATCH handler now coerces patch values to `bool` when the path is exactly `/tracingEnabled` or `/cacheClusterEnabled` — stage variables whose name happens to be `tracingEnabled` aren't affected because the match is exact, not substring. Method-settings patches (which already coerced their own booleans on per-method paths) are unchanged. Contributed by @duc12597. - [v1.3.44 — May 19, 2026](https://ministack.org/blog/changelog-v1-3-44.html): Three ships. **Step Functions JSONata standard library expansion** — the evaluator now ships ~30 built-in functions on top of the existing core: string (`$uppercase`, `$lowercase`, `$substring`, `$trim`, `$contains`, `$split`, `$join`, `$replace`, `$pad`), numeric (`$sum`, `$average`, `$max`, `$min`, `$abs`, `$floor`, `$ceil`, `$round`, `$power`, `$sqrt`, `$formatNumber`), array (`$sort` including the `function($l, $r){...}` comparator form, `$reverse`, `$distinct`, `$append`), object (`$keys`, `$values`, `$lookup` over both objects and arrays-of-objects, `$exists`), type (`$type`, `$boolean`), date/time (`$now()` / `$now(picture)` / `$now(picture, timezone)` with the XPath-3.1 picture-string subset, `$millis`), utility (`$uuid`, `$base64encode`, `$base64decode`). `$contains`/`$split`/`$replace` accept JSONata regex literals (`/pattern/flags`); `$replace` supports `$1`/`$&` substitution refs. `$exists` distinguishes a missing path from an explicit `null` per the JSONata spec — the motivating Choice condition `{% $exists($states.input.userId) %}` routes correctly whether the field is present, explicitly null, or missing. `$round` uses banker's rounding (round-half-to-even). Implemented natively in Python with no new runtime dependency. **RDS Data API readiness fix**: when a Docker-backed RDS instance is still bootstrapping, `ExecuteStatement` / `BatchExecuteStatement` previously fell back to the in-memory SQL stub on any connection error and returned a 200 acknowledging `CREATE USER` / `GRANT` statements that never reached MySQL. The fallback now only applies to control-plane-only clusters; container-backed clusters whose endpoint can't be reached surface `DatabaseUnavailableException` (HTTP 504, the canonical AWS error code). Real SQL errors (lock-wait timeout, etc.) still surface as `BadRequestException`. **Non-blocking `CreateDBInstance`**: returns immediately with `DBInstanceStatus="creating"` matching real AWS — readiness finalisation (authenticated-connection probe, MySQL master-user `GRANT`, transition to `available`, Aurora cluster endpoint sync) runs on a daemon thread with the request's contextvars snapshot. No wall-clock timeout — the daemon tracks backing-container liveness instead, so the instance stays `creating` while the container is up and flips to `failed` if it exits before becoming reachable. Image-side password mismatches log at WARNING level with a remediation hint. **Standard AWS ECS Docker labels on `RunTask` containers**: every container MiniStack spawns for an ECS task now carries the five canonical `com.amazonaws.ecs.*` labels real ECS sets (cluster ARN, container-name, task-arn, task-definition-family, task-definition-version), matching the AWS ECS container metadata file spec. Host-side log shippers, monitoring agents, and `docker ps --filter "label=…"` queries identify MiniStack-spawned containers the same way they would against real ECS. - [v1.3.43 — May 18, 2026](https://ministack.org/blog/changelog-v1-3-43.html): Three ships. **AWS IoT Core (Phase 1)** is a new service covering the control plane (Things / ThingTypes / ThingGroups, certificates issued by an in-process Local CA, IoT Policies with versioning + attach/detach, `DescribeEndpoint`) and a WebSocket-only MQTT 3.1.1 data plane multiplexed on the gateway port (HTTP `iot-data Publish` at `POST /topics/{topic}` with QoS 0/1 + retain; clients connect over `mqtt` Sec-WebSocket-Protocol). Persistent sessions (`cleanSession=0`), QoS 1 with in-flight tracking and DUP-flag retransmits, Last Will and Testament on ungraceful disconnect, duplicate-client-id force-disconnect, retained-message delivery on subscribe — all per the MQTT 3.1.1 spec. Multi-tenancy is enforced by account-prefixed topics at the bridge layer: the account ID is resolved from the SigV4 credential at the WebSocket upgrade and every PUBLISH/SUBSCRIBE topic is prefixed before it hits the in-process pub/sub registry, so two accounts publishing to the same topic name never see each other's traffic. Local CA root certificate exposed at `GET /_ministack/iot/ca.pem`; CA + issued certs + broker state (retained messages, persistent sessions) persist across restarts when `PERSIST_STATE=1`. Requires the `cryptography` package (declared in `[full]`); slim image users hit a clean `RuntimeError` on first IoT call. Deferred to later phases: Device Shadows, mTLS on 8883, retained-message queries, Rules Engine, Jobs, Fleet Provisioning. IoT policies are stored but not enforced on the data plane. Plain TCP 1883 is intentionally not exposed (real AWS IoT requires TLS or SigV4 on every connection). **Athena ↔ Glue catalog integration + S3 result persistence**: `StartQueryExecution` now resolves `database.table` references against Glue's `GetTable` to find the underlying S3 location, so queries against Glue-managed tables work without hand-written `read_csv('s3://...')` paths. Completed query results write to the configured `OutputLocation` as `.csv` plus a `.csv.metadata` companion (column names + Athena-mapped types), and the CSV file begins with the column-name header row (matching real Athena's output format). DuckDB execution is offloaded to a worker thread via `asyncio.to_thread`, so multiple concurrent Athena queries run on parallel threads while the event loop stays free. **EventBridge rule targets pointing at Step Functions state machines** — targets with `arn:aws:states:::stateMachine:` previously fell through to the "unsupported target type" warning and silently dropped events; the dispatcher now routes them through `stepfunctions._start_execution`, which runs on a daemon thread with a `contextvars.copy_context()` snapshot so the request's account context is preserved. The transformed payload (post `Input` / `InputPath` / `InputTransformer`) is passed verbatim as the execution input. - [v1.3.42 — May 16, 2026](https://ministack.org/blog/changelog-v1-3-42.html): Four ships. **MWAA** (Managed Workflows for Apache Airflow) emulates the REST control plane and spawns real `apache/airflow:` containers in standalone mode on the same Docker network as MiniStack — both Airflow 2.x and 3.x are supported. `CreateEnvironment` provisions the container, syncs DAGs from the configured `SourceBucketArn` + `DagS3Path` into `/opt/airflow/dags/` once it reaches AVAILABLE, and forces an aggressive scan interval so DAGs become visible within seconds. `CreateWebLoginToken` and `CreateCliToken` return the correct field names per the boto3 MWAA model (`WebToken` / `CliToken`). `InvokeRestApi` proxies straight through to Airflow's REST API — `/api/v2/` for v3 (open via Simple Auth Manager with `ALL_ADMINS=true`), `/api/v1/` for v2 (Basic auth using the standalone-generated admin password captured from the container after boot). Full lifecycle: `GetEnvironment`, `UpdateEnvironment`, `ListEnvironments`, `DeleteEnvironment` (stops + removes the container, releases the host port so long-running stacks don't leak ports). The boto3 host prefix (`api.airflow.`) is correctly bypassed by the S3 vhost extractor so requests reach the MWAA handler instead of being misread as bucket names. End-to-end verified with real DAGs visible via `InvokeRestApi GET /dags` on both `apache/airflow:2.10.4` and `apache/airflow:3.0.6`. **CloudWatch alarm actions** now publish to SNS on every `OK` ↔ `ALARM` ↔ `INSUFFICIENT_DATA` state transition. `AlarmActions` / `OKActions` / `InsufficientDataActions` lists are dispatched per the matching destination state, SNS topic ARNs receive the AWS-shaped JSON payload (`AlarmName`, `NewStateValue`, `NewStateReason`, `StateChangeTime`, `Region`, `OldStateValue`, `Trigger` sub-object with metric/threshold/comparison). Fires on both `SetAlarmState` (manual flip) and the auto-evaluation path triggered by `PutMetricData`; `ActionsEnabled=False` (or `DisableAlarmActions`) suppresses dispatch. Closes the largest single inter-service integration gap — alerting-driven test scenarios now work end-to-end against MiniStack. **Lambda → CloudWatch metrics**: every invocation now publishes the four canonical `AWS/Lambda` metrics dimensioned by `FunctionName` — `Invocations` (Count, 1 per call), `Errors` (Count, 1 on handled/unhandled failure), `Duration` (Milliseconds, wall-clock around the worker call), `Throttles` (Count, 1 when reserved-concurrency rejected the call). Recorded for both `RequestResponse` and `Event` invocation types and queryable via standard `GetMetricStatistics`. Combined with the alarm-action fix, end-to-end Lambda observability tests (invoke → error → alarm transitions to ALARM → SNS notification arrives) work in one local run. **CloudFormation `AWS::ApiGateway::Account`** is now a supported resource type — the singleton CFN resource that stores `CloudWatchRoleArn` for the API Gateway account. Previously failed with `Unsupported resource type`, which blocked any CDK stack using `new RestApi({ cloudWatchRole: true })` — a very common pattern. The handler writes the role ARN into the same store the runtime `UpdateAccount` / `GetAccount` API reads from, so the value round-trips end-to-end. - [v1.3.41 — May 16, 2026](https://ministack.org/blog/changelog-v1-3-41.html): KMS `Decrypt` error code parity. When the caller omitted `KeyId` and the `CiphertextBlob` was too short or otherwise unparseable, MiniStack returned `NotFoundException` ("Unable to find the key for decryption"); real AWS returns `InvalidCiphertextException` in that case because the request can't even reach key lookup. Wrapper libraries that catch encryption faults separately from key-lookup faults (retry on `NotFoundException`, surface `InvalidCiphertextException` immediately) now see the same code locally as in production. The `KeyId`-given branch is unchanged — explicit-key-not-found still returns `NotFoundException`, also matching AWS. Encrypt/Decrypt round-trips, asymmetric key paths (`RSAES_OAEP_SHA_256` etc.), and every other KMS operation are untouched. - [v1.3.40 — May 15, 2026](https://ministack.org/blog/changelog-v1-3-40.html): Cognito's invitation and verification mail now flows through MiniStack's in-process SES emulator: `AdminCreateUser`, `SignUp`, `ResendConfirmationCode` (previously missing — shipped alongside), `ForgotPassword`, and `AdminResetUserPassword` hand their welcome / temporary-password / verification messages to SES, so tests observe them at `/_ministack/ses/messages` and they relay via SMTP when `SMTP_HOST` is set. `MessageAction=SUPPRESS` skips, `RESEND` re-sends, `DesiredDeliveryMediums=["SMS"]` excludes email, `{username}` / `{####}` placeholders expand in `InviteMessageTemplate` and `VerificationMessageTemplate`, sender resolves to `EmailConfiguration.From` falling back to the AWS-canonical `no-reply@verificationemail.com` (overridable via `COGNITO_DEFAULT_FROM`); set `COGNITO_EMAIL_ENABLED=false` to short-circuit globally. Step Functions JSONata gains state-level `Assign` + an execution-scoped variable store; values bound on one state can be referenced as `$name` (with dotted-path access like `$user.email`) in any later JSONata expression. Pass `$states.result` resolves to the computed Output; Task `$states.result` to the raw API result; Catch handlers expose `$states.errorOutput`. Undefined refs surface as `States.QueryEvaluationError`, matching AWS's documented error code for JSONata evaluation failures. Cognito sign-in honors `AliasAttributes` and `UsernameAttributes`: users can sign in by `email` / `phone_number` / `preferred_username` and the lookup resolves correctly. Email and phone aliases require the corresponding `_verified` attribute to equal `"true"` (matches AWS); `preferred_username` has no verification gate. SQS `RedrivePolicy` is validated at `CreateQueue` / `SetQueueAttributes` — a parseable JSON object with a non-empty `deadLetterTargetArn` and a numeric `maxReceiveCount` between 1 and 1000. Malformed values return 400 `InvalidAttributeValue` instead of silently corrupting the queue and crashing `ReceiveMessage` with an `'str' object has no attribute 'get'` `InternalError`. DynamoDB `SET v = (if_not_exists(v, :d) - :amt)` now subtracts correctly — the outer parens used to pin every token at depth > 0, so the top-level operator scan never saw the `-` and silently dropped the arithmetic. S3 → Lambda notifications fire for non-boto3 SDK clients: the notification XML parser now accepts both `` (botocore's wire form) and `` (AWS SDK for Java v2, Go SDK, Terraform, hand-crafted XML). - [v1.3.39 — May 15, 2026](https://ministack.org/blog/changelog-v1-3-39.html): Step Functions JSONata is now wired across Task, Pass, and Choice states with a real operator surface. Task states with `QueryLanguage: "JSONata"` evaluate `Arguments` before dispatching `aws-sdk` integrations (previously bypassed local payload construction and called services with empty payloads) and evaluate success/Catch `Output` against `$states.input`, `$states.result`, `$states.errorOutput`. Pass states evaluate `Output` (was silently ignored, passing input through unchanged). Choice states evaluate per-branch `Condition` and per-branch `Output` (was always treating `{% ... %}` conditions as falsy, falling through to `Default`). The evaluator gained the operators real workflows actually use: comparison (`= != < <= > >=`), arithmetic (`+ - * / %`), string concat (`&`), boolean (`and or in`), unary minus, paren grouping, and `$count` / `$length` / `$not` / `$string` / `$number` functions. Left-associative parsing throughout. Step Functions executions no longer stall at `ExecutionStarted` under non-default 12-digit account IDs — `_executions` is an `AccountScopedDict` keyed by a `ContextVar`, and `threading.Thread` doesn't propagate contextvars, so the background worker was looking up the execution under the default account and silently returning. Fixed with `contextvars.copy_context().run` on each thread target, with per-thread snapshots at the Parallel and Map spawn sites (a single `Context` cannot be entered by two threads concurrently). Cognito OIDC federation completes: `/oauth2/idpresponse` exchanges the IdP's `code` against its `token_url`, decodes the `id_token` (no signature verification, same posture as SAML), applies `AttributeMapping`, provisions the `{provider}_{sub}` federated user, and 302s back to the app with a MiniStack-issued auth code (SAML federation continues to use `/saml2/idpresponse` unchanged). Node.js Lambda handlers can now `require('@aws-sdk/client-*')` the same way real AWS-managed runtimes do: `@aws-sdk/client-lambda` gets a dedicated REST stub; 28 `awsJson1.x` services (SSM, SFN, Logs, SecretsManager, EventBridge, Kinesis, ECS, DynamoDB, SQS, Glue, Athena, Firehose, Cognito IDP/Identity, EMR, ECR, ACM, WAFv2/WAF/WAF-Regional, Organizations, KMS, CodeBuild, Transfer, ServiceDiscovery, Resource Groups Tagging, CloudTrail) resolve via a generic `X-Amz-Target` Proxy stub; errors expose `err.name` for the v3 catch-by-name convention; HTTPS→HTTP localhost downgrade extended to CDK Provider Framework's `cfn-response.js` PUT. Real packages (e.g. via a Lambda Layer) still take precedence. - [v1.3.38 — May 13, 2026](https://ministack.org/blog/changelog-v1-3-38.html): Five new CloudFormation provisioners closing the long-standing gaps for HTTPS, ALB, RDS, and CDK Step Functions stacks. `AWS::CertificateManager::Certificate` provisions a Certificate matching `RequestCertificate` shape (Ref returns ARN; honours DomainName, SubjectAlternativeNames, ValidationMethod, Tags, KeyAlgorithm, CertificateTransparencyLoggingPreference). `AWS::ElasticLoadBalancingV2::TargetGroup` and `AWS::ElasticLoadBalancingV2::ListenerRule` complete the ALB CFN story (LoadBalancer and Listener already existed but had nothing to forward to); ListenerRule conditions accept both the flat `{Field, Values}` shape and the per-field nested form (`PathPatternConfig.Values`, `HostHeaderConfig.Values`, `HttpHeaderConfig`, `HttpRequestMethodConfig`, `QueryStringConfig`, `SourceIpConfig`); actions support `forward`/`redirect`/`fixed-response`. `AWS::RDS::DBInstance` provisions standalone DB instances and Aurora cluster members, metadata-only like the existing `AWS::RDS::DBCluster` handler; Aurora cluster members inherit master credentials from the cluster automatically; `Fn::GetAtt` returns `Endpoint.Address`, `Endpoint.Port`, `DbiResourceId`, `DBInstanceArn`. `AWS::StepFunctions::StateMachine` now accepts the inline `Definition` object and `DefinitionS3Location` (fetched from the in-memory S3 service), and applies `DefinitionSubstitutions` (`${KEY}` placeholders) — so CDK's `DefinitionBody.fromFile()` stops producing `InvalidDefinition: StartAt state 'None' not found`. ECS task IAM role credentials endpoint at `GET /v2/credentials/` returns the AWS-strict 5-field 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 AWS SDKs running inside an emulated ECS task use MiniStack end-to-end with no client config. ECS `connectivityAt` and `stoppingAt` now wire as JSON numbers (Go AWS SDK v2 strict timestamp parsing). CFN `AWS::ECS::TaskDefinition` populates `registeredAt`, `registeredBy`, and `compatibilities`. - [v1.3.37 — May 12, 2026](https://ministack.org/blog/changelog-v1-3-37.html): CloudFormation `AWS::ApiGateway::Authorizer` (TOKEN / REQUEST / COGNITO_USER_POOLS) now provisions against the existing apigateway_v1 store instead of failing with `Unsupported resource type`; maps `Name`, `Type`, `AuthorizerUri`, `AuthorizerCredentials`, `IdentitySource`, `IdentityValidationExpression`, `AuthorizerResultTtlInSeconds`, `ProviderARNs`, `RestApiId`. SQS `AddPermission` / `RemovePermission` wire through to the queue's `Policy` attribute in AWS canonical shape (bare 12-digit account IDs in `Principal.AWS`, lowercase `sqs:` action namespace, `/SQSDefaultPolicy` Id); duplicate `Label` rejected with `InvalidParameterValue`; RemovePermission idempotent. RDS `DescribePendingMaintenanceActions` accepts the call and returns an empty list (unblocks brownfield state-capture tooling). SQS `SendMessage` validates body byte length against the queue's `MaximumMessageSize` attribute and returns `InvalidParameterValue` (400) on oversize. SNS `Publish` and `PublishBatch` enforce the 256 KiB Message+MessageAttributes limit (262144 bytes); Publish returns `InvalidParameter` (400); PublishBatch surfaces each oversized entry as a per-entry failure. EventBridge SQS targets stamp `SqsParameters.MessageGroupId` on FIFO queues (was dropped at dispatch); a content-based `MessageDeduplicationId` and `fifo_seq` are also stamped. SQS `DeleteQueue` raises 400 `QueueDoesNotExist` (awsQueryCompatible `AWS.SimpleQueueService.NonExistentQueue`) for missing queues (was silently 200). S3 `UploadPartCopy` validates `x-amz-copy-source-range` and returns 400 `InvalidArgument` for malformed/reversed/out-of-bounds inputs (was 500/silent 200); out-of-bounds includes object size in the message. S3 `_parse_bucket_key` strips absolute-form request targets so AWS SDK for .NET v4's HTTP/1.1 `PUT http://ministack:4566/bucket/key` no longer hits `NoSuchBucket: http:`. - [v1.3.36 — May 11, 2026](https://ministack.org/blog/changelog-v1-3-36.html): IAM AWS-managed policies (`arn:aws:iam::aws:policy/`) now resolve from any session account, pre-seeded with 20 of the most commonly referenced canonical documents (AdministratorAccess, PowerUserAccess, ReadOnlyAccess, SecurityAudit, AWSLambdaBasicExecutionRole, AWSLambdaVPCAccessExecutionRole, AmazonS3FullAccess/ReadOnlyAccess, AmazonEC2FullAccess/ReadOnlyAccess, AmazonSSMManagedInstanceCore, AmazonDynamoDBFullAccess, AmazonSQSFullAccess, AmazonSNSFullAccess, AmazonECSTaskExecutionRolePolicy, CloudWatchAgentServerPolicy, CloudWatchLogsFullAccess, AWSCloudFormationFullAccess, IAMFullAccess, IAMReadOnlyAccess) carrying their canonical AWS documents verbatim. Unknown AWS-managed ARNs return `NoSuchEntity` by default so typos like `AdminstratorAccess` surface locally the way they do against real AWS; opt in to permissive autovivify with `MINISTACK_AUTOCREATE_AWS_MANAGED=1`. `AttachmentCount` is tracked per-(session-account, arn) via an account-scoped sidecar so `GetPolicy` reflects the calling account's own attachments, matching real AWS. `ListPolicies` respects `Scope=All`/`AWS`/`Local`; mutation operations on AWS-managed ARNs return `AccessDenied` / `InvalidInput`. Cost and Usage Reports service ships with the full 7-operation surface (`PutReportDefinition`, `DescribeReportDefinitions`, `ModifyReportDefinition`, `DeleteReportDefinition`, `TagResource`, `UntagResource`, `ListTagsForResource`); report definitions persist but report file generation is intentionally not emulated — targets IaC validation for stacks that include `aws_cur_report_definition`. Lambda `ruby4.0` runtime wired to AWS's official `public.ecr.aws/lambda/ruby:4.0` base image (tracks botocore 1.42.94). RDS `DescribeDBClusters` serialization corrected for `DatabaseName` (now elided when unset instead of emitted as `""`), `NetworkType` (now defaults to `IPV4` instead of being absent), and `EngineLifecycleSupport` (now defaults to `open-source-rds-extended-support` instead of being absent). RDS `DescribeDBClusterParameters` emits the `` element so botocore materializes `Parameters[].Source` correctly. Two warm-boot persistence gaps closed: CUR module never called `load_state("cur")` at import (state was written on shutdown but never read on restart); IAM `_aws_managed_attachment_counts` sidecar was missing from `get_state` / `restore_state`. - [v1.3.35 — May 11, 2026](https://ministack.org/blog/changelog-v1-3-35.html): EKS `CreateCluster` works again — the k3s server container is now launched with `privileged=True`. The previous granular `cap_add` list could not grant the `/sys/fs/cgroup` remount k3s server mode requires, so containers exited on boot with `failed to evacuate root cgroup: mkdir /sys/fs/cgroup/init: read-only file system`. The cap_add list is retained as defence-in-depth; the privileged trade-off is documented in the EKS README section. SNS FIFO topic → standard SQS queue subscriptions now succeed; the stale validation that returned `InvalidParameterException: Topic with FIFO requires a subscription to a FIFO SQS Queue` is removed, matching AWS behaviour since 2023-09-14 (consumers of a standard queue subscribed to a FIFO topic may receive messages out of order and more than once). RDS `CreateDBInstance` now honours caller-supplied `PreferredMaintenanceWindow` instead of hardcoding `sun:05:00-sun:06:00` — `ModifyDBInstance` and cluster-level windows already worked, so the divergence was per-instance on create only. - [v1.3.34 — May 11, 2026](https://ministack.org/blog/changelog-v1-3-34.html): ECR now serves the Docker Registry HTTP API V2 protocol on the same gateway as the AWS API, matching real ECR. `docker push` and `docker pull` work end-to-end: `/v2/` ping, `/v2/_catalog`, chunked and single-shot blob uploads (`POST` / `PATCH` / `PUT /v2//blobs/uploads/[]`), cross-repo blob mount, blob `HEAD`/`GET`/`DELETE`, manifest `PUT`/`GET`/`HEAD`/`DELETE` by tag or digest, and `/tags/list`. Layer and manifest bytes persist under `PERSIST_STATE=1`; in-flight uploads are intentionally ephemeral. The bundled routing fix matches only registry-shaped paths (`/blobs/`, `/manifests/`, `/tags/list`) so API Gateway v2, AppSync Events, and SES v2 are untouched. CloudFormation `Custom::*` and `AWS::CloudFormation::CustomResource` run the full Create / Update / Delete lifecycle through a local `/_ministack/cfn-response/{token}` intercept; the provisioner runs in `asyncio.to_thread` so the event loop stays free for the Lambda's `PUT` callback — required for CDK `cr.Provider`-backed Lambdas. Cognito OAuth2 `id_token` now echoes the client-supplied `nonce` per OIDC Core 1.0 §3.1.3.7; strict OIDC libraries (`oidc-client-ts`, `react-oidc-context`, Auth0 / Microsoft client SDKs) silently discard tokens missing an expected nonce. - [v1.3.33 — May 9, 2026](https://ministack.org/blog/changelog-v1-3-33.html): CloudFormation `AWS::DynamoDB::GlobalTable` covers the schema CDK `TableV2` emits. Honours `KeySchema`, `AttributeDefinitions`, `BillingMode`, `StreamSpecification`, `GlobalSecondaryIndexes`, `LocalSecondaryIndexes`, `SSESpecification`, `TimeToLiveSpecification`, `TableName`. For PROVISIONED billing, `WriteProvisionedThroughputSettings.WriteCapacityAutoScalingSettings.MinCapacity` and `ReadProvisionedThroughputSettings.ReadCapacityAutoScalingSettings.MinCapacity` are translated to the engine's static `ProvisionedThroughput.{Write,Read}CapacityUnits` (single-process emulator doesn't simulate auto-scaling, so the floor becomes the constant). `Replicas` (required by CFN) and the other multi-region / AWS-side auto-scaling properties (`MultiRegionConsistency`, `GlobalTableWitnesses`, `GlobalTableSourceArn`, `WarmThroughput`, `ReadOnDemandThroughputSettings`, `WriteOnDemandThroughputSettings`) are accepted and ignored. Stacks that mix `AWS::DynamoDB::Table` and `AWS::DynamoDB::GlobalTable` deploy unmodified. - [v1.3.32 — May 9, 2026](https://ministack.org/blog/changelog-v1-3-32.html): Cognito OIDC autodiscovery now returns reachable URLs at the MiniStack gateway instead of unreachable `cognito-idp..amazonaws.com` URLs; `response_types_supported` advertises both `code` and `token`. Cognito OAuth2 / OIDC endpoints (`/oauth2/authorize`, `/oauth2/token`, `/oauth2/userInfo`, `/logout`, `/.well-known/*`) now send wildcard `Access-Control-Allow-Origin: *` so Amplify, `oidc-client-ts`, and `react-oidc-context` work cross-origin from a browser. EC2 VPN Connection support (`CreateVpnConnection`, `DescribeVpnConnections`, `DeleteVpnConnection`, `CreateVpnConnectionRoute`, `DeleteVpnConnectionRoute`) plus `DescribeRouteTables` now emits `propagatingVgwSet` set by `EnableVgwRoutePropagation`. EC2 `RunInstances` honours `--private-ip-address` and `--iam-instance-profile` (both were silently dropped; default IPs were malformed due to a missing dot separator in the prefix). DynamoDB GSI Query pagination now orders by `(INDEX_SORT, BASE_PK, BASE_SK)` so `ExclusiveStartKey` doesn't drop or cycle items when GSI sort keys collide — common with single-table designs / ElectroDB collections. - [v1.3.31 — May 7, 2026](https://ministack.org/blog/changelog-v1-3-31.html): Lambda multi-account isolation — function workers spawned under non-default accounts now receive `AWS_ACCESS_KEY_ID` derived from the function ARN instead of the host process env var, so `STS GetCallerIdentity` and internal SDK calls inside the handler resolve to the correct account; warm-worker pool key is now `{account}:{function}:{qualifier}`. EC2 `RunInstances` and `DescribeInstances` emit a default root `BlockDeviceMappings` entry (auto-attached `/dev/xvda` EBS volume, gp3, 8 GiB, `DeleteOnTermination: true`) so Cloud Custodian, AWS Config, and any policy tool that classifies instances by BDM works against MiniStack. S3 `GetObject` by `VersionId` emits RFC 7231 HTTP-date `Last-Modified` (instead of internal ISO-8601), fixing AWS SDK for JavaScript v3's strict header parser. EC2 AWS-managed prefix lists (`DescribePrefixLists`, `DescribeManagedPrefixLists`, `GetManagedPrefixListEntries`) return deterministic CIDRs for `s3`, `dynamodb`, `s3express`, `vpc-lattice`, `route53-healthchecks`, `ec2-instance-connect`, `cloudfront`, `groundstation` (IPv4 in CGNAT `100.64.0.0/10`, IPv6 in `64:ff9b:1::/48`). - [v1.3.30 — May 6, 2026](https://ministack.org/blog/changelog-v1-3-30.html): Step Functions REST-JSON `aws-sdk` integrations (e.g. `aws-sdk:rdsdata:executeStatement`) now expose output keys with the same PascalCase convention used by the query and REST-XML dispatchers (`Records`, `NumberOfRecordsUpdated`, `GeneratedFields`, `StringValue`) instead of raw camelCase wire keys, so `ResultSelector` paths like `$.Records` resolve correctly. - [v1.3.29 — May 6, 2026](https://ministack.org/blog/changelog-v1-3-29.html): EC2 `DescribeVpcEndpointServices` returns the standard catalog of 2 Gateway services (`s3`, `dynamodb`) and 17 Interface PrivateLink services with region-templated DNS names and stable per-service IDs; `ServiceNames`, `service-name`, `service-type` filters supported. DynamoDB legacy `AttributeUpdates` (pre-expression `UpdateItem` parameter with `PUT` / `DELETE` / `ADD` actions; mutually exclusive with `UpdateExpression`; .NET SDK upserts now round-trip non-key fields). Step Functions `aws-sdk:ec2` security-group dispatch uses EC2-shaped numbered lists (`Filter.1.Value.1`) instead of `member.N`, maps `Description` → `GroupDescription`, and exposes `SecurityGroups` rather than raw `SecurityGroupInfo`. Step Functions `aws-sdk:s3` REST-XML dispatcher covers `ListBuckets`, `CreateBucket`, `DeleteBucket`, `HeadBucket`, `GetBucketVersioning`, `ListObjectsV2`, `ListObjects`, `HeadObject`, `CopyObject`, `DeleteObject`, `GetObjectTagging`, `PutObjectTagging`. SQS `ReceiveMessage` honours the modern `MessageSystemAttributeNames` field (AWS SDK v2 / Java / Kotlin). CFN `AWS::SNS::Subscription` honours `RawMessageDelivery`. - [v1.3.28 — May 5, 2026](https://ministack.org/blog/changelog-v1-3-28.html): ECS Task Metadata V4 — every container started by `RunTask` gets `ECS_CONTAINER_METADATA_URI_V4` injected; gateway serves `/v4/` (current container), `/v4//task` (sibling `Containers` array), and `/stats` + `/task/stats` stub. `RunTask` also translates `privileged`, `linuxParameters.capabilities.add`, `pidMode: host`, and `volumes` + `mountPoints` into Docker bind mounts. DynamoDB legacy `Expected` (PutItem / UpdateItem / DeleteItem) and `KeyConditions` (Query) now evaluated correctly with all 13 comparison operators (`EQ`, `NE`, `LE`, `LT`, `GE`, `GT`, `NOT_NULL`, `NULL`, `CONTAINS`, `NOT_CONTAINS`, `BEGINS_WITH`, `IN`, `BETWEEN`) and type-aware numeric compare. `TransactWriteItems` `CancellationReasons` now reports `ConditionalCheckFailed` for every failing item, not just the first. - [v1.3.27 — May 4, 2026](https://ministack.org/blog/changelog-v1-3-27.html): AWS CloudTrail (in-memory audit log + control plane; opt-in via `CLOUDTRAIL_RECORDING=1`; ring-buffered per account; all 8 `LookupAttributes`; `IsLogging` flips on `Start`/`StopLogging`; `ListTrails` and `UpdateTrail` handlers) and AWS Resource Groups (19 of 23 spec ops covering group CRUD, resource queries, configuration, membership, tagging, account settings; tag-sync ops omitted — not exposed by AWS CLI / Terraform). Plus AWS-spec audit fixes: API Gateway v1 `GetUsagePlanKey` per-key handler, HTTP_PROXY `{paramName}` substitution from `integration.request.path.X` mappings + query-string forwarding, `UpdateModel` (`PATCH /restapis/{id}/models/{name}`); Transfer Family `LOGICAL` root home-directory mappings (`Entry="/"`); STS `Credentials.Expiration` is int epoch in the JSON path; DynamoDB `ConditionalCheckFailedException` populates `Item` on `ReturnValuesOnConditionCheckFailure="ALL_OLD"` (PutItem / UpdateItem / DeleteItem / TransactWriteItems); CloudFormation `AWS::S3::Bucket` preserves physical resource id on stack update; CloudFormation `AWS::Lambda::Function` returns real `CodeSize` and `CodeSha256`. - [v1.3.26 — May 4, 2026](https://ministack.org/blog/changelog-v1-3-26.html): S3 Files (`s3files-2025-05-05`) routes and shapes verified against `botocore/data/s3files/2025-05-05/service-2.json` — `CreateFileSystem` is `PUT /file-systems` (not `POST`), camelCase request/response (`bucket`, `roleArn`, `fileSystemId`, `creationTime` int epoch), tagging at `/resource-tags/{resourceId}`, `PutSynchronizationConfiguration` optimistic concurrency via `latestVersionNumber`, AWS-standard error codes; resolves the reported `Unknown S3 Files route: PUT /file-systems` failure from the AWS CLI / Terraform. OpenSearch non-VPC domains stop emitting empty `VPCOptions` alongside `Endpoint` (Terraform AWS provider was misclassifying them as VPC-backed); VPC-shaped domains return `Endpoints["vpc"]` instead of `Endpoint`. CloudFormation `AWS::CloudFront::KeyValueStore` provisioning with in-place `Comment` updates via the new per-type CFN engine `update` handler; native `CreateKeyValueStore` accepts the optional `ImportSource` (`SourceType` + `SourceARN`). - [v1.3.25 — May 3, 2026](https://ministack.org/blog/changelog-v1-3-25.html): AWS AppSync Events API (HTTP publish + WebSocket subscribe/publish on the `aws-appsync-event-ws` subprotocol with full Lambda authorizer payload parity); CloudFront KeyValueStore management plane (under `cloudfront`) and the separate `cloudfront-keyvaluestore` data-plane SDK service (atomic `UpdateKeys`, opaque `NextToken`, `MaxResults` capped at 50, `DeleteKey` 404 on missing); EventBridge `cron()` full AWS parity with `L`, `LW`, `W`, `L`, `#` operators and DoM/DoW mutual-exclusion enforced at `PutRule`; 8 EventBridge AWS-spec divergences fixed (`CreatedBy`/`ManagedBy` on `DescribeRule`, `PutEvents` 10-entry cap, int-epoch `Time`, content-filter `exists:false` matches absent keys, `ListRules` pagination, opaque `NextToken` on `ListRuleNamesByTarget`, omitted empty `Policy`, `EventSource` state in `PENDING`/`ACTIVE`/`DELETED` enum). - [v1.3.24 — May 2, 2026](https://ministack.org/blog/changelog-v1-3-24.html): bug-fix release. `x-amzn-errortype` response header on every JSON-protocol error (Java SDK v2 / Go SDK v2 / Rust SDK now see the actual error code, not "unknown error type"). AppConfig 404 bodies include `__type` alongside `Code`/`Message`. Three previously-stateless services (`account`, `waf-classic`, `resourcegroupstaggingapi`) expose a no-op `reset()`. - [v1.3.23 — May 1, 2026](https://ministack.org/blog/changelog-v1-3-23.html): OpenSearch Service with full management plane plus optional real `opensearchproject/opensearch` cluster + Dashboards sidecar; EventBridge `rate()` rules now auto-fire on AWS-anchored `CreationTime` countdown; AWS Organizations, Account, Batch, WAF v1 stubs; EC2 `DescribeRegions`; Lambda `FileSystemConfigs` accepts S3 ARNs; APIGW v1 TLS-1.3 enum; idle RAM cut ~30% via `PYTHONOPTIMIZE=2` + `MALLOC_ARENA_MAX=2`. - [v1.3.22 — April 30, 2026](https://ministack.org/blog/changelog-v1-3-22.html): EC2 IMDS v1 + v2 (SDK credential chains resolve via `AWS_EC2_METADATA_SERVICE_ENDPOINT`); Cognito PreTokenGeneration Lambda invocation (V2_0 + V1_0 response handling); S3 PostObject for browser-based form uploads with `content-length-range` enforcement; full S3 `StorageClass` round-trip; per-script init-script env vars. - [v1.3.21 — April 29, 2026](https://ministack.org/blog/changelog-v1-3-21.html): real Redis cluster mode for ElastiCache (`redis-cli --cluster create`, real `CLUSTER SLOTS` / `MOVED`); 16-site ElastiCache `` → AWS-spec element-name fix that unbreaks aws-sdk-go-v2 and Java SDK v2; RDS error-code wire fixes; opaque base64url pagination on ten REST API list ops. - [Full release archive on GitHub](https://github.com/ministackorg/ministack/blob/master/CHANGELOG.md): every release with full attribution. - [Service timeline](https://ministack.org/blog/service-timeline.html): when each AWS service first shipped in MiniStack. ## Source - [GitHub repository](https://github.com/ministackorg/ministack): MIT-licensed source, issues, PRs, full CHANGELOG. - [r/ministack on Reddit](https://www.reddit.com/r/ministack/): community Q&A, release announcements. ## Optional - [Blog index](https://ministack.org/blog/index.html): all guides, release posts, and feature deep-dives in one hub. - [LinkedIn](https://www.linkedin.com/company/ministackorg/): release announcements, ecosystem news. - [Sitemap](https://ministack.org/sitemap.xml): every public URL with `lastmod` timestamps.