October 26, 2026 · v1.5.33
v1.5.33 adds Kinesis stream and consumer resource policies with AWS's validation messages, lets a Lambda event source mapping read a stream in another account through that policy, and supports AWS::Athena::WorkGroup in CloudFormation. ALB targets now get requests as the load balancer sends them and responses carry the headers ALB and CloudFront set, S3 GET, HEAD and DELETE errors follow the observed order and shapes, and keys such as folder markers survive a restart with S3_PERSIST=1. It also refuses Lambda packages that aren't zips, matches Kinesis filters on decoded data, follows DynamoDB's TransactWriteItems check order, and evaluates JSONata on Step Functions Map and Parallel states.
PutResourcePolicy, GetResourcePolicy and DeleteResourcePolicy answered InvalidAction; they now store a policy per stream or consumer, persisted and dropped with it, with AWS's checks in AWS's order: the ARN's region (InvalidArgumentException "The region specified in the ARN '<arn>' does not match the endpoint region"), the policy ("Policy validation error: This policy contains invalid Json", "Missing required field Version" or "Statement", and per statement the actions a stream or consumer can share and a Resource equal to the ARN, with the statement's line and column), then the resource ("Stream <name> under account <id> not found."). GetResourcePolicy without a policy answers {"Policy": "{}"} and DeleteResourcePolicy "No resource policy found for resource ARN <arn>.". Reported by @Jolley71717.CreateEventSourceMapping reads the stream first and answers "Received Exception while reading from provided stream. " with the reason ("No account found for the given parameters", "Stream <name> under account <id> not found.", or under AUTH=true the read the stream's policy doesn't allow the function's execution role), and the policy is checked again on every poll. Another region's stream answers "Event source region must match Lambda region <region>". Reported by @Jolley71717.AWS::Athena::WorkGroup. created with its Description, State, Tags and WorkGroupConfiguration, updated in place, replaced when Name changes and deleted with the stack; Ref returns the name and Fn::GetAtt CreationTime and the effective engine version. Reported by @mishukdutta-cz.AUTH=true ordinary header-signed downloads check the endpoint and signing region before the bucket's home region, then credentials and missing buckets, and HeadObject, HeadBucket and DeleteObject check the home region first; a DELETE sent to another regional endpoint no longer deletes the object. In both auth modes a NoSuchKey carries Key, HEAD errors have an empty body and no Content-Length, and HeadBucket reports the bucket's region (also on a denial) and its ARN. Iceberg REST HEAD requests keep their JSON errors. Contributed by @gakuto-cw21.S3_PERSIST=1 a folder marker such as project/Incoming/ came back without its slash, and keys under it, a key under another key's object (a/b beside a) or one with doubled or dot segments (x//y) were lost or rewritten; they are now stored by an encoded name under the bucket's .ministack-keys directory, and every other key keeps its path, so earlier data still loads. Reported by @jasonthagerty.Host as the load balancer sends it (no port on 80 or 443, the listener's port otherwise, unmodified with routing.http.preserve_host_header.enabled), the client's Accept-Encoding, and OPTIONS requests instead of a gateway-made CORS answer. A Lambda target's event carries the last value of a repeated query key, not decoded, no multi-value keys unless enabled, and body: "". Contributed by @skialpine.Server: awselb/2.0 unless routing.http.response.server.enabled is false, it writes its own Date over an instance or IP target's, and the listener's response-header attributes (the CORS ones, strict_transport_security, content_security_policy, x_content_type_options, x_frame_options) are applied. A redirect fills #{protocol}, #{port} and #{query} from the listener and request (http://host:80/path), and authenticate-oidc on a 443 listener names its callback without the client's port. Contributed by @skialpine.X-Amz-Cf-Id as URL-safe base64 of 40 bytes and Via: 1.1 <32 hex>.cloudfront.net (CloudFront); Server: CloudFront is added only when a viewer-response function ran and the origin sent none, and a Lambda Function URL origin's response carries a single x-amzn-RequestId. Contributed by @skialpine.Date and Server. HTTP/1.1 status lines carry their reason phrase (HTTP/1.1 200 OK), every response carries one Date, also a WebSocket handshake and Hypercorn's own errors, and no response carries Server: hypercorn-h11. Contributed by @skialpine.rawQueryString is the query string as sent. ?flag reached the function as flag=; the Lambda proxy and REQUEST authorizer events now carry the request's own query string. Contributed by @skialpine.CreateFunction, UpdateFunctionCode and PublishLayerVersion refuse a package that is not a zip, an empty one included, with InvalidParameterValueException "Could not unzip uploaded file. Please check your file, then try to upload again.", leaving the function's code as it was. Two MiniStacks on one host no longer remove each other's extracted code, and restoring persisted state keeps one copy of code that versions share. Contributed by @skialpine.FilterCriteria pattern never matched a Kinesis record, so a filtered mapping dropped everything; metadata fields such as partitionKey now match at the top of the pattern and data as base64-decoded JSON, and data that isn't JSON still matches metadata patterns. Contributed by @Jolley71717.TransactWriteItems follows DynamoDB's check order. an Update whose condition failed is cancelled with ConditionalCheckFailed even when its update expression can't be applied to the item, an update-expression error on one member no longer hides a failed condition on another, a missing table answers ResourceNotFoundException, and a begins_with condition with a number operand answers ValidationException. Contributed by @jun-ut.Map, Parallel and variables. an inline Map evaluates Items, ItemSelector, MaxConcurrency, Output and Assign (an Items that isn't an array fails with States.QueryEvaluationError), a Catch on Map or Parallel applies its Output and Assign, and a variable assigned null reads back as null. Contributed by @mishukdutta-cz.--JOB_NAME and --JOB_RUN_ID (a run's Arguments still win, pythonshell scripts don't get them), and a job's own botocore loads from where the script asks, so a --py-files zip that holds botocore no longer fails with DataNotFoundError. Contributed by @mishukdutta-cz.docker pull ministackorg/ministack:1.5.33 docker run -d -p 4566:4566 ministackorg/ministack:1.5.33
Or pin in compose.yaml:
services:
ministack:
image: ministackorg/ministack:1.5.33
ports:
- "4566:4566"
Issues and PRs welcome on GitHub. Discussion on r/ministack.