v0.12.0
Released: November 6, 2026
This release is about seeing what your app does in the cloud. One command reads the logs of every part of an environment. Every request is one trace, from the Next.js frontend through Kong to each service and database query. Each environment gets a dashboard in its cloud's console. It all works the same on GCP, AWS and Azure, with no setup. The Terraform providers move to their latest majors, and background work starts the moment it's queued, on every cloud.
Cloud logs
infra:logs reads a deployed environment's logs from your terminal.
- Every source: NestJS services, workers, Next.js apps, Kong, migration runs, scheduled jobs, and the load balancer and WAF in front of them.
- The source list: without a source name, the command lists the sources and when each one last logged.
- One timeline:
--allmerges every source. Each line is taggedapp(your code),platform(deploys, starts, stops, crashes, health checks) oredge(requests and WAF blocks). - Filters:
--since,--until,--level,--kind,--grepand--json. Times are UTC. - No line limit. Lines print as they arrive, and the filters narrow them down.
- Log level per service:
logLevelininfrastructure.json, defaultinfo.LOG_LEVELin your secrets stays local.
Per provider:
- GCP: Google allows 60 log reads per minute per project. When a read reaches that limit, the command keeps the lines it printed and gives you a Logs Explorer link with the same query. The deploy account gets the Logs Viewer role.
- AWS: platform lines come from ECS service events and stopped tasks, with the reason and exit code. Each scheduled job run logs its start, status and duration. The WAF log leaves out the
Authorization,Cookieand frontend token headers. - Azure: Container Apps log to resource-specific tables, and Kong and the Next.js apps add their App Service platform logs. Scheduled job runs log the response and its status.
The MCP server (cli-mcp 0.10.0) has a new tool, get_logs. It returns the newest 200 lines of the window and says what it left out, so an agent narrows the query instead of guessing. Logs can hold personal data, so the tool tells the agent to ask you before every read. get_secret does the same, and the secrets resources (tsdevstack://secrets/user, tsdevstack://secrets/names) show key names only. get_secret also reads a service's scope (service). See Cloud Logs.
Traces
Every request is one trace:
- Next.js frontend: its server spans and its calls to Kong.
- Kong: the request and the call to the service.
- Services and workers: the request, calls to other services, BullMQ jobs and messages.
- Database: each Prisma query, with the SQL and its
$1placeholders, never the values.
A queued job and a message handler run in the trace of the request that started them.
Traces go to each cloud's own service: Cloud Trace on GCP, X-Ray through CloudWatch Transaction Search on AWS, Application Insights on Azure. Locally they go to Jaeger. Each service signs in with its own cloud identity, so there are no keys or connection strings to manage. Kong sends its spans to a collector container that runs beside it and keeps its memory under 200 MiB.
- Logs and traces together: every line a service logs inside a request carries
trace_id.infra:logs --grep <trace id>finds the request's lines in every service. The GCP console links logs and traces both ways, and AWS lines carryxray_trace_id. - Sampling:
tracing.sampleRateper environment, default 1 (every trace). Kong decides for each request, and the services follow. - Your trace IDs stay yours: Kong drops trace headers from outside clients and keeps the ones from your frontend. The frontend starts a new trace for each browser request.
- Quiet probes: health checks and
/metricsget no spans, in Kong and the Next.js apps too.
The new @tsdevstack/telemetry package does the export for nest-common and the Next.js frontends. See Traces and Dashboards.
infra:deploy turns on Transaction Search for the AWS account. It changes how X-Ray bills every app in the account, and it stays on after infra:destroy.
At high traffic, traces cost money, and on Azure from moderate traffic on. Set tracing.sampleRate to keep a share of them. The cost pages show the numbers for GCP, AWS and Azure.
Dashboards
Each environment gets a dashboard in its cloud's console: Cloud Monitoring on GCP, CloudWatch on AWS, an Azure Monitor workbook on Azure. infra:deploy creates it and prints its link. The panels cover the services, Kong and Next.js, the database, Redis, the edge and the error lines.
Each database records statistics per query:
- GCP: Cloud SQL Query Insights.
- AWS: Database Insights, with Performance Insights on for 7 days (free).
- Azure: Query Store on General Purpose and Memory Optimized tiers, and the enhanced Postgres metrics on every tier. Microsoft advises against Query Store on Burstable tiers, so the workbook says why it's off there.
AWS: Container Insights is off. The dashboard uses the free ECS metrics, and crashes and restarts show in infra:logs. By AWS's pricing example, that saves a small environment about $46-49 a month.
Background work
Queue processors and message handlers run in workers, which always run. Work starts the moment it's queued, on every cloud.
- Workers scale on CPU between
minInstancesandmaxInstances: at 70% on AWS and Azure, at Cloud Run's 60% on GCP. - The auth template comes with
auth-worker, which sends the emails. npm run devstarts the workers next to the services, with their output in the same terminal.- GCP: NestJS services with
minInstances1 or more run with CPU always on, so their own background work runs between requests too. At the default size (1 vCPU, 512 MiB) that is about $50 a month per service instead of about $10. SetminInstances: 0to keep request-based billing and scale to zero. - Azure: services run one instance by default (
minInstances1). Set 0 to scale to zero.
Messaging:
- A handler that runs long keeps its messages, so another instance doesn't run them a second time.
- A stream keeps each message until every subscriber group has handled it.
- A service deletes its consumer group on a topic it doesn't subscribe to anymore, about 15 minutes after the deploy.
Terraform providers
- AWS 6, Google 8 and azurerm 5. Azure adds the azapi provider for the traces rule.
- Provider upgrades are explicit.
infra:planandinfra:deploystop when the lock file is older than the new versions, and name the command to run:infra:plan --upgrade. You review the plan and commit the lock file, and CI uses it. - AWS: the scheduled job function runs on Node.js 24.
- GCP: the load balancer stays the Classic Application Load Balancer.
Local development
- Jaeger 2, Grafana 13 and Prometheus 3.
- Kong sends its spans to Jaeger too, so a request is one trace locally, Kong included.
- The Grafana dashboard sits in a
tsdevstackfolder that you can view without logging in.
Improvements
- Azure: PostgreSQL accepts the current generation:
GP_Standard_D2ds_v5,GP_Standard_D4ds_v5andMO_Standard_E4ds_v5. Microsoft grants region access per size series, so a new subscription may get one generation and not the other. Azure account setup shows how to ask for it. - Azure: Front Door checks the Next.js apps on
/health, the same path App Service checks. - AWS: Kong's task has 1 GiB by default, room for Kong and its collector. That is about $1.60 a month more per task.
- AWS: migration logs are kept 30 days, like the service logs. Groups from earlier versions pick this up on their next migration.
infra:deployends with the dashboard link and, on Azure, the name servers of the DNS zone.- Next.js frontends log a failed API call in one line: method, path, status and message. Request bodies and headers stay out of the logs.
- Email links (confirm email, reset password) carry the token after
#. Browsers never send that part to a server, so the token stays out of every log, from the load balancer to Kong. Links already sent keep working. - Log redaction covers more names by default:
accessToken,refreshToken,newPassword,currentPassword,passwordHash,privateKey, and the request headers that carry credentials (authorization,cookie,x-api-keyand the framework's own). The observability page lists them and how deep they match. - Workers log and trace under their own name (
auth-worker). - nest-common:
ObservabilityModule.forRoot()works like the plain import, with one request line and one metric per request, and itslogging,metricsandhealthoptions turn each part off. - Agent skills cover workers, messaging, traces, safe error logging in the frontend, and the Azure PostgreSQL sizes.
@tsdevstack/react-bot-detection: phone and pen users count as real pointer activity, and pasting or autofilling a one-field form (forgot password) goes through.- Dependencies: follow-redirects 1.16.2, and
@azure/identity4.14.1 with msal-node 7.npm auditshows no high or critical findings.
Breaking changes
Pre-1.0, so they ship in a minor version. The upgrade steps below cover each.
- Terraform lock file: existing environments run
infra:plan --upgradeonce. - nest-common:
TracingInterceptoris removed.createAppadds the request span as middleware. kong.user.yml: a plugin namedopentelemetryclashes with the framework's.- Queue processors run in workers only.
- Messaging:
maxLencaps a topic only while it has no subscriber, and the dead-letter stream. Before you remove a service that subscribes to topics, remove its subscriptions and deploy it. - GCP: NestJS services with
minInstances1 or more bill per instance, and acpubelow 1 fails validation for them (Google's rule for always-on CPU). - Azure: services default to
minInstances1, and Next.js apps answer/healthwith 200. - Permissions: GCP projects rerun
infra:bootstrap. The Azure deploy service principal assigns Monitoring Metrics Publisher. The AWS CI role's policy includesxray:*. @tsdevstack/react-bot-detection:handleFormSubmitandanalyzeBehaviortake{ fieldCount }. The mouse-pattern rule is gone, and the reason "No natural mouse movement detected" reads "No natural pointer activity detected".
Upgrading an existing project
1. Packages.
Run the second command for each NestJS service. If your code uses TracingInterceptor, delete it: the request span is already there.
2. Workers. Move queue processors out of AppModule into a worker. For an auth service from the template, copy src/worker.ts, src/worker.module.ts and tsconfig.worker.json from the template, delete src/processors/processors.module.ts and its import in AppModule, and add the scripts to the service's package.json:
Then register the worker:
In the root package.json, change dev so it starts the workers too:
3. Next.js frontends. For a frontend from the template:
Add instrumentation.ts next to package.json:
Add the trace interceptor to each Kong client, after forwardClientIp:
Copy lib/utils/describe-error.ts and lib/utils/forward-client-ip.ts from the template. Log describeError(error) instead of the error object in handleApiError and in the console.error calls under app/api.
For the email links, copy lib/utils/take-link-token.ts, app/(public)/confirm/confirm-client.tsx and app/(public)/reset-password/reset-password-client.tsx from the template. The pages read the token after # and from ?token=. Then, in an auth service from the template, change the three links in src/auth/auth.service.ts to /confirm#token= and /reset-password#token=. Ship the frontend first, or both in the same deploy.
On Azure, a Next.js app answers /health with 200. The template has the route (app/health/route.ts).
4. kong.user.yml. If it has a plugin named opentelemetry, delete it. Kong's tracing comes from the framework.
5. Local stack.
This moves the local stack to Jaeger 2, Grafana 13 and Prometheus 3 and turns on Kong's spans. To view the Grafana dashboard without logging in, delete ./data/grafana once.
6. Cloud accounts.
- GCP: run
npx tsdevstack infra:bootstrap --env <env>. It adds the Logs Viewer and Monitoring Dashboard Editor roles and turns on the trace, logging and monitoring APIs. - AWS: if you deploy from CI, add
xray:*to theCloudWatchLogsAndMetricsstatement of the role's policy. See AWS CI/CD setup. - Azure: add Monitoring Metrics Publisher to the roles the deploy service principal may assign (resource group > Access control > Role assignments > View/Edit in the Condition column). See Azure account setup.
7. Each cloud environment. First run a clean infra:plan with your current CLI version. Then:
Review the plan, then commit the updated .terraform.lock.hcl before you deploy. On Azure, Kong's App Service moves to two containers (Kong and its collector) with this deploy, and infra:logs reads the lines written after it.
New projects get all of this from the start.