github ministackorg/ministack v1.5.12

2 hours ago

What's Changed

New Contributors

[1.5.12] — 2026-09-15

Added

  • EKS — Pod Identity associations — the five operations did not exist, so a controller that reads pod identity to find its role, the AWS Load Balancer Controller among them, could not run against MiniStack. Create, Describe, List, Update and Delete serve the records, one per namespace and service account. Deleting a cluster removes them with it. Reported by @josephaw1022.
  • EKS — aws eks update-kubeconfig works against MiniStack — k3s could not authenticate the IAM exec token aws eks get-token produces, so the only way in was to copy the admin kubeconfig out of the container. MiniStack now answers a TokenReview webhook: AUTH=false accepts local bearer tokens, AUTH=true verifies the presigned token and then requires a creator or Access Entry grant. The five general-purpose access policies grant their published Kubernetes permissions at cluster or namespace scope. Contributed by @jgrumboe. Reported by @StraggleCraft.

Changed

  • SES — one v2 implementation, returning the v1 MessageId shapeses.py carried an unreachable copy of the v2 endpoints that had drifted from ses_v2.py. The duplicate is gone and every v2 request is served by one implementation, which surfaced a divergence it had hidden: SendEmail answered an id prefixed ministack-, where the v1 paths return <uuid>@email.amazonses.com. Both now return the shared shape. Contributed by @jgrumboe.
  • Persistence — every service module declares the same state contract — a module could expose get_state, a restore_state, both or neither, so persistence was wired per service and nothing could be derived from it. Every module in ministack/services/ now exposes the same get_state / load_persisted_state / reset trio, stateless ones included, and a registry-driven test refuses a service that does not. Contributed by @jgrumboe.

Fixed

  • S3 — a notification configuration whose destination does not exist is refused — the configuration was stored and the failed test delivery swallowed, so a queue or topic that was not there looked like a successful setup and no event ever arrived. AWS verifies an SQS or SNS destination by sending it a test notification and fails the whole PUT when it does not arrive; a missing one is now InvalidArgument with nothing stored. Reported by @ortizgui.
  • ECS — a task walks the whole lifecycle — a Docker-backed task went straight from PENDING to RUNNING to STOPPED, so a consumer waiting on any other state waited forever. It now reports the states AWS documents, in order: PROVISIONING, PENDING, ACTIVATING, then DEACTIVATING, STOPPING, DEPROVISIONING and STOPPED. Every starting state counts against a service's desired capacity, while pendingCount counts PENDING alone as the API model defines it. Reported by @iot-rocket.
  • ECS — a task's version counts its state changes — the counter was minted at 1 and never moved, so a consumer could not tell a stale DescribeTasks copy from the current one. It now moves on each change the record reports, matching a real Fargate task: 1 at PROVISIONING through 6 at STOPPED. Contributed by @iot-rocket.
  • ECS — only an awsvpc task reports an elastic network interface — every task got an ElasticNetworkInterface attachment once its container was up, whatever its network mode, while the Task reference describes attachments as the adapter a task has "if the task uses the awsvpc network mode". Other modes now report an empty list, and an awsvpc attachment names the subnet the request placed the task in. Contributed by @iot-rocket.
  • ECS — a task no longer reports attachmentsStatus — the task record carried the member next to its attachment, and there is no such member on the Task shape: the API model has it on Cluster only. A client reading the raw wire saw an invented field. Contributed by @iot-rocket.
  • ECS — the task metadata endpoint reports the status the task is in${ECS_CONTAINER_METADATA_URI_V4} served DesiredStatus and KnownStatus as the literal RUNNING, captured at registration, so a container reading its own metadata was told the task was running while DescribeTasks reported otherwise. Both now follow the task record, with KnownStatus per container as AWS reports it. Contributed by @iot-rocket.
  • Cognito — a hosted-UI request that invokes a Lambda trigger no longer deadlocks/saml2/idpresponse, /oauth2/idpresponse and /oauth2/token ran on the event loop, so a trigger calling back into MiniStack queued behind its own request and timed out as a 400. All three now run off the loop, and a replayed authorization code gets invalid_grant instead of a 500. Contributed by @kjdev.
  • SQS — every batch action validates the request before its entries — the ten-entry limit reached SendMessageBatch only, and three more request-level errors the model declares on all three batch actions were never raised. DeleteMessageBatch and ChangeMessageVisibilityBatch now refuse an empty batch, more than ten entries, repeated ids and malformed ids, failing the whole request rather than landing in Failed. Contributed by @CaptainAni187.
  • Persistence — the state map is derived from SERVICE_REGISTRY — it was a hand-maintained dict alongside the registry, and cloudcontrol, config, cur and organizations were never added to it, so everything they held was dropped on every warm boot. The save map and the reset set now come from the registry, including the modules a service reaches through its own dispatcher, and a test refuses any service that would be saved with no way to restore it. Contributed by @jgrumboe.
  • IAM — a resource-scoped policy matches, instead of denying — under AUTH=true the resource ARN resolved to * for whole families of request, so only Resource: "*" matched and everything narrower was denied. IoT jobs, provisioning templates, both IoT data planes, multi-level MQTT topics and API Gateway invokes now resolve to their real ARNs. Contributed by @iot-rocket.
  • CloudFormation — AWS::IAM::Policy is provisioned inline, not as a managed policy — the document was stored as an account-global managed policy and attached, where CloudFormation embeds it on each Role, User and Group it names. CDK derives PolicyName from the construct path, so stacks sharing a path collapsed onto one record and each deploy took the grants from the ones before it. ListRolePolicies and GetAccountAuthorizationDetails now report it and the IAM evaluator finds it. Contributed by @iot-rocket.
  • Lambda — SQS event source batches are dispatched concurrently — one thread polled every mapping and waited for each invoke, so a slow handler held messages on unrelated queues, and ScalingConfig.MaximumConcurrency was stored but ignored. Batches now run off the poll thread, up to MaximumConcurrency per mapping, with a FIFO event source held to one at a time so message groups keep their order. Contributed by @ThailerL.

Internal

  • Test harness — the suite runs on a non-default GATEWAY_PORT — eight tests compared a value a service had built from GATEWAY_PORT against a hard-coded 4566, so the suite passed only on the default port, which is exactly the port a contributor moves off to avoid resetting a shared server. Contributed by @iot-rocket.

Don't miss a new ministack release

NewReleases is sending notifications on new releases.