Skip to content

Releases: ministackorg/ministack

v1.3.28

05 May 17:40
dc781a0

Choose a tag to compare

What's Changed

New Contributors

[1.3.28] — 2026-05-05

Added

  • ECS Task Metadata V4 — every container started by RunTask now gets ECS_CONTAINER_METADATA_URI_V4 injected, and the gateway serves /v4/<token>, /v4/<token>/task (with sibling Containers array), and /v4/<token>/stats + /task/stats (stub). Standard com.amazonaws.ecs.* container labels. RunTask also translates privileged, linuxParameters.capabilities.add, pidMode: host, and volumes + mountPoints into Docker bind mounts. Contributed by @YakirOren.

Fixed

  • DynamoDB legacy Expected (PutItem / UpdateItem / DeleteItem) and KeyConditions (Query) — previously ignored; SDKs and code paths that still use the pre-expression API now work. ScanFilter / QueryFilter comparison 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 TransactWriteItems multi-failure reporting — only the first failing item was marked in CancellationReasons; AWS returns a ConditionalCheckFailed entry 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

04 May 22:34
2fd3b1f

Choose a tag to compare

What's Changed

New Contributors

[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). LookupEvents supports all 8 AWS LookupAttributes. Control plane: CreateTrail, DeleteTrail, GetTrail, DescribeTrails, ListTrails, UpdateTrail, GetTrailStatus, StartLogging / StopLogging with real IsLogging state, 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 GetUsagePlanKeyGET /usageplans/{planId}/keys/{keyId} handler was missing; per-key path fell through to 404. Terraform's GetUsagePlanKey refresh after CreateUsagePlanKey aborted every aws_api_gateway_usage_plan_key apply. Contributed by @marcin-nowak-scl.
  • API Gateway v1 HTTP_PROXY path-param substitution + query-string forwarding{paramName} placeholders in integration uri were forwarded literally; the inbound execute path was appended to the integration URI; query string was dropped. Now substitutes from integration.request.path.X = method.request.path.X mappings (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 UpdateModelPATCH /restapis/{id}/models/{name} was missing; Terraform aws_api_gateway_model updates 404
  • Transfer Family LOGICAL root home directory mappingsEntry="/" failed to match because the resolver built "//" as the prefix. Contributed by @stefanmb.
  • CloudTrail router target prefix — was AmazonCloudTrailService; AWS uses CloudTrail_20131101. Routing still worked via credential scope, but the prefix entry was dead code.
  • CloudTrail IsLogging state on Stop/StartLogging — both were no-ops; GetTrailStatus always returned IsLogging: True. Now flips the trail record's state and stamps _StartedAt / _StoppedAt (int epoch).
  • STS Credentials.Expiration is int epoch in the JSON pathAssumeRole / AssumeRoleWithWebIdentity / GetSessionToken returned a float; Java/Go SDK v2 reject it.
  • backup / eks _epoch() / _now() return int — were time.time() (float); consumed by record fields like createdAt.
  • DynamoDB ConditionalCheckFailedException populates Item on ReturnValuesOnConditionCheckFailure="ALL_OLD"PutItem / UpdateItem / DeleteItem / TransactWriteItems now return the prior item alongside the error code (and on the failing CancellationReason for transactions). Verified against botocore: CancellationReason and ConditionalCheckFailedException shapes both include Item. Reported by @darkamgine.
  • CFN AWS::S3::Bucket preserves physical id on update — auto-named buckets got a new random name on every UpdateStack, breaking {Ref} after redeploy. Contributed by @erick-reis-gran.
  • CFN AWS::Lambda::Function returns real CodeSize / CodeSha256 — were hardcoded; now computed from the deployment-package bytes. Contributed by @erick-reis-gran.

v1.3.26

04 May 11:54
f8d7914

Choose a tag to compare

What's Changed

[1.3.26] — 2026-05-04

Added

  • CloudFormation AWS::CloudFront::KeyValueStore — Create / Update (Comment in place) / Delete; exposes Arn, Id, Status via Fn::GetAtt. CFN engine now routes previously-provisioned resources through a per-type update handler when one is defined, falling back to idempotent create otherwise. CloudFront CreateKeyValueStore accepts the optional ImportSource (SourceType + SourceARN) and round-trips it on the record.

Fixed

  • OpenSearch non-VPC domains omit empty VPCOptionsCreateDomain / DescribeDomain previously returned VPCOptions: {} alongside Endpoint, causing Terraform AWS provider reads to classify the domain as VPC-backed and fail with OpenSearch Domain in VPC expected to have null Endpoint value. Non-VPC domains now omit VPCOptions; VPC-shaped domains return Endpoints["vpc"] instead of Endpoint. Contributed by @marcin-nowak-scl.
  • S3 Files routes and shapes match AWS s3files-2025-05-05CreateFileSystem is PUT /file-systems (not POST); request and response bodies use camelCase (bucket, roleArn, fileSystemId, creationTime int epoch); resource tagging moved to /resource-tags/{resourceId}; PutSynchronizationConfiguration enforces optimistic concurrency via latestVersionNumber; standard ValidationException / ResourceNotFoundException / ConflictException errors with application/json content type. Resolves the reported Unknown S3 Files route: PUT /file-systems failure from the AWS CLI / Terraform. Reported by @tmq107

v1.3.25

03 May 16:31

Choose a tag to compare

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-ws subprotocol). Strict auth via APPSYNC_EVENTS_ENFORCE_AUTH=1. Contributed by @marcin-nowak-scl.
  • CloudFront KeyValueStore — management plane — Create/Describe/List/Update/Delete with ETag concurrency; KeyValueStoreAssociations round-tripped through CloudFront Functions. Contributed by @DaviReisVieira.
  • CloudFront KeyValueStore — data plane — separate cloudfront-keyvaluestore service 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 the L (last day / <n>L last weekday-of-month), LW (last weekday), <n>W (nearest weekday), and <n>#<k> (kth weekday-of-month) operators. DoM/DoW mutual-exclusion enforced at PutRule. Contributed by @hiddengearz.

Fixed

  • AppSync Events ChannelNamespace response now includes channelNamespaceArn — spec member was omitted; Terraform / Java SDK v2 saw null where AWS returns the ARN.
  • CloudFront KVS data-plane DescribeKeyValueStore Created / LastModified — were hardcoded to 0; 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_NAMES host-name layer.
  • EventBridge DescribeRule / ListRules now emit CreatedBy and ManagedBy — spec members were silently dropped from _rule_out.
  • EventBridge PutEvents rejects more than 10 entries — AWS spec caps Entries at 10; ministack accepted any size.
  • EventBridge event Time is 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 the exists branch was evaluated, so patterns that should fire on missing fields silently dropped.
  • EventBridge ListRules paginates — added Limit (1-100) and opaque NextToken; previously returned the full list and SDK paginators looped on the first page.
  • EventBridge ListRuleNamesByTarget NextToken is opaque — was a raw integer offset string.
  • EventBridge DescribeEventBus / ListEventBuses omit Policy when no policy set — was emitting "", divergent from AWS shape.
  • EventBridge DescribeEventSource State — was hardcoded ENABLED; AWS enum is PENDING / ACTIVE / DELETED. Now returns ACTIVE.

v1.3.24

02 May 13:06

Choose a tag to compare

[1.3.24] — 2026-05-02

Fixed

  • x-amzn-errortype header now emitted on every JSON-protocol error response. Real AWS sends the error type in both the body (__type) and the x-amzn-errortype header. boto3 falls back to the body, but Java SDK v2, Go SDK v2, and Rust SDK prefer the header — without it they surface SdkClientException: unknown error type instead of the actual code. Applied centrally in error_response_json and 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 __type saw an unknown shape. Body now carries both styles.
  • Three previously-stateless services expose a no-op reset() (account, waf-classic, resourcegroupstaggingapi) so /_ministack/reset no longer logs a warning per call.

v1.3.23

01 May 19:28
18f4175

Choose a tag to compare

What's Changed

[1.3.23] — 2026-05-01

Added

  • Amazon OpenSearch Service — management plane on /2021-01-01/*: CreateDomain, DescribeDomain(s), DeleteDomain, ListDomainNames (with EngineType filter), UpdateDomainConfig, DescribeDomainConfig (Options/Status wrapping), DescribeDomainChangeProgress, ListVersions, GetCompatibleVersions, AddTags/ListTags/RemoveTags. Account-scoped state. Default data plane is a stub endpoint; set OPENSEARCH_DATAPLANE=1 to spawn one real opensearchproject/opensearch container per CreateDomain (same pattern as ElastiCache/RDS). Add OPENSEARCH_DASHBOARDS=1 for an optional per-domain opensearch-dashboards sidecar — DescribeDomain.DashboardEndpoint is populated. DeleteDomain tears down spawned containers. Terraform aws_opensearch_domain compatible. Requested by @marcin-nowak-scl.
  • EventBridge scheduled rule auto-firerate(N minute|hour|day) rules now fire automatically. A daemon thread (eb-scheduler) ticks every 10 s; the per-rule countdown anchors to CreationTime so the first fire lands one full interval after PutRule. Scheduled event payload matches AWS exactly (source: aws.events, detail-type: Scheduled Event, detail: {}, ISO 8601 time). Multi-tenant — iterates the rules store directly. cron() expressions are stored but not yet auto-fired (one-time INFO log 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 Path field (2026-03 AWS additive change).
  • AWS Account service — GetAccountInformation, GetContactInformation, ListRegions, GetRegionOptStatus. Returns the new AccountState: ACTIVE field (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/GetChangeTokenStatus return valid responses, Get* for unknown resources returns WAFNonexistentItemException. For full WebACL state use wafv2.
  • EC2 DescribeRegions — returns 31 commercial regions with correct OptInStatus (opt-in-not-required for legacy us-/eu-/ap-* regions, opted-in for newer ones). Supports RegionNames filter and AllRegions toggle.
  • Lambda FileSystemConfigs accepts 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 on CreateEventBus / UpdateEventBus; persisted, returned on DescribeEventBus.
  • API Gateway v1 securityPolicy accepts the new TLS-1.3 enum — allow-lists SecurityPolicy-TLS13-1-2-FIPS-PFS-PQ-2025-09 (2026-03 addition) and any future opaque values; default remains TLS_1_2.

Fixed

  • S3 PostObject accepts unquoted Content-Disposition field names — .NET's MultipartFormDataContent emits name=foo for ASCII-clean values per RFC 2183; the parser previously only matched the quoted form name="foo" and dropped key/success_action_status, causing browser-form uploads from .NET clients to 400. Reported by @mattburton.
  • EventBridge target dispatch time field is ISO 8601 — was a Unix epoch float, AWS specifies a string like 2026-05-01T18:08:16Z. Both PutEvents-driven and scheduled-rule-driven dispatches now use the canonical format.

Performance

  • Idle RAM% — Dockerfile now sets PYTHONOPTIMIZE=2 and MALLOC_ARENA_MAX=2. Verified zero correctness impact (no assert or __doc__ introspection in ministack source); throughput unchanged.

v1.3.22

30 Apr 18:51
e8e73dd

Choose a tag to compare

What's Changed

[1.3.22] — 2026-04-30

Added

  • Cognito PreTokenGeneration Lambda triggerLambdaConfig.PreTokenGenerationConfig (V2_0) and the legacy LambdaConfig.PreTokenGeneration (V1_0) are now round-tripped through CreateUserPool / UpdateUserPool / DescribeUserPool and 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.scopes for V2+, callerContext.clientId, etc.) and applies the Lambda's response.claimsAndScopeOverrideDetails.{accessTokenGeneration,idTokenGeneration} (V2_0: claimsToAddOrOverride, claimsToSuppress, scopesToAdd, scopesToSuppress, groupOverrideDetails) — or the legacy response.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); set MINISTACK_COGNITO_PRETOKEN_STRICT=1 to fail closed the way real AWS does. Invocation reuses the existing _resolve_name_and_qualifier_get_func_record_for_qualifier_execute_function chain in lambda_svc.py — no new handlers added. Reported by @aahoughton (#533).
  • S3 PostObject (browser-based form upload)POST /<bucket>/ with multipart/form-data is now handled. Honours key (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), and success_action_redirect (303 with bucket=&key=&etag= appended). On 201 returns the <PostResponse> XML with Location/Bucket/Key/ETag. Versioning, persistence, multi-tenancy, S3 event notifications all flow through the same path as PutObject. The content-length-range policy condition is enforced — uploads under the minimum return EntityTooSmall 400 and uploads over the maximum return EntityTooLarge 400 (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's generate_presigned_post works end-to-end. Requested by @mattburton (#535).
  • Init / ready scripts: expose MINISTACK_INIT_SCRIPT_DIR and MINISTACK_INIT_SCRIPT_PATH to each script — every .sh / .py run 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 computing dirname "${BASH_SOURCE[0]}". Phase-level MINISTACK_INIT_BOOT_DIR / MINISTACK_INIT_READY_DIR are also set when those directories exist. Requested by @andreluiznsilva (#520).
  • EC2 Instance Metadata Service (IMDS) emulator — new imds service responds on the gateway port at /latest/api/token (IMDSv2) and /latest/meta-data/... / /latest/dynamic/instance-identity/document. Returns a credentials document under role ministack-instance-role so SDKs that fall through to the IMDS step of the default credential chain (boto3, aws-sdk-go-v2, AWS SDK Java v2) get a valid ASIA* session key + token. Both IMDSv1 (token-less) and IMDSv2 (PUT /token → GET with X-aws-ec2-metadata-token) supported; set MINISTACK_IMDS_V2_REQUIRED=1 to reject token-less requests, matching AWS hop-limit-1 IMDSv2-only instances. Point SDKs at ministack via AWS_EC2_METADATA_SERVICE_ENDPOINT=http://localhost:4566 (or ec2_metadata_service_endpoint in ~/.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 StorageClass was dropped on the floor — objects written with StorageClass=GLACIER / INTELLIGENT_TIERING / etc. came back from GetObject, HeadObject, ListObjects(V2), and ListObjectVersions as STANDARD. The header is now stored on the object record and emitted on the wire (header omitted for the default STANDARD, matching AWS). Same propagation through CopyObject (with optional override via x-amz-storage-class) and CreateMultipartUploadCompleteMultipartUpload. Unknown storage class values now return InvalidStorageClass (400). Verified against botocore/data/s3/2006-03-01/service-2.json. Reported by @JoeHale (#534).

v1.3.21

29 Apr 22:26
7ded88f

Choose a tag to compare

What's Changed

[1.3.21] — 2026-04-29

Added

  • ElastiCache: real Redis replication groups + opt-in real Redis Cluster modeCreateReplicationGroup now spawns live Redis containers per shard (was a metadata-only stub). Behind ELASTICACHE_CLUSTER_MODE_REAL=1 + DOCKER_NETWORK, NumNodeGroups=N/ReplicasPerNodeGroup=R provisions N × (1+R) cluster-enabled nodes, runs redis-cli --cluster create, and serves real CLUSTER SLOTS / MOVED redirects. NumNodeGroups=2 rejected with InvalidParameterValue (matches AWS: only 1 or ≥3 shards). Account-scoped container names + account_id label so accounts can share rg_id; orphan-container reaper at startup. Reported by @akursar.

Fixed

  • ElastiCache list responses wrapped items in <member> instead of the AWS-spec element nameDescribeCacheClusters and 10 other list-emitting ops emitted <member> where AWS uses the model-declared locationName (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 Fault suffix on two not-found shapesDescribeDBInstances (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 against botocore/data/rds/2014-10-31/service-2.json (the wire error.code differs 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's sdkFind, which compares awsErr.ErrorCode() == "DBInstanceNotFound" to detect the not-found branch and reach the create path; with the Fault suffix the branch never matched and the CR sat at Ready=False. Also affects aws-sdk-go-v2 (smithy.APIError.ErrorCode()) and any boto3 caller matching on e.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 _error helper now emits Sender for 4xx and Receiver for 5xx. Cosmetic for SDKs that read <Code> only, but completes the documented AWS shape.
  • API Gateway REST API (v1): pagination missing on 10 list operationsGetRestApis, GetResources, GetDeployments, GetAuthorizers, GetModels, GetApiKeys, GetUsagePlans, GetUsagePlanKeys, GetDomainNames, and GetBasePathMappings ignored the AWS-spec limit (default 25, max 500) + position query params and always returned the full list with no position cursor. 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 per limit, return an opaque base64url-encoded position token when more pages remain, and reject malformed tokens with BadRequestException. GetStages is correctly not paginated — its AWS shape has no limit/position fields.
  • API Gateway REST API (v1) PutMethodResponse and PutIntegrationResponse returned HTTP 200 instead of 201. Real AWS returns 201 on resource creation (verified against botocore/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

29 Apr 18:42
df5a7c9

Choose a tag to compare

What's Changed

New Contributors

[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 JWT issuer + audience validation against a JWKS URL (RS256 signatures only — Cognito's standard), apply request parameter mappings for HTTP / HTTP_PROXY integrations (append/overwrite/remove for headers and querystring, plus overwrite: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 form https://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 (default 30), MINISTACK_APIGW_JWKS_TIMEOUT_SECONDS (default 5). Cognito's signing key is now persisted under ${STATE_DIR}/cognito-rsa-key.pem so tokens minted in one process verify in another. Contributed by @marcin-nowak-scl.
  • DynamoDB Streams read API — new ministack/services/dynamodb_streams.py exposes ListStreams, DescribeStream, GetShardIterator, and GetRecords via boto3.client("dynamodbstreams") and the streams.dynamodb.* host. Reads the records already captured by the main DynamoDB service (emitted from PutItem, UpdateItem, DeleteItem, TransactWriteItems, and BatchWriteItem) 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. Unblocks DynamoDbOutboxWorker-style consumers. Contributed by @marcin-nowak-scl.
  • DynamoDB → Kinesis streaming destinationEnableKinesisStreamingDestination, DisableKinesisStreamingDestination, DescribeKinesisStreamingDestination, and UpdateKinesisStreamingDestination on boto3.client("dynamodb"). Item mutations from PutItem / UpdateItem / DeleteItem / TransactWriteItems / BatchWriteItem fan out to every ACTIVE destination as JSON-encoded records (via kinesis.put_record_internal) for Kinesis / Lambda ESM / Firehose-style consumers. DISABLED destinations remain on Describe for the ~24h AWS window; DeleteTable drops 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 when USE_SSL=1 (also accepts true / yes), aligning with LocalStack's USE_SSL flag name so a compose.yml switching 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. an mkcert-issued one for browser trust), set MINISTACK_SSL_CERT and MINISTACK_SSL_KEY to PEM paths. Auto-generation shells out to the openssl CLI (already present in both images), so no Python crypto dep is added. Unblocks AWS SDKs that hardcode https:// 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:removeFromGlobalCluster left global cluster members attached — query-protocol parameter conversion uppercased DbClusterIdentifier to DBClusterIdentifier, but the RDS API shape for RemoveFromGlobalCluster intentionally uses DbClusterIdentifier. The task now preserves that member name so a successful remove actually detaches the cluster and a following DeleteGlobalCluster matches AWS behavior. Contributed by @jayjanssen.
  • Kinesis ListShards NextToken returned a raw shard ID instead of an opaque pagination token — AWS specifies an opaque token (length 1-1048576) with a 300-second TTL that yields ExpiredNextTokenException when 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 DescribeContinuousBackups returned EarliestRestorableDateTime: 0 / LatestRestorableDateTime: 0 when PITR was disabled — emitting Unix epoch 1970 misled SDK consumers that parsed the values into datetimes. Both fields are now omitted when PointInTimeRecoveryStatus is DISABLED and populated with a real timestamp when ENABLED.
  • DynamoDB DescribeEndpoints returned the hardcoded real-AWS endpoint dynamodb.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

29 Apr 11:18

Choose a tag to compare

What's Changed

New Contributors

  • @dcrn made their first contribution in #514

[1.3.19] — 2026-04-29

Added

  • S3 virtual-hosted and path-style integration testsTestS3VhostGetPutObject exercises both addressing styles end-to-end (simple and max-length dotted bucket names), TestExtractS3VhostBucket unit-tests the vhost extraction function, and patch_endpoint_dns lets virtual-hosted requests resolve against localhost in CI. make_client now accepts additional_config_kwargs for per-test SDK config overrides. Contributed by @mgius-ae.

Fixed

  • S3 requests via custom MINISTACK_HOST hostname returned NoSuchBucket_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 to http://aws.private:4566 were misrouted as a vhost request for a bucket named aws. The function now checks the tail against MINISTACK_HOST and 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, and localhost are correctly treated as path-style. Reported by @dsrosario.
  • Glue GetDatabase returned LocationUri: "" when not set — AWS specifies a minimum length of 1 for LocationUri, so the empty-string default violated the spec. Now returns null when the field is omitted from CreateDatabase. Contributed by @dcrn.
  • Ruff linter not running on pull requests — CI workflow trigger was missing the PR event.