Changelog: October 2026

This month, we added cancellation support to runs, builds, and deployments. We improved cost reporting to reflect credits, reservations, and savings plans along with faster load times. The Environment Variables screen now shows the real applied values instead of "(generated)", and environments gained descriptions, tags, and drag-to-reorder pipelines. Read on for details.

October 1, 2026
Brad Sickles
Brad Sickles
Co-founder and CEO
Changelog: October 2026

Overview

# Product

- Cancel Runs, Builds, and Deployments
- More Accurate, Faster Cost Reporting
- Real Values on the Environment Variables Screen
- Environment Descriptions, Tags, and Pipeline Order
- One Preview Environment Comment per Pull Request
- IaC Sync Ownership Repair
- Org API Keys That Can Publish Modules
- `workspaces select` Pulls the Effective Config
- Mobile Layout
- Fixes & Smaller Improvements

## Cancel Runs, Builds, and Deployments

You can now stop a run, build, or deployment that's already underway, such as an apply started against the wrong environment or a build of the wrong commit.
Click **Cancel** on any in-progress workflow, or cancel it from the CLI:

```
nullstone workflows list --active
nullstone workflows cancel <id>
```

Cancel is available to anyone who can start the workflow, and it shuts things down cleanly:

- **Queued or waiting work stops right away.** A run waiting on dependencies, on approval, or behind another run is cancelled immediately, and the next run in line starts
- **Terraform/OpenTofu gets to finish safely.** Nullstone lets the resource change in progress complete and releases the state lock, so the workspace isn't left locked. If the process hasn't stopped after 5 minutes, Nullstone stops it and unlocks the state for you
- **Docker builds and pushes** stop the same way
- **Deployments that are already rolling out** stop being watched and are marked cancelled. Nullstone doesn't roll back a rollout your cloud provider has already started, and the workflow tells you it may still finish

While a workflow shuts down it shows **Cancelling**, then **Cancelled**.
Cancelling one app or block in an environment-wide workflow stops only that part.
Builds that run in GitHub Actions can't be cancelled from Nullstone yet.

## More Accurate, Faster Cost Reporting

Cost reporting now reflects what you actually pay, including credits, reservations, and savings plans, and cost pages load faster.

What's new:

- **Reservations and savings plans are spread over the usage they cover.** By default, costs now show amortized spend: an upfront reservation or savings plan purchase no longer shows up as a one-month spike, and each block that benefits carries its share. Switch to **billed cost** when you want to match the invoice line by line
- **Credits and refunds count.** They show as negative spend on the billed chart instead of being left out. A switch hides negligible line items
- **Faster load times.** Cost pages no longer query your cloud provider on every visit; Nullstone collects cost data daily. Each view shows when it was last updated and has a button to refresh it
- **History beyond the current month.** AWS fills in the last 13 months automatically. GCP history comes from the BigQuery billing export you already connected
- **Totals match your bill.** Spend that can't be traced to a block still appears as **Unmanaged** instead of being hidden or spread across blocks

AWS and GCP costs now use the same definitions, based on the FinOps [FOCUS](https://focus.finops.org/) standard, so the numbers mean the same thing on both clouds.

### On-demand price and discounts on AWS

To compare what you pay with what the same usage would cost at on-demand prices, connect an AWS **Data Export** (FOCUS 1.2) to your cost provider.
Once the export starts delivering, Nullstone adds **List cost** (on-demand price) and **Contracted cost** (after reservations, savings plans, and negotiated rates), down to the individual resource.
This is optional. Cost providers without an export keep working as they do today.

[Set up AWS cost reporting](https://docs.nullstone.io/integrations/cloud-providers/aws.html)

## Real Values on the Environment Variables Screen

In August, we redesigned the Environment Variables screen. It still showed **(generated)** for most values injected by modules and capabilities, and it left out platform built-ins like `GOOGLE_*` entirely.
It now shows the value your app actually received.

What's new:

- **Applied values.** Each variable shows the value from the last infrastructure apply. Values added by a deploy show as their own layer, including `NULLSTONE_VERSION`, `NULLSTONE_COMMIT_SHA`, `OTEL_RESOURCE_ATTRIBUTES`, and any `--env-var` you passed. The next apply clears that layer
- **A reason instead of a placeholder.** When there is no value to show, the row says why: not launched yet, set on the next infra update, secret (key only), runtime reference, or the module doesn't report its values
- **Pending changes.** If you've changed a variable since the last apply, the row shows the applied value and **→ pending** with the new value
- **Secret references.** For a secret, the row shows where it lives (secret ARN, GCP secret id, or Kubernetes secret name), never the value

This works for environments that are already applied, with no module upgrade.
Custom app modules that don't use `ns_env_variables` in the standard way show "module doesn't report env values" until they emit the new `ns_platform_data` record.

## Environment Descriptions, Tags, and Pipeline Order

Environments can now carry a **description** and a set of **tags**, and you can edit both on the environment's settings page.
Tags are free-form `key=value` pairs, and each key is set or cleared on its own, so two people editing different tags never overwrite each other.

Tags are also filters in the CLI, building on last month's CI pipeline flags:

```
nullstone envs list --stack=<stack> --tag claim=alice --type=preview
nullstone envs list --stack=<stack> --tag claim=
```

An empty value (`--tag claim=`) matches environments where the tag is missing or blank. That makes it easy to find untagged preview environments.
`envs list` also gained `--status`, `--prod` / `--non-prod`, and `--name`.

**Reorder the pipeline.** Pipeline environments on the stack's Environments page can now be dragged into a new order, with left/right arrows as a keyboard alternative.
Before this, the only fix for a stack where `prod` was created before `staging` was to delete and recreate environments.
Apps set to auto-deploy to the first environment follow whichever environment is first after the move.

[CLI reference](https://docs.nullstone.io/getting-started/cli/docs.html)

## One Preview Environment Comment per Pull Request

Previously, a pull request with a preview environment collected a new Nullstone comment for every launch, sync, and destroy.
Each preview environment now gets **one comment**, and Nullstone edits it in place as the environment moves through its lifecycle.

- A status headline at the top (launching, launched, failed, destroying, destroyed)
- Once the environment is launched, the public URLs of **every app** in it, so a reviewer or an AI agent can go from the PR straight to the running preview
- On failure, the error, and a link to the environment in Nullstone
- A footer with the time of the last update and the commit it reflects
- Per-app detail collapsed underneath

New commits update the same comment, and a deleted comment is recreated on the next update.

The CLI can also change which branch or pull request a preview environment's apps track, selecting apps by repository:

```
nullstone envs apps list --stack=<stack> --env=<env>
nullstone envs apps set --stack=<stack> --env=<env> --repo=acme/widgets --branch=feat/new-ui
```

## IaC Sync Ownership Repair

IaC sync records which repository owns each block and each env event, and rejects a sync from any other repository.
If that ownership was recorded wrong or went stale, the only fix used to be a support request.

What's new:

- **Edit ownership yourself.** The block settings page and the env event edit page each have an **IaC ownership** section for setting or clearing the owning repository. Stack architects and owners can use it
- **A hint when a sync fails on ownership.** A failed sync in the UI or `nullstone iac sync --wait` links straight to the settings page of each conflicting block or event
- **`nullstone iac sync --repo=<owner/name>`** sets the repository for detached or CI checkouts. The CLI now also detects the repository from SSH host-alias remotes (`github-work:owner/repo.git`), `ssh://`, and `http://` origins. Without a repository the command fails with a clear error. Previously it synced with a blank owner, and every owned block showed up as a conflict
- **A failed sync now reports as failed.** A Sync Repo (or env up, block apply, and similar) whose planning step failed used to show a green **completed** with "No changes". It now fails with the real error. The block that caused an ownership conflict is marked failed, and only the bystanders are marked cancelled
- **CLI-triggered syncs show their repository** in the activity views instead of "no repo"

## Org API Keys That Can Publish Modules

Organization API keys can now have an **organization role**: Member (the default, unchanged) or **Architect**.
An Architect key can run `nullstone modules register` and `nullstone modules publish` from CI, which previously failed with a bare `Forbidden`.
Only an org architect or admin can grant the Architect role. A key can never be given Admin, and a key can never create or modify other API keys.

Organization API keys are now documented, and the GitHub Actions and CircleCI guides point to them instead of personal keys.

## `workspaces select` Pulls the Effective Config

When you develop a module locally, `nullstone workspaces select` now pulls the **effective** config by default. That is the last applied config plus any changes queued in the UI but not yet applied, which is almost always what you are about to test.

```
# effective (default)
nullstone workspaces select --stack=<stack> --block=<block> --env=<env>
# last applied
nullstone workspaces select --stack=<stack> --block=<block> --env=<env> --config=latest
# last run
nullstone workspaces select --stack=<stack> --block=<block> --env=<env> --config=current
```

`.nullstone/active-workspace.yml` now includes every resolved connection, for the workspace and each of its capabilities, so the local Terraform provider and the selected config always agree.
Survey prompts also name the capability each connection belongs to.
A workspace that has never been run now gets a fully populated config instead of an empty one.

## Mobile Layout

The Nullstone UI is now usable on a phone.
The sidebar was hover-only, which made navigation unreachable on touch screens. It now opens as a slide-out drawer from a menu button below tablet width.

- The header, page padding, and breadcrumbs reflow for narrow screens. Breadcrumbs collapse step by step, and their pickers open full width
- Dialogs size to the screen, tooltips open on tap, and tabs scroll sideways instead of overflowing
- Buttons, select items, and tabs get larger touch targets on small screens
- Charts stack into one column, and log panes size to the screen

Desktop is unchanged: every mobile change applies only at small widths.
Wide tables and larger touch targets on the remaining controls come in a follow-up pass.

## Fixes & Smaller Improvements

- **Permission changes take effect without logging out.** After an admin changed your org or stack membership, the UI kept showing your old permissions until you logged out and back in. The refreshed session was being dropped on most responses. The UI now picks up role changes as soon as they happen
- **Concise plan diff pretty-prints structured values.** In last month's Infra Activity plan diff, objects and arrays such as `labels`, `metadata`, and cloud-init `user-data` now render indented, with real line breaks, instead of as one minified line
- **`nullstone iac test` shows type-only changes.** A number variable stored as the string `"30"` used to print `30 => 30`. It now prints `"30" => 30`. The root cause is fixed too: `--var` values from the CLI are now converted to the variable's declared type, and invalid values are rejected
- **Env Variables screen:** long values no longer cover the edit and delete buttons
- **State History:** a long trigger badge no longer breaks the row layout
- **Faster alerts on platform-side failures.** Every failed run, build, and deploy is now classified as caused by your configuration or by Nullstone. Anything on our side alerts our team within minutes, instead of blending in with ordinary deploy failures

# Catalog

### GCE Load Balancers

`gcp-gce-server` now owns everything that has to reference its instance group: health checks, backend services, and forwarding rules. Load balancer capabilities describe what they need through a typed `load_balancers` spec.
This fixes a failure mode where a load balancer's target pool was recreated outside Nullstone and the server silently lost all its backends.

- **`gcp-gce-server` 0.1.0** supports `tcp` load balancers: external or internal passthrough, or a global TCP proxy with optional PROXY protocol. It also adds `liveness_port` for TCP liveness checks with instance-group auto-healing
- **`gcp-gce-tcp-load-balancer` 0.1.0** emits the tcp spec. It gains `internal`, `global`, `proxy_protocol`, and a structured `health_check`, and keeps its existing IP address
- **Port translation on the VM.** A new `server_port` lets the load balancer accept traffic on `service_port` and deliver it to a different port on the VM. Example: SFTP on port 22 while Container-Optimized OS keeps port 22 for IAP/OS Login SSH. The client's source IP is preserved

**Upgrade both together.** `gcp-gce-server` 0.1.0 and `gcp-gce-tcp-load-balancer` 0.1.0 must be applied as a pair. The upgrade recreates the forwarding rule on the same address, which causes roughly 30–60 seconds of refused connections on `service_port`. If the first apply reports the address is already in use, apply again.

- [GCE Server](https://app.nullstone.io/registry/modules/nullstone/gcp-gce-server)
- [GCE TCP Load Balancer](https://app.nullstone.io/registry/modules/nullstone/gcp-gce-tcp-load-balancer)

### TLS 1.2 Minimum on GCP Load Balancers

`gcp-gke-gateway`, `gcp-gke-dedicated-load-balancer` (when `enable_https` is on), and `gcp-gcs-cdn` now attach an SSL policy with `min_tls_version = TLS_1_2` and the `RESTRICTED` profile.
The policy is updated in place, so IPs and DNS don't change.
Clients still on TLS 1.0 or 1.1 will fail the handshake after the next apply. This is intentional.

- [GKE Gateway](https://app.nullstone.io/registry/modules/nullstone/gcp-gke-gateway)
- [GKE Dedicated Load Balancer](https://app.nullstone.io/registry/modules/nullstone/gcp-gke-dedicated-load-balancer)
- [GCS CDN](https://app.nullstone.io/registry/modules/nullstone/gcp-gcs-cdn)

### Static Sites on S3 and CloudFront

Three additions for multi-page static sites:

- **`clean_urls`** on `aws-s3-cdn` serves extension-less URLs for sites that build `page.html` files (Astro with `build.format: 'file'`, for example). `mode = "redirect"` (the default) answers `/page` with a `301` to `/page.html`, and `mode = "rewrite"` serves `page.html` silently. Trailing-slash paths always resolve to `index.html`
- **`redirect_www`** on `aws-s3-cdn` sends `www.<domain>` to the apex with a `301`, keeping the path and query string. Previously `enable_www` served identical content on both hosts, and search engines saw it as duplicate content
- **`aws-s3-site`** now revalidates non-hashed files: `.xml`, `.txt`, `.json`, `.webmanifest`, and favicons, not just `.html`. Before, `sitemap-index.xml`, `robots.txt`, and `manifest.json` could be cached for a year. `extra_revalidate_globs` adds your own patterns. Existing sites with `revalidate_html_pages = true` pick up the new caching headers after an apply and a redeploy

`clean_urls` and `redirect_www` are off by default, so existing distributions don't change.

- [S3 CDN](https://app.nullstone.io/registry/modules/nullstone/aws-s3-cdn)
- [S3 Site](https://app.nullstone.io/registry/modules/nullstone/aws-s3-site)

### db-admin Error Alerts on Cloud SQL

`gcp-cloudsql-postgres` and `gcp-cloudsql-mysql` now alert when their db-admin function's 5xx rate crosses `db_admin_error_rate_threshold` (default 10%) over five minutes.
The db-admin function is what creates databases, roles, and grants for your apps, so a failure there otherwise shows up only when an access capability fails to apply.
The alert requires a connected `notification` datastore, and quiet periods never trigger it. `gcp-cloudsql-mysql` also gains the `notification` connection, which it did not have before.

- [Cloud SQL Postgres](https://app.nullstone.io/registry/modules/nullstone/gcp-cloudsql-postgres)
- [Cloud SQL MySQL](https://app.nullstone.io/registry/modules/nullstone/gcp-cloudsql-mysql)