DocsAWS 101Blog
← Back to Blog

Lambda layers fix, KMS Ed25519, EKS topology labels, ELBv2 setters, Glue UDF

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.

Lambda — layer attachment finally works after the first invoke

Two coupled bugs broke the Lambda + Layers flow when a layer was attached after the function had already been invoked once:

Reported by @omargr299.

KMS — Ed25519 (ECC_NIST_EDWARDS25519) sign / verify

CreateKey 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.

EKS — default topology.kubernetes.io/zone and topology.kubernetes.io/region labels on k3s nodes

Every 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.

ELBv2 — SetSubnets, SetIpAddressType, SetSecurityGroups

Three load-balancer mutation actions implemented per the botocore output shapes:

Glue — UserDefinedFunction APIs

Full 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.

IAM — seeded EKS managed policies

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.

EC2 — Attachment.AttachTime on ENI describe

AttachNetworkInterface 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).

Glue — CreateDatabase honors top-level Tags

The 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.

Glue — UpdateTable optimistic concurrency via VersionId

The 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.

ECS — DeleteService marks INACTIVE instead of removing the record

Matches 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.

Cognito — CUSTOM_AUTH trigger Lambdas no longer deadlock the event loop

The 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.

Upgrade

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"

Stay in sync

Issues and PRs welcome on GitHub. Discussion on r/ministack.