> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# v0.9.0

Released: October 24, 2026

Three things stand out: RustFS replaces MinIO as the local S3 server (MinIO's images were removed from Docker Hub), rate limiting uses provider-specific client address headers and per-route limits, and Kong and Next.js scale on Azure App Service. Also in this release: IPv6 on every provider, deploys that wait out capacity shortages, database readiness at startup, cleaner logs.

***

## Local development

- **RustFS replaces MinIO** as the local S3 server. Same endpoint (`http://localhost:9000`) and console (`9001`); credentials `rustfsadmin` / `rustfsadmin`; data in `./data/rustfs/`.

## Rate limiting

- **Client address from the edge:** `X-Azure-SocketIP` on Azure, `X-Tsdevstack-Edge-IP` on GCP and AWS. Clients cannot forge it.
- **Per-visitor limits behind the Next.js frontend:** the new `tsdevstack-client-ip` gateway plugin and the `KONG_FRONTEND_TOKEN` secret.
- **Private path from the frontend to the gateway:** a private endpoint on Azure (about $8 a month), Cloud Map on AWS.
- **Per-route limits** in nest-common, with fixed windows.

Details in [Client addresses](/docs/customization/kong-customization.md#client-addresses) and [Rate limiting](/docs/packages/nest-common.md#rate-limiting).

## Azure

- **App Service scaling:** `minInstances` and `maxInstances` set the instance count on Basic plans and a CPU autoscale range on Standard and Premium. See [Azure App Service plans](/docs/infrastructure/service-configuration.md#azure-app-service-plans).
- Managed Redis is private to your VNet.
- `infra:destroy` removes the Container Apps and migration jobs too.
- IPv6 on the apex domain; `database.ha` adds a zone-redundant standby; `cloud:init` checks the 12 resource providers.

## GCP

- **IPv6** on the load balancer; the deploy prints the AAAA records. See [GCP DNS & Domains](/docs/infrastructure/providers/gcp/dns-and-domains.md).
- Startup probe every 2 seconds; presigned URLs signed with each service's own identity.
- Cloud Run resource rules are validated before a deploy; `infra:bootstrap` enables only the APIs in use.

## AWS

- **IPv6** on every CloudFront distribution, with AAAA records in Route 53.
- `infra:destroy` tears down the environment in one run.
- `database.diskSize`, `ha` and `backup` apply; workers are pinned to the deployed image tag.

## Every provider

- **Capacity retry:** `infra:deploy` waits 5 minutes and tries again, up to 3 times.
- `infrastructure.json` is validated in build and deploy commands.
- **Database readiness:** `waitForDatabase` holds startup until the database answers; the pool keeps connections longer.
- Redis certificate verification on AWS and Azure; `/health` includes Redis.

## Cleaner logs

- One entry per request with `http.*` fields; 4xx at warn, 5xx at error; probes at debug; Nest's own messages through the same logger.

## Smaller improvements

- Generated clients validate arrays, enums and maps as declared. Signup lowercases emails; login matches any case. `API_KEY` resolves through `SecretsService` everywhere. Secret prompts are masked. The local gateway image is named after your project. MCP tools fail fast on prompts and get 90 minutes for deploys.

## Breaking changes

Pre-1.0, so they ship in a minor version. The upgrade guide below covers each.

- **Secrets:** run `cloud-secrets:push` once per cloud environment to create `KONG_FRONTEND_TOKEN`; `infra:build-kong` stops without it.
- **Gateway:** a full `kong.custom.yml` must declare `tsdevstack-client-ip` itself. The deploy sets `KONG_TRUSTED_IPS`, `KONG_REAL_IP_HEADER` and `KONG_REAL_IP_RECURSIVE`.
- **AWS:** `KONG_INTERNAL_URL` becomes `http://kong.{project}.local:8080`; the next deploy adds Cloud Map, a CloudFront function and AAAA records.
- **Local stack:** storage credentials change to `rustfsadmin`; `sync` regenerates `.env` and `docker-compose.yml`. Files in `./data/minio/` are not carried over.
- **Rate limits:** new counter keys (limits start from zero); routes count separately unless they share a `customKeyGenerator` key.
- **Settings that take effect on the next deploy:** AWS `database.diskSize` (grows storage, cannot shrink), `ha` (Multi-AZ, about twice the cost), `backup: false` (retention 0); Azure `database.ha` (standby, about twice the cost), App Service instance counts (each billed), Kong's private endpoint with Next.js apps (about $8 a month).
- **Validation:** values that deployed before can now fail: a tier from another provider, an Azure `diskSize` outside 32/64/128/256/512, `redis.memoryGb` outside GCP, `cpu` or `memory` on Kong or Next.js on Azure, `ha` on a Burstable tier, `ha` with `backup: false` on GCP, Cloud Run CPU below 1 without `concurrency: 1`.
- **Logs:** request fields moved under `http`; the `Incoming request` entry is gone. `cloud-secrets:push` sends `LOG_LEVEL=info`.
- **nest-common:** `SecretsService.getProvider().getAll()` is removed; `/health` reports Redis `down` (status `degraded`, still 200).
- **Generated clients:** regenerated DTOs accept arrays and `number | string` fields as declared.
- **GCP:** the first deploy with buckets moves the presigned URL permission; presigned URLs can fail for a few minutes.

## nest-common 0.9.0

New: `waitForDatabase`. Changed: per-route rate-limit keys, request log format, Nest's logger routed through the framework logger, client address from `X-Tsdevstack-Real-IP`. Removed: `getAll()` on the secrets provider.

## Upgrading an existing project

**1. Update the packages.**

```bash
npm install -D @tsdevstack/cli@^0.9.0
npm install @tsdevstack/nest-common@^0.9.0 -w auth-service   # repeat for each NestJS service
```

**2. Refresh secrets and the local stack.** `npx tsdevstack sync` locally (adds `KONG_FRONTEND_TOKEN`, switches to RustFS), then per cloud environment:

```bash
npx tsdevstack cloud-secrets:push --env <env>
```

**3. Frontend: pass the visitor.** Copy `lib/utils/forward-client-ip.ts` from a fresh `npx @tsdevstack/cli init` project and register it on every client your server uses to call the gateway (the template does it in `lib/externalApi/auth-service.api.ts`):

```typescript
import { forwardClientIp } from "@/lib/utils/forward-client-ip";

authClient.instance.interceptors.request.use(forwardClientIp);
```

**4. Services: wait for the database.** In each `src/prisma/prisma.service.ts`:

```typescript
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { createPrismaConnection, waitForDatabase } from '@tsdevstack/nest-common';
// other imports unchanged

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  // pool, constructor and onModuleDestroy unchanged

  async onModuleInit(): Promise<void> {
    await waitForDatabase(this.pool);
  }
}
```

Optional, auth template: copy `src/auth/normalize-email.ts` and the `auth.service.ts` changes for lowercase emails.

**5. Check `infrastructure.json`:** `npx tsdevstack infra:plan --env <env>` validates it and shows what changes. Read "Settings that take effect on the next deploy" first.

**6. Full `kong.custom.yml` only:** add the global plugin ([Kong Customization](/docs/customization/kong-customization.md#escape-hatch-with-kongcustomyml)):

```yaml
plugins:
  - name: tsdevstack-client-ip
    config:
      frontend_token: ${KONG_FRONTEND_TOKEN}
      client_ip_header: X-Tsdevstack-Client-IP
      token_header: X-Tsdevstack-Frontend-Token
      real_ip_header: X-Tsdevstack-Real-IP
```

**7. Deploy:** `npx tsdevstack infra:deploy --env <env>`. On GCP, add the printed AAAA records at your registrar. Point log-based alerts at the `http.*` fields.

**8. Clean up (optional):** `docker image rm tsdevstack-kong:latest` if your project has another name; on GCP, disable the Cloud Functions, Cloud Build and Serverless VPC Access APIs.

New projects get all of this from the templates.
