Releases: ministackorg/ministack
Releases · ministackorg/ministack
v1.3.28
What's Changed
New Contributors
- @YakirOren made their first contribution in #553
[1.3.28] — 2026-05-05
Added
- ECS Task Metadata V4 — every container started by
RunTasknow getsECS_CONTAINER_METADATA_URI_V4injected, and the gateway serves/v4/<token>,/v4/<token>/task(with siblingContainersarray), and/v4/<token>/stats+/task/stats(stub). Standardcom.amazonaws.ecs.*container labels.RunTaskalso translatesprivileged,linuxParameters.capabilities.add,pidMode: host, andvolumes+mountPointsinto Docker bind mounts. Contributed by @YakirOren.
Fixed
- DynamoDB legacy
Expected(PutItem / UpdateItem / DeleteItem) andKeyConditions(Query) — previously ignored; SDKs and code paths that still use the pre-expression API now work.ScanFilter/QueryFiltercomparison support extended to all 13 legacy operators (EQ,NE,LE,LT,GE,GT,NOT_NULL,NULL,CONTAINS,NOT_CONTAINS,BEGINS_WITH,IN,BETWEEN) with type-aware numeric comparison. Reported by @darkamgine - DynamoDB
TransactWriteItemsmulti-failure reporting — only the first failing item was marked inCancellationReasons; AWS returns aConditionalCheckFailedentry for every failing item in the transaction. Now evaluates all conditions in a first pass and reports each failure. Reported by @anghel93 and @gnjack
v1.3.27
What's Changed
New Contributors
- @stefanmb made their first contribution in #557
- @erick-reis-gran made their first contribution in #560
[1.3.27] — 2026-05-04
Added
- AWS CloudTrail — in-memory audit log + control plane. Recording opt-in via
CLOUDTRAIL_RECORDING=1; per-account ring buffer (CLOUDTRAIL_MAX_EVENTS=10000).LookupEventssupports all 8 AWSLookupAttributes. Control plane:CreateTrail,DeleteTrail,GetTrail,DescribeTrails,ListTrails,UpdateTrail,GetTrailStatus,StartLogging/StopLoggingwith realIsLoggingstate,Put/GetEventSelectors,AddTags/ListTags/RemoveTags. Contributed by @AdigaAkhil. - AWS Resource Groups (
resource-groups, 2017-11-27) — 19 of 23 spec operations: group CRUD, resource queries, configuration, membership, tagging, account settings. Tag-sync ops omitted (not exposed by AWS CLI / Terraform). Requested by @staranto.
Fixed
- API Gateway v1
GetUsagePlanKey—GET /usageplans/{planId}/keys/{keyId}handler was missing; per-key path fell through to 404. Terraform'sGetUsagePlanKeyrefresh afterCreateUsagePlanKeyaborted everyaws_api_gateway_usage_plan_keyapply. Contributed by @marcin-nowak-scl. - API Gateway v1 HTTP_PROXY path-param substitution + query-string forwarding —
{paramName}placeholders in integrationuriwere forwarded literally; the inbound execute path was appended to the integration URI; query string was dropped. Now substitutes fromintegration.request.path.X = method.request.path.Xmappings (plus{proxy}for{proxy+}), uses the substituted URI as the upstream URL, and forwards the query string. Contributed by @marcin-nowak-scl. - API Gateway v1
UpdateModel—PATCH /restapis/{id}/models/{name}was missing; Terraformaws_api_gateway_modelupdates 404 - Transfer Family
LOGICALroot home directory mappings —Entry="/"failed to match because the resolver built"//"as the prefix. Contributed by @stefanmb. - CloudTrail router target prefix — was
AmazonCloudTrailService; AWS usesCloudTrail_20131101. Routing still worked via credential scope, but the prefix entry was dead code. - CloudTrail
IsLoggingstate onStop/StartLogging— both were no-ops;GetTrailStatusalways returnedIsLogging: True. Now flips the trail record's state and stamps_StartedAt/_StoppedAt(int epoch). - STS
Credentials.Expirationis int epoch in the JSON path —AssumeRole/AssumeRoleWithWebIdentity/GetSessionTokenreturned a float; Java/Go SDK v2 reject it. backup/eks_epoch()/_now()return int — weretime.time()(float); consumed by record fields likecreatedAt.- DynamoDB
ConditionalCheckFailedExceptionpopulatesItemonReturnValuesOnConditionCheckFailure="ALL_OLD"—PutItem/UpdateItem/DeleteItem/TransactWriteItemsnow return the prior item alongside the error code (and on the failingCancellationReasonfor transactions). Verified against botocore:CancellationReasonandConditionalCheckFailedExceptionshapes both includeItem. Reported by @darkamgine. - CFN
AWS::S3::Bucketpreserves physical id on update — auto-named buckets got a new random name on everyUpdateStack, breaking{Ref}after redeploy. Contributed by @erick-reis-gran. - CFN
AWS::Lambda::Functionreturns realCodeSize/CodeSha256— were hardcoded; now computed from the deployment-package bytes. Contributed by @erick-reis-gran.
v1.3.26
What's Changed
[1.3.26] — 2026-05-04
Added
- CloudFormation
AWS::CloudFront::KeyValueStore— Create / Update (Comment in place) / Delete; exposesArn,Id,StatusviaFn::GetAtt. CFN engine now routes previously-provisioned resources through a per-typeupdatehandler when one is defined, falling back to idempotentcreateotherwise. CloudFrontCreateKeyValueStoreaccepts the optionalImportSource(SourceType+SourceARN) and round-trips it on the record.
Fixed
- OpenSearch non-VPC domains omit empty
VPCOptions—CreateDomain/DescribeDomainpreviously returnedVPCOptions: {}alongsideEndpoint, causing Terraform AWS provider reads to classify the domain as VPC-backed and fail withOpenSearch Domain in VPC expected to have null Endpoint value. Non-VPC domains now omitVPCOptions; VPC-shaped domains returnEndpoints["vpc"]instead ofEndpoint. Contributed by @marcin-nowak-scl. - S3 Files routes and shapes match AWS
s3files-2025-05-05—CreateFileSystemisPUT /file-systems(notPOST); request and response bodies use camelCase (bucket,roleArn,fileSystemId,creationTimeint epoch); resource tagging moved to/resource-tags/{resourceId};PutSynchronizationConfigurationenforces optimistic concurrency vialatestVersionNumber; standardValidationException/ResourceNotFoundException/ConflictExceptionerrors withapplication/jsoncontent type. Resolves the reportedUnknown S3 Files route: PUT /file-systemsfailure from the AWS CLI / Terraform. Reported by @tmq107
v1.3.25
What's Changed
[1.3.25] — 2026-05-03
Added
- AppSync Events API — Event API management under
/v2/apis, channel namespaces, API keys via/v1/apis/{apiId}/apikeys, HTTP publish on{apiId}.appsync-api.*, and realtime WebSocket on{apiId}.appsync-realtime-api.*(aws-appsync-event-wssubprotocol). Strict auth viaAPPSYNC_EVENTS_ENFORCE_AUTH=1. Contributed by @marcin-nowak-scl. - CloudFront KeyValueStore — management plane — Create/Describe/List/Update/Delete with ETag concurrency;
KeyValueStoreAssociationsround-tripped through CloudFront Functions. Contributed by @DaviReisVieira. - CloudFront KeyValueStore — data plane — separate
cloudfront-keyvaluestoreservice covering Describe, ListKeys, GetKey, PutKey, DeleteKey, UpdateKeys with ETag concurrency. Requested by @shellscape. Contributed by @DaviReisVieira. - EventBridge
cron()schedule auto-fire — full AWS-spec parity. Zero-dep parser for the 6-field syntax:*,?, ranges, steps, lists, named month/weekday tokens, and theL(last day /<n>Llast weekday-of-month),LW(last weekday),<n>W(nearest weekday), and<n>#<k>(kth weekday-of-month) operators. DoM/DoW mutual-exclusion enforced atPutRule. Contributed by @hiddengearz.
Fixed
- AppSync Events
ChannelNamespaceresponse now includeschannelNamespaceArn— spec member was omitted; Terraform / Java SDK v2 sawnullwhere AWS returns the ARN. - CloudFront KVS data-plane
DescribeKeyValueStoreCreated/LastModified— were hardcoded to0; now parsed from the management-plane timestamp into int epoch seconds. - S3 vhost routing excludes
cloudfront-kvs.*— moved the bypass from a/key-value-stores/path check up to the_NON_S3_VHOST_NAMEShost-name layer. - EventBridge
DescribeRule/ListRulesnow emitCreatedByandManagedBy— spec members were silently dropped from_rule_out. - EventBridge
PutEventsrejects more than 10 entries — AWS spec capsEntriesat 10; ministack accepted any size. - EventBridge event
Timeis int epoch seconds, not float — Java/Go SDK v2 timestamp parsers reject high-precision floats; archive replays now also dispatch the int form. - EventBridge content-filter
[{"exists": false}]matches absent keys — short-circuited to no-match before theexistsbranch was evaluated, so patterns that should fire on missing fields silently dropped. - EventBridge
ListRulespaginates — addedLimit(1-100) and opaqueNextToken; previously returned the full list and SDK paginators looped on the first page. - EventBridge
ListRuleNamesByTargetNextTokenis opaque — was a raw integer offset string. - EventBridge
DescribeEventBus/ListEventBusesomitPolicywhen no policy set — was emitting"", divergent from AWS shape. - EventBridge
DescribeEventSourceState — was hardcodedENABLED; AWS enum isPENDING/ACTIVE/DELETED. Now returnsACTIVE.
v1.3.24
[1.3.24] — 2026-05-02
Fixed
x-amzn-errortypeheader now emitted on every JSON-protocol error response. Real AWS sends the error type in both the body (__type) and thex-amzn-errortypeheader. boto3 falls back to the body, but Java SDK v2, Go SDK v2, and Rust SDK prefer the header — without it they surfaceSdkClientException: unknown error typeinstead of the actual code. Applied centrally inerror_response_jsonand inline in 12 services that build error bodies directly (apigateway v1/v2, opensearch, scheduler, eks, ses, backup, sqs, cloudwatch, dynamodb, tagging).- AppConfig 404 bodies now include
__type. Was{"Code": ..., "Message": ...}; generic JSON error parsers that look for__typesaw an unknown shape. Body now carries both styles. - Three previously-stateless services expose a no-op
reset()(account,waf-classic,resourcegroupstaggingapi) so/_ministack/resetno longer logs a warning per call.
v1.3.23
What's Changed
[1.3.23] — 2026-05-01
Added
- Amazon OpenSearch Service — management plane on
/2021-01-01/*: CreateDomain, DescribeDomain(s), DeleteDomain, ListDomainNames (withEngineTypefilter), UpdateDomainConfig, DescribeDomainConfig (Options/Statuswrapping), DescribeDomainChangeProgress, ListVersions, GetCompatibleVersions, AddTags/ListTags/RemoveTags. Account-scoped state. Default data plane is a stub endpoint; setOPENSEARCH_DATAPLANE=1to spawn one realopensearchproject/opensearchcontainer perCreateDomain(same pattern as ElastiCache/RDS). AddOPENSEARCH_DASHBOARDS=1for an optional per-domainopensearch-dashboardssidecar —DescribeDomain.DashboardEndpointis populated.DeleteDomaintears down spawned containers. Terraformaws_opensearch_domaincompatible. Requested by @marcin-nowak-scl. - EventBridge scheduled rule auto-fire —
rate(N minute|hour|day)rules now fire automatically. A daemon thread (eb-scheduler) ticks every 10 s; the per-rule countdown anchors toCreationTimeso the first fire lands one full interval afterPutRule. Scheduled event payload matches AWS exactly (source: aws.events,detail-type: Scheduled Event,detail: {}, ISO 8601time). Multi-tenant — iterates the rules store directly.cron()expressions are stored but not yet auto-fired (one-timeINFOlog surfaces the gap). Contributed by @hiddengearz. - AWS Organizations — DescribeOrganization, ListRoots, ListAccounts, DescribeAccount, ListOrganizationalUnitsForParent / ListAccountsForParent, CreateOrganizationalUnit / DescribeOrganizationalUnit / DeleteOrganizationalUnit. Single-master-account org auto-initialised on first call; nested OUs carry the new
Pathfield (2026-03 AWS additive change). - AWS Account service — GetAccountInformation, GetContactInformation, ListRegions, GetRegionOptStatus. Returns the new
AccountState: ACTIVEfield (2026-04 AWS additive change). Older boto3 SDKs strip the field; newer ones see it. - AWS Batch — control-plane stub: ComputeEnvironments, JobQueues, JobDefinitions (auto-revisioning), SubmitJob (auto-
SUCCEEDED), DescribeJobs, ListJobs. Account-scoped. - WAF Classic + Regional (v1) — minimal stub so legacy clients (Terraform, old CFN) get clean empty-state responses instead of 405.
List*returns empty arrays,GetChangeToken/GetChangeTokenStatusreturn valid responses,Get*for unknown resources returnsWAFNonexistentItemException. For full WebACL state usewafv2. - EC2 DescribeRegions — returns 31 commercial regions with correct
OptInStatus(opt-in-not-requiredfor legacy us-/eu-/ap-* regions,opted-infor newer ones). SupportsRegionNamesfilter andAllRegionstoggle. - Lambda
FileSystemConfigsaccepts S3 ARNs — the 2026-04 AWS S3-mount addition. Server stores and round-trips whatever ARN format the SDK sends (EFS access points, S3 buckets, future shapes). - EventBridge
LogConfig— additive 2026-03 field onCreateEventBus/UpdateEventBus; persisted, returned onDescribeEventBus. - API Gateway v1
securityPolicyaccepts the new TLS-1.3 enum — allow-listsSecurityPolicy-TLS13-1-2-FIPS-PFS-PQ-2025-09(2026-03 addition) and any future opaque values; default remainsTLS_1_2.
Fixed
- S3
PostObjectaccepts unquotedContent-Dispositionfield names — .NET'sMultipartFormDataContentemitsname=foofor ASCII-clean values per RFC 2183; the parser previously only matched the quoted formname="foo"and droppedkey/success_action_status, causing browser-form uploads from .NET clients to 400. Reported by @mattburton. - EventBridge target dispatch
timefield is ISO 8601 — was a Unix epoch float, AWS specifies a string like2026-05-01T18:08:16Z. BothPutEvents-driven and scheduled-rule-driven dispatches now use the canonical format.
Performance
- Idle RAM% — Dockerfile now sets
PYTHONOPTIMIZE=2andMALLOC_ARENA_MAX=2. Verified zero correctness impact (noassertor__doc__introspection in ministack source); throughput unchanged.
v1.3.22
What's Changed
[1.3.22] — 2026-04-30
Added
- Cognito PreTokenGeneration Lambda trigger —
LambdaConfig.PreTokenGenerationConfig(V2_0) and the legacyLambdaConfig.PreTokenGeneration(V1_0) are now round-tripped throughCreateUserPool/UpdateUserPool/DescribeUserPooland invoked at token-mint time. Before signing an access or id token, ministack synchronously invokes the configured Lambda with the AWS-shaped event (triggerSource,userPoolId,request.userAttributes,request.groupConfiguration,request.scopesfor V2+,callerContext.clientId, etc.) and applies the Lambda'sresponse.claimsAndScopeOverrideDetails.{accessTokenGeneration,idTokenGeneration}(V2_0:claimsToAddOrOverride,claimsToSuppress,scopesToAdd,scopesToSuppress,groupOverrideDetails) — or the legacyresponse.claimsOverrideDetails(V1_0, id token only). Refresh tokens are opaque in AWS and skip the trigger. Lambda errors fail open (token issued without overrides + warning logged); setMINISTACK_COGNITO_PRETOKEN_STRICT=1to fail closed the way real AWS does. Invocation reuses the existing_resolve_name_and_qualifier→_get_func_record_for_qualifier→_execute_functionchain inlambda_svc.py— no new handlers added. Reported by @aahoughton (#533). - S3 PostObject (browser-based form upload) —
POST /<bucket>/withmultipart/form-datais now handled. Honourskey(with${filename}substitution from the file part),Content-Type,x-amz-meta-*,x-amz-storage-class,x-amz-tagging, the object-lock headers,success_action_status(200/201/204; default 204), andsuccess_action_redirect(303 withbucket=&key=&etag=appended). On 201 returns the<PostResponse>XML withLocation/Bucket/Key/ETag. Versioning, persistence, multi-tenancy, S3 event notifications all flow through the same path asPutObject. Thecontent-length-rangepolicy condition is enforced — uploads under the minimum returnEntityTooSmall400 and uploads over the maximum returnEntityTooLarge400 (matches AWS error codes). Other policy conditions and the signature field are accepted but not validated — same lenient stance as ministack's presigned-URL handling. boto3'sgenerate_presigned_postworks end-to-end. Requested by @mattburton (#535). - Init / ready scripts: expose
MINISTACK_INIT_SCRIPT_DIRandMINISTACK_INIT_SCRIPT_PATHto each script — every.sh/.pyrun from/docker-entrypoint-initaws.d[/ready.d](or/etc/localstack/init/{boot,ready}.d) now sees its own directory and absolute path in the environment, so scripts can reference sibling files (aws s3 cp "${MINISTACK_INIT_SCRIPT_DIR}/data.json" s3://bucket/) without hardcoding the mount path or computingdirname "${BASH_SOURCE[0]}". Phase-levelMINISTACK_INIT_BOOT_DIR/MINISTACK_INIT_READY_DIRare also set when those directories exist. Requested by @andreluiznsilva (#520). - EC2 Instance Metadata Service (IMDS) emulator — new
imdsservice responds on the gateway port at/latest/api/token(IMDSv2) and/latest/meta-data/...//latest/dynamic/instance-identity/document. Returns a credentials document under roleministack-instance-roleso SDKs that fall through to the IMDS step of the default credential chain (boto3, aws-sdk-go-v2, AWS SDK Java v2) get a validASIA*session key + token. Both IMDSv1 (token-less) and IMDSv2 (PUT /token → GET withX-aws-ec2-metadata-token) supported; setMINISTACK_IMDS_V2_REQUIRED=1to reject token-less requests, matching AWS hop-limit-1 IMDSv2-only instances. Point SDKs at ministack viaAWS_EC2_METADATA_SERVICE_ENDPOINT=http://localhost:4566(orec2_metadata_service_endpointin~/.aws/config); we don't bind the link-local 169.254.169.254 IP — that's a per-container network-alias concern, not portable from inside ministack. Reported by @bimargulies
Fixed
- S3 PutObject
StorageClasswas dropped on the floor — objects written withStorageClass=GLACIER/INTELLIGENT_TIERING/ etc. came back fromGetObject,HeadObject,ListObjects(V2), andListObjectVersionsasSTANDARD. The header is now stored on the object record and emitted on the wire (header omitted for the defaultSTANDARD, matching AWS). Same propagation throughCopyObject(with optional override viax-amz-storage-class) andCreateMultipartUpload→CompleteMultipartUpload. Unknown storage class values now returnInvalidStorageClass(400). Verified againstbotocore/data/s3/2006-03-01/service-2.json. Reported by @JoeHale (#534).
v1.3.21
What's Changed
[1.3.21] — 2026-04-29
Added
- ElastiCache: real Redis replication groups + opt-in real Redis Cluster mode —
CreateReplicationGroupnow spawns live Redis containers per shard (was a metadata-only stub). BehindELASTICACHE_CLUSTER_MODE_REAL=1+DOCKER_NETWORK,NumNodeGroups=N/ReplicasPerNodeGroup=RprovisionsN × (1+R)cluster-enabled nodes, runsredis-cli --cluster create, and serves realCLUSTER SLOTS/MOVEDredirects.NumNodeGroups=2rejected withInvalidParameterValue(matches AWS: only 1 or ≥3 shards). Account-scoped container names +account_idlabel so accounts can sharerg_id; orphan-container reaper at startup. Reported by @akursar.
Fixed
- ElastiCache list responses wrapped items in
<member>instead of the AWS-spec element name —DescribeCacheClustersand 10 other list-emitting ops emitted<member>where AWS uses the model-declaredlocationName(e.g.<CacheCluster>,<Tag>,<Snapshot>). Strict generated SDKs (aws-sdk-go-v2, Java/Rust v2) parse a<member>-wrapped list as empty; botocore is permissive, so boto3 / CLI users never saw it. 16 sites fixed; the 5 remaining<member>sites (UserList,UserGroupList,UserIdList, etc.) match AWS. Verified against botocore service-2.json. Reported by @jmickey (#530). - RDS error codes carried a stale
Faultsuffix on two not-found shapes —DescribeDBInstances(and 7 other DBInstance ops) emitted<Code>DBInstanceNotFoundFault</Code>while real AWS returns<Code>DBInstanceNotFound</Code>;DescribeDBParameters(and 9 other DBParameterGroup ops) emitted<Code>DBParameterGroupNotFoundFault</Code>while real AWS returns<Code>DBParameterGroupNotFound</Code>. Verified againstbotocore/data/rds/2014-10-31/service-2.json(the wireerror.codediffers from the shape name on ~19 RDS not-found errors — these two were the ones ministack emitted with the wrong wire code). Breaks string-matching consumers like the ACK RDS controller'ssdkFind, which comparesawsErr.ErrorCode() == "DBInstanceNotFound"to detect the not-found branch and reach the create path; with theFaultsuffix the branch never matched and the CR sat atReady=False. Also affectsaws-sdk-go-v2(smithy.APIError.ErrorCode()) and any boto3 caller matching one.response["Error"]["Code"]. Reported by @jmickey. - RDS error responses were missing
<Type>Sender</Type>/<Type>Receiver</Type>— real AWS Query-protocol error envelopes include the fault type alongside<Code>and<Message>. The_errorhelper now emitsSenderfor 4xx andReceiverfor 5xx. Cosmetic for SDKs that read<Code>only, but completes the documented AWS shape. - API Gateway REST API (v1): pagination missing on 10 list operations —
GetRestApis,GetResources,GetDeployments,GetAuthorizers,GetModels,GetApiKeys,GetUsagePlans,GetUsagePlanKeys,GetDomainNames, andGetBasePathMappingsignored the AWS-speclimit(default 25, max 500) +positionquery params and always returned the full list with nopositioncursor. Pagination-aware SDKs that round-trip the cursor (boto3 paginators, AWS CLI--max-items/--starting-token, Java SDK v2) silently received the same first page on every call. The 10 ops now slice perlimit, return an opaque base64url-encodedpositiontoken when more pages remain, and reject malformed tokens withBadRequestException.GetStagesis correctly not paginated — its AWS shape has nolimit/positionfields. - API Gateway REST API (v1)
PutMethodResponseandPutIntegrationResponsereturned HTTP 200 instead of 201. Real AWS returns 201 on resource creation (verified againstbotocore/data/apigateway/2015-07-09/service-2.json); the AWS CLI prints the resource on 201 and is silent on 200, so scripts that branched on stderr would diverge. The remaining v1 Create/Put ops already returned 201.
v1.3.20
What's Changed
New Contributors
- @dcrn made their first contribution in #514
- @marcin-nowak-scl made their first contribution in #515
[1.3.20] — 2026-04-29
Added
- API Gateway (HTTP API and REST): JWT authorizer enforcement, HTTP proxy parameter mapping, and non-blocking proxy/JWKS I/O — HTTP API (
apigateway) and REST API (apigateway_v1) now enforce JWTissuer+audiencevalidation against a JWKS URL (RS256 signatures only — Cognito's standard), apply request parameter mappings forHTTP/HTTP_PROXYintegrations (append/overwrite/removefor headers and querystring, plusoverwrite:path, with$context.authorizer.jwt.claims.*and$stageVariables.*substitution), and offload upstream proxy + JWKS fetches off the event loop so a slow backend can no longer stall unrelated requests. Authorizer issuers of the formhttps://cognito-idp.{region}.amazonaws.com/{poolId}are rewritten to MiniStack's local Cognito JWKS endpoint so locally-minted tokens verify without leaving the box. Reserved-header list for parameter mapping matches the AWS HTTP API spec exactly. Operator tuning:MINISTACK_APIGW_PROXY_TIMEOUT_SECONDS(default30),MINISTACK_APIGW_JWKS_TIMEOUT_SECONDS(default5). Cognito's signing key is now persisted under${STATE_DIR}/cognito-rsa-key.pemso tokens minted in one process verify in another. Contributed by @marcin-nowak-scl. - DynamoDB Streams read API — new
ministack/services/dynamodb_streams.pyexposesListStreams,DescribeStream,GetShardIterator, andGetRecordsviaboto3.client("dynamodbstreams")and thestreams.dynamodb.*host. Reads the records already captured by the main DynamoDB service (emitted fromPutItem,UpdateItem,DeleteItem,TransactWriteItems, andBatchWriteItem) so the public Streams API and the internal Lambda ESM path share one source of truth. Supports all four iterator types (TRIM_HORIZON,LATEST,AT_SEQUENCE_NUMBER,AFTER_SEQUENCE_NUMBER) and all four stream view types (NEW_AND_OLD_IMAGES,NEW_IMAGE,OLD_IMAGE,KEYS_ONLY). Single synthetic shard per stream; opaque base64 iterator tokens. UnblocksDynamoDbOutboxWorker-style consumers. Contributed by @marcin-nowak-scl. - DynamoDB → Kinesis streaming destination —
EnableKinesisStreamingDestination,DisableKinesisStreamingDestination,DescribeKinesisStreamingDestination, andUpdateKinesisStreamingDestinationonboto3.client("dynamodb"). Item mutations fromPutItem/UpdateItem/DeleteItem/TransactWriteItems/BatchWriteItemfan out to every ACTIVE destination as JSON-encoded records (viakinesis.put_record_internal) for Kinesis / Lambda ESM / Firehose-style consumers. DISABLED destinations remain onDescribefor the ~24h AWS window;DeleteTabledrops destinations. The wire envelope reuses the DynamoDB Streams record shape — AWS does not publicly document the exact Kinesis envelope it produces, so MiniStack approximates it with the Streams record. Contributed by @marcin-nowak-scl. - Native HTTPS via
USE_SSL=1— the gateway listener now speaks TLS whenUSE_SSL=1(also acceptstrue/yes), aligning with LocalStack'sUSE_SSLflag name so acompose.ymlswitching emulator doesn't need TLS-specific changes. By default, MiniStack auto-generates a self-signed RSA cert (CN:ministack-local, SAN:localhost,ministack,127.0.0.1,::1) cached under${TMPDIR}/ministack-tls/so the cert survives restarts. To pin a specific cert (e.g. anmkcert-issued one for browser trust), setMINISTACK_SSL_CERTandMINISTACK_SSL_KEYto PEM paths. Auto-generation shells out to theopensslCLI (already present in both images), so no Python crypto dep is added. Unblocks AWS SDKs that hardcodehttps://against Cognito Hosted UI endpoints (e.g. Amplify v6) without needing a separate TLS-terminating proxy. Contributed by @prandogabriel.
Fixed
- Step Functions
aws-sdk:rds:removeFromGlobalClusterleft global cluster members attached — query-protocol parameter conversion uppercasedDbClusterIdentifiertoDBClusterIdentifier, but the RDS API shape forRemoveFromGlobalClusterintentionally usesDbClusterIdentifier. The task now preserves that member name so a successful remove actually detaches the cluster and a followingDeleteGlobalClustermatches AWS behavior. Contributed by @jayjanssen. - Kinesis
ListShardsNextTokenreturned a raw shard ID instead of an opaque pagination token — AWS specifies an opaque token (length 1-1048576) with a 300-second TTL that yieldsExpiredNextTokenExceptionwhen expired. MiniStack now emits a base64url-encoded opaque token and rejects expired or malformed tokens with the AWS-correct error code, so SDKs that round-trip the token (and the rare consumers that inspect or persist it) see AWS-shape behavior. - DynamoDB
DescribeContinuousBackupsreturnedEarliestRestorableDateTime: 0/LatestRestorableDateTime: 0when PITR was disabled — emitting Unix epoch 1970 misled SDK consumers that parsed the values into datetimes. Both fields are now omitted whenPointInTimeRecoveryStatusisDISABLEDand populated with a real timestamp whenENABLED. - DynamoDB
DescribeEndpointsreturned the hardcoded real-AWS endpointdynamodb.us-east-1.amazonaws.com— endpoint-discovery-aware SDKs would cache that address and silently redirect subsequent calls AWAY from MiniStack to real AWS. The endpoint now reflects MiniStack's own host (MINISTACK_HOST:GATEWAY_PORT) so SDKs keep talking to the emulator.
v1.3.19
What's Changed
New Contributors
[1.3.19] — 2026-04-29
Added
- S3 virtual-hosted and path-style integration tests —
TestS3VhostGetPutObjectexercises both addressing styles end-to-end (simple and max-length dotted bucket names),TestExtractS3VhostBucketunit-tests the vhost extraction function, andpatch_endpoint_dnslets virtual-hosted requests resolve against localhost in CI.make_clientnow acceptsadditional_config_kwargsfor per-test SDK config overrides. Contributed by @mgius-ae.
Fixed
- S3 requests via custom
MINISTACK_HOSThostname returnedNoSuchBucket—_extract_s3_vhost_bucket(introduced in 1.3.17) treated any dotted hostname as a virtual-hosted S3 URL, extracting the first label as a bucket name. Requests tohttp://aws.private:4566were misrouted as a vhost request for a bucket namedaws. The function now checks the tail againstMINISTACK_HOSTand recognises all 18 documented AWS S3 virtual-hosted patterns (s3,s3-accelerate,s3-fips,s3-accesspoint,s3-accesspoint-fips,s3-website,s3express-*, and their dualstack/regional variants). Bare hostnames, IPv4 addresses, andlocalhostare correctly treated as path-style. Reported by @dsrosario. - Glue
GetDatabasereturnedLocationUri: ""when not set — AWS specifies a minimum length of 1 forLocationUri, so the empty-string default violated the spec. Now returnsnullwhen the field is omitted fromCreateDatabase. Contributed by @dcrn. - Ruff linter not running on pull requests — CI workflow trigger was missing the PR event.