June 4, 2026 · v1.3.58
The headline fix this release: UpdateFunctionConfiguration(Layers=[...]) now actually works end-to-end — GetFunctionConfiguration reports each layer's real CodeSize, and the warm worker is recycled so the next Invoke mounts the layer at /opt/layer_N and the handler can import from it. KMS gains Ed25519 sign / verify, EKS k3s nodes pick up the real AWS topology labels, ELBv2 gains SetSubnets / SetIpAddressType / SetSecurityGroups, Glue gains the full UserDefinedFunction lifecycle, and IAM's EKS managed-policy seeds now carry the real AWS documents instead of the wildcard fallback.
Two coupled bugs broke the Lambda + Layers flow when a layer was attached after the function had already been invoked once:
GetFunctionConfiguration.Layers[*].CodeSize was hardcoded to 0 in both CreateFunction and UpdateFunctionConfiguration. The layer record itself stored the real zip size; the function-config build site was the gap. Now both sites resolve the layer ARN against the stored version and emit the real CodeSize, matching the AWS shape.account:func:qualifier, with no dependency on layers. So after UpdateFunctionConfiguration(Layers=[arn]) the next invoke reused the existing worker process — the one spawned without the layer extracted on disk — and the handler's import from the layer module failed at entry. UpdateFunctionConfiguration now calls invalidate_worker when any worker-affecting field changes (Layers, Runtime, Handler, Environment, MemorySize, Architectures, VpcConfig, FileSystemConfigs), so the next invoke spawns a fresh worker with the layer mounted at /opt/layer_N.Reported by @omargr299.
ECC_NIST_EDWARDS25519) sign / verifyCreateKey with KeySpec=ECC_NIST_EDWARDS25519 now generates a real Ed25519 keypair (via cryptography) and returns SigningAlgorithms=["ED25519_SHA_512","ED25519_PH_SHA_512"], matching the AWS Developer Guide "Supported signing algorithms for ECC key specs" table verbatim. Sign / Verify enforce the AWS Sign-API contract: ED25519_SHA_512 requires MessageType=RAW (rejected with UnsupportedOperationException otherwise), and ED25519_PH_SHA_512 (HashEdDSA / Ed25519ph, RFC 8032 §5.1) returns UnsupportedOperationException rather than route through pure Ed25519 — those signatures would be incompatible with real AWS KMS, since Ed25519ph adds a dom2 prefix that the cryptography library doesn't expose. Contributed by @KABBOUCHI.
topology.kubernetes.io/zone and topology.kubernetes.io/region labels on k3s nodesEvery cluster's k3s container now receives --node-label topology.kubernetes.io/zone={region}a and --node-label topology.kubernetes.io/region={region}, matching the labels the AWS cloud-controller-manager emits on real EKS nodes. Topology-aware controllers — Karpenter, Cluster Autoscaler, scheduler topologySpreadConstraints — now have what they need to place pods without the manual kubectl label node workaround. Region resolves through get_region(); per-node-group label overrides belong on CreateNodegroup.labels — the AWS-shape-correct surface — not a ministack-specific tag convention. Contributed by @b-rajesh.
SetSubnets, SetIpAddressType, SetSecurityGroupsThree load-balancer mutation actions implemented per the botocore output shapes:
SetSubnets — accepts Subnets list or SubnetMappings form, plus optional IpAddressType; returns AvailabilityZones + IpAddressType.SetIpAddressType — validates against the AWS enum (ipv4 | dualstack | dualstack-without-public-ipv4) and returns IpAddressType.SetSecurityGroups — replaces the LB's security groups; returns SecurityGroupIds (note: not SecurityGroups — the AWS output shape uses a different field name from the input).UserDefinedFunction APIsFull UDF lifecycle: CreateUserDefinedFunction, UpdateUserDefinedFunction, DeleteUserDefinedFunction, GetUserDefinedFunction, GetUserDefinedFunctions. Stored records carry FunctionName, DatabaseName, ClassName, OwnerName, OwnerType, CreateTime, ResourceUris, CatalogId per the AWS UserDefinedFunction output shape. GetUserDefinedFunctions honors the AWS-required Pattern glob.
AmazonEKSClusterPolicy, AmazonEKSWorkerNodePolicy, AmazonEKS_CNI_Policy, and AmazonEKSServicePolicy now resolve to the real AWS policy documents — the exact JSON from the AWS Managed Policy Reference — rather than the wildcard Allow * fallback. GetPolicyVersion returns the real action lists (elasticloadbalancing:CreateLoadBalancer, ec2:CreateSecurityGroup, eks-auth:AssumeRoleForPodIdentity, etc.), so Terraform diffs against real AWS now line up, and policy simulators that walk the action list behave the same locally as in production.
Attachment.AttachTime on ENI describeAttachNetworkInterface now records the attach timestamp on the ENI's Attachment record, and DescribeNetworkInterfaces surfaces it as <attachTime> in the wire XML — matching the AWS NetworkInterfaceAttachment shape (verified against botocore's field-name mapping). Required by tools that audit attachment age (Cloud Custodian rules, AWS Config rules, custom IaC linters).
CreateDatabase honors top-level TagsThe Glue CreateDatabase input has the database fields under DatabaseInput and tags as a separate top-level Tags field. MiniStack was reading DatabaseInput and silently dropping Tags on the floor, so GetTags(ResourceArn=arn) against a freshly-created tagged database returned empty. Tags are now stored against the database ARN (arn:aws:glue:{region}:{account}:database/{name}) at create time and cleaned up on DeleteDatabase.
UpdateTable optimistic concurrency via VersionIdThe AWS Glue Table output shape carries a VersionId field, and UpdateTable accepts an optional VersionId for optimistic concurrency. MiniStack's _update_table previously overwrote the table unconditionally, so two concurrent writers couldn't detect each other's edits. Table records now carry a monotonically-increasing VersionId (string); UpdateTable with a stale VersionId returns ConcurrentModificationException; a matching version bumps and applies. Calls that omit VersionId keep the existing last-write-wins behavior for back-compat with simple writers.
DeleteService marks INACTIVE instead of removing the recordMatches the AWS contract: "Services in the DRAINING or INACTIVE status can still be viewed with the DescribeServices API operation." Tasks are stopped synchronously, then the service stays in the store with status=INACTIVE and tags still attached. Re-creating a service with the same name is allowed once the prior incarnation is INACTIVE (matching the AWS-documented conflict-only-on-ACTIVE/DRAINING rule). Real AWS may eventually purge INACTIVE records (no fixed window per the docs); ministack keeps them for the process lifetime.
CUSTOM_AUTH trigger Lambdas no longer deadlock the event loopThe Cognito IDP / Identity dispatchers were synchronous. When a Cognito op invoked a trigger Lambda (Define / Create / Verify auth challenge, pre-token, etc.), the dispatcher blocked the ASGI event loop waiting for the Lambda HTTP callback — which the same loop needed to be free to serve. Every CUSTOM_AUTH flow hung at the first trigger. _dispatch_idp and _dispatch_identity are now async and run the sync handlers via asyncio.to_thread, so the loop stays available to accept the Lambda's callback. Reported by @aahoughton.
docker pull ministackorg/ministack:1.3.58 docker run -d -p 4566:4566 ministackorg/ministack:1.3.58
Or pin in compose.yaml:
services:
ministack:
image: ministackorg/ministack:1.3.58
ports:
- "4566:4566"
Issues and PRs welcome on GitHub. Discussion on r/ministack.