github ministackorg/ministack v1.3.16

6 hours ago

What's Changed

New Contributors


[1.3.16] — 2026-04-27

Added

  • BIND_HOST env var to configure the listen interfaceBIND_HOST=127.0.0.1 ministack now restricts the listener to loopback for pip install users on shared dev machines. Defaults to 0.0.0.0, so existing setups are unchanged. Distinct from MINISTACK_HOST (the virtual hostname used for S3 virtual-host / execute-api URL matching). Contributed by @mattwang44.
  • Lambda Code.S3ObjectVersion honoured end-to-endCreateFunction, UpdateFunctionCode, PublishLayerVersion, and the CloudFormation AWS::Lambda::Function provisioner all thread the version through to S3, so Terraform aws_lambda_function.s3_object_version and CDK Code.fromBucket(..., objectVersion=...) deploy the pinned bytes instead of silently picking up the latest.

Fixed

  • Lambda 250 MB unzipped-size limit was not enforcedCreateFunction / UpdateFunctionCode / PublishLayerVersion accepted oversize zips and failed only at invocation time. All three now reject up-front with InvalidParameterValueException, matching AWS.
  • S3 with S3_PERSIST=1: versioned object bodies capped at 10 MB and GetObject(VersionId) returned 500 for larger multipart uploads — bodies now persist to disk (in-memory record drops the body, on-demand reads stream back), versioned reads return the persisted bytes, and disk writes go through atomic tmp+rename with mode 0o600 / dir 0o700 plus a path-traversal guard that rejects keys resolving outside S3_DATA_DIR.
  • CloudFormation AWS::Lambda::Function never fetched S3 code_lambda_create stored Code.S3Bucket / Code.S3Key as metadata but never resolved them against the in-memory S3 service, so every CFN-deployed S3-backed Lambda had code_zip = None and failed to invoke. Inline ZipFile was unaffected; everything else broken. Provisioner now fetches the bytes via the standard Lambda S3 helper.
  • Persisted Cognito hosted-UI / federation _auth_codes lost across warm-boot_auth_codes had a 5-minute TTL but was declared "ephemeral, not persisted", so any in-flight hosted-UI sign-in straddling a warm-boot was silently invalidated. Now wired into get_state / restore_state so codes survive a restart up to their normal TTL. Plain-dict choice (no AccountScopedDict) preserved with a corrected rationale: none of the OAuth2 endpoints carry SigV4, so wrapping in AccountScopedDict would be functionally equivalent. Contributed by @bognari.
  • PERSIST_STATE=1: twelve mutated state dicts were silently dropped on warm-boot. Five wired in by @bognari (secretsmanager._resource_policies, kinesis._consumers, ecs._attributes, sns._platform_applications, sns._platform_endpoints) — without these, aws_secretsmanager_secret_policy, aws_kinesis_stream_consumer, ECS PutAttributes, and mobile-push topology silently disappeared on restart. Plus cloudwatch_logs._destinations / _metric_filters / _queries (also @bognari) — log-destinations and metric filters wiped on restart. Plus the seven follow-ups for the same bug family: sqs._queue_name_to_url (snapshotted via dict(asd) instead of copy.deepcopy, dropping every non-current-tenant mapping), eventbridge._event_bus_policies / _connections / _api_destinations (every aws_cloudwatch_event_connection and aws_cloudwatch_event_api_destination lost), ssm._parameter_history and _tags (GetParameterHistory returned empty after warm-boot), and lambda_svc._kinesis_positions / _dynamodb_stream_positions (every restart replayed event-source-mapping streams from StartingPosition, a real at-least-once-delivery violation).
  • Persisted services failed to restore on startup for autoscaling / backup / eks / scheduler / pipes — these five services declared restore_state but never invoked it on import, so warm-boots came up with empty state regardless of PERSIST_STATE=1. Wired in. Contributed by @bognari.
  • Eager-imported non-routable persisted services on startup — services without an HTTP route never imported, so their load_state block never fired and persisted state evaporated. Eager-import now triggers restore. Contributed by @bognari.
  • NameError at import on warm-boot for any persisted service whose restore_state referenced a forward-declared symbol — parametrized regression test added across every persisted service to catch the import-order shape that previously hit lambda_svc._ensure_poller, ecs._attributes, and acm._synthetic_pem. Contributed by @bognari.
  • ACM GetCertificate returned a placeholder PEM and leaked private-key bytes to disk on RequestCertificate issuance — body / chain fidelity now matches real AWS shape, and the private-key disk-leak path is scrubbed. Contributed by @bognari.
  • API Gateway v2 integration physicalId returned {apiId}/{integrationId} instead of just {integrationId} — broke Ref resolution against aws_apigatewayv2_integration, so route → integration lookup failed at request time and CFN-deployed HTTP APIs returned 500 No integration configured for every request. Ref now matches AWS; handle_execute and _invoke_ws_lambda defensively strip a legacy {apiId}/ prefix so existing stacks continue to work. Contributed by @hiddengearz.
  • API Gateway v1 PutIntegration dropped contentHandling — the field was accepted on create but never persisted, so CONVERT_TO_TEXT / CONVERT_TO_BINARY payload translation silently no-op'd. Contributed by @bognari.
  • SNS → SQS raw delivery did not forward message attributes — raw subscriptions delivered the message body but stripped MessageAttributes, so SQS receivers never saw them. Forwarded now, plus the follow-up that adds the matching MD5OfMessageAttributes header so Java / Go SDK receivers (which verify the digest) match real AWS. Contributed by @arischow.
  • Three medium / low correctness bugs. apigateway and apigateway_v1 get_state() returned live AccountScopedDict references instead of deep copies, so a concurrent write during shutdown serialisation could corrupt the persisted snapshot. secretsmanager._delete_secret(force=True) deleted the secret but left orphan entries in _resource_policies keyed by ARN — invisible to the API but accumulating in memory and surviving warm-boot. acm._list_certificates returned {"NextToken": null} unconditionally — boto3 strips it client-side, but Java / Go / raw-HTTP pagination clients that loop on if NextToken in response looped forever. Contributed by @bognari. Pattern extended in this release with a sweep across ses_v2, apigateway v2, and apigateway v1 (ten more endpoints) so every list response now omits NextToken when there is no next page; AppSync's GraphQL {items, nextToken} shape is intentionally unchanged.
  • /health reported version: dev in the published Docker imagepip is stripped from the runtime image, so the importlib.metadata lookup that worked under pip install ministack returned the fallback. Now reads from a MINISTACK_VERSION env var injected at image build time.

Don't miss a new ministack release

NewReleases is sending notifications on new releases.