---
title: "v1.7.5"
description: "Migration tooling that meets real databases, gateway and `robots.txt` hardening, builds without a database, and a pnpm pin."
requested_language: cs
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/cs/changelog/v1.7.5
version: changelog
docs_index: https://docs.systhema.app/cs/llms.txt
---
> This page isn't translated yet. Showing English.


## ✨ Highlights

1.7.5 is the batch that came out of consumer projects upgrading to 1.7.4 and reporting back. Most of it is the upgrade and migration tooling meeting real databases: `systhema migrate --schema` stops blocking on three false positives, the General Settings data hooks create and repair their columns the way Payload's own schema would, and the header rename hook works on PostgreSQL again. Around that are two hardening fixes for the gateway and `robots.txt`, a build mode for hosts whose database is only reachable at runtime, and a pnpm pin so a project's security overrides and the Lexical admin patch are never silently dropped.

### Build without a database

`SYSTHEMA_PRERENDER=off` (`false` and `0` also work) lets `next build` succeed on hosts where the database is only reachable over a private network, such as Railway's `*.railway.internal`. `generateSysthemaStaticParams` returns no params without starting Payload, so pages render on their first request and stay ISR-cached for the route's `revalidate`. The managed catch-all `page.tsx` now passes a `getPayload` getter instead of a promise, because an eager `getPayload()` started connecting before the flag was read; `systhema upgrade` refreshes an unedited `page.tsx`, and a project that patched the file to return `[]` gets `page.tsx.new` and can move to the env var. Verified on Railway against a real consumer project: the build makes no database connection, and the runtime reaches PostgreSQL over the private network (#253).

### `robots.txt` names only your own hosts

The scaffolded `robots.txt` route built its `Sitemap:` line from the client-controlled `Host`, `X-Forwarded-Host` and `X-Forwarded-Proto` headers and sent it with `Cache-Control: public, s-maxage=3600`, so one forged request could point every crawler at another host's sitemap for an hour. The route now calls `resolveSysthemaPublicOrigin` from the new `@systhemaui/next/origin` entry, which accepts a forwarded or `Host` value only when it names `NEXT_PUBLIC_SERVER_URL` or one of the project's locale domains, never reads `X-Forwarded-Proto`, and falls back to the canonical origin otherwise. `getSysthemaPublicOrigins()` lists the allowed origins. The `robots-host-allowlist` codemod moves existing projects over (#246).

### Honest statuses for unknown paths

A cached 404 was revalidated into `304 Not Modified`: Next hashes every non-dynamic payload into an `ETag` without looking at the status, and the catch-all's `notFound()` render is an ISR entry with status 404. The gateway now drops `If-None-Match` from page requests, so the origin answers 404 again; responses keep their `ETag`, so Cloudflare keeps caching pages and answering conditional requests at the edge, and `public/` files keep their 304s. An anonymous request for an Admin file probe such as `/admin/.env` got Payload's 200 page because `AuthProvider` renders nothing for a logged-out SSR; a dotted Admin path without a Payload session cookie is now rewritten to the site's 404. Every Payload project gets both through its generated `src/proxy.ts` (#247).

### `systhema migrate --schema` stops blocking on false positives

Three states real projects were stuck in (#249, #259):

- **A column that differs only in its default** is planned as `ALTER TABLE … ALTER COLUMN … SET DEFAULT` or `DROP DEFAULT`. It rewrites no row, so a plugin release that moves a default (`@payloadcms/plugin-import-export` 3.90 did, on `exports` and `imports`) no longer needs a hand-written migration. Defaults PostgreSQL writes back in another form (`jsonb` key order, casts, parentheses) are compared by value, so they are not re-planned on every run.
- **Index names past PostgreSQL's identifier limit** (63 bytes by default), which Payload generates for deeply nested arrays, are matched by the truncated name PostgreSQL actually stored instead of blocking on every run. Names that collide after truncation still block.
- **A label added to several enums at once**, as a new locale is to `enum__locales` and each per-locale publishing enum, no longer trips the same-transaction safety check on its own `ADD VALUE`, and a new enum that merely declares the label does not block either. The guard also catches enum-array defaults and explicit casts to the extended enum, which used to slip through and fail mid-run with `55P04`.

### General Settings hooks that match Payload's schema

The SEO and Emails General Settings data hooks (`migrate-seo-data`, `migrate-emails-data`) added bare nullable `varchar` and `integer` columns, and because `systhema upgrade` runs them before any schema addition, `migrate --schema` then blocked on `seo_title_separator` and `seo_title_template`, and a SQLite push rebuilt `general_settings`. The hooks now add each column with the definition Payload's schema gives the field: its default, `NOT NULL` when required, the PostgreSQL enum of a radio or select, and the SQLite upload foreign key. On PostgreSQL they also repair the bare columns an earlier release's hook left behind, bringing each one to its default, `NOT NULL` and, when that is safe, its type. And they write into the settings row that exists instead of assuming `id = 1`: PostgreSQL projects with UUID IDs failed with `column "id" is of type uuid but expression is of type integer`, and SQLite UUID projects got a second row keyed `"1"` that Payload never read. A missing row is created with the copied values and the ID Payload would give it (#252, #258, #260).

### Pin pnpm, or lose your overrides

pnpm 11 and later print one warning and then ignore the `pnpm` field of `package.json`, where every Systhema scaffold keeps its security overrides, `onlyBuiltDependencies` and the Lexical admin patch. A project installed with pnpm 11 or 12 got vulnerable transitive versions and no patch. `systhema create` now pins new pnpm projects to `pnpm@10.34.6` through `packageManager`, which pnpm 11+ honour by switching, and the new fast `package-manager-pin` doctor check warns on an existing project with no exact pin or with a pnpm that cannot install its `pnpm-lock.yaml` (the `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` a build host prints). `systhema doctor --fix` writes a pnpm version that fits. New projects also get `pnpm.overrides` floors for `nanoid` 3.3.18 and `js-yaml` 4.3.2, and the `fast-uri` floor rises to 3.1.8 (#251, #255).

### A customised `src/proxy.ts` survives the upgrade

`create-app-files` overwrote a customised `src/proxy.ts` in a project without a managed-files ledger entry for it, such as one upgrading from before 1.7.1, so its rate limiting, CSP and security headers survived only in a one-time `.bak`. A proxy matching one Systhema has shipped is still refreshed; any other keeps its bytes and the current template is written to `src/proxy.ts.new`. Disabling Next-only locales keeps a customised proxy the same way, `systhema upgrade` replays the `Preserved` lines from its `sync` step, and the new report-only `replaced-proxy` doctor check warns when `src/proxy.ts` is a stock proxy while `src/proxy.ts.bak` matches no proxy Systhema shipped, which is the trace an earlier upgrade leaves (#248).

---

## ⬆️ Upgrade

```bash
pnpm add -g @systhemaui/cli@latest
cd <your-project>
systhema upgrade --dry-run
systhema upgrade --yes --allow-database
```

One codemod runs: `robots-host-allowlist` rewrites `src/app/robots.txt/route.ts` when it matches a version Systhema shipped, ignoring whitespace. A customised route that still reads the host headers is skipped and reported; switch it to `resolveSysthemaPublicOrigin` from `@systhemaui/next/origin` yourself.

**Re-run the General Settings hooks on PostgreSQL** if a 1.7.1 to 1.7.4 upgrade left bare `seo_*` or `emails_*` columns that block `systhema migrate --schema`. `systhema upgrade --allow-database` runs them again; so does `systhema migrate migrate-seo-data` and `systhema migrate migrate-emails-data`. A re-run repairs the bare columns and does nothing when none are left.

**Re-run `systhema migrate migrate-header-nav-dbnames` on PostgreSQL** if it crashed with `Cannot read properties of undefined (reading '0')`. It renamed nothing in that state, so the re-run does the whole job.

**Pin pnpm.** Run `systhema doctor` and let `--fix` write `packageManager`, or add `"packageManager": "pnpm@10.34.6"` to `package.json` yourself. Build hosts that follow `packageManager` (Railway, Coolify, Vercel, Corepack) switch to it. The new security floors reach new projects only; copy the `nanoid`, `js-yaml` and `fast-uri` entries from `scripts/security-overrides.json` into your `pnpm.overrides` and run `pnpm install`.

**If your build host cannot reach the database**, set `SYSTHEMA_PRERENDER=off` in the build environment. The first visitor to each page after a deploy waits for a server render; publishing still revalidates pages as before.

**If you had patched `src/app/(site)/[[...segments]]/page.tsx`** to skip the build-time query, the upgrade writes `page.tsx.new`; take the managed file and set the env var instead.

---

## ⚠️ Breaking & Behavioral Changes

### 1. `robots.txt` no longer reflects a forwarded host

The route's `Sitemap:` line names the origin of `NEXT_PUBLIC_SERVER_URL` or one of the project's locale domains, whichever the `X-Forwarded-Host` or `Host` header matches; any other value, and any `X-Forwarded-Proto`, is ignored. A tunnel that sets `X-Forwarded-Host` to an internal name falls through to an allowed `Host`. `systhema upgrade` rewrites a shipped route; a customised one is reported (#246).

### 2. The gateway answers pages without 304s and 404s Admin file probes

Page requests lose their `If-None-Match` before reaching Next, so the origin never answers a cached 404 with 304. The cost is that an expired edge copy fetches the full page instead of a 304; `/api`, `/_next`, `/sys`, `/trpc` and `/_vercel` keep their conditional requests, and `public/` files keep `If-Modified-Since`. Anonymous requests for an Admin path with a dot in a segment (`/admin/.env`, `/admin/wp-login.php`) get the site's 404; logged-in requests still reach Payload, so a document ID containing a dot keeps working (#247).

### 3. `create-app-files` keeps an unknown `src/proxy.ts`

A pre-ledger `src/proxy.ts` that matches no proxy Systhema shipped is no longer replaced; the template goes to `src/proxy.ts.new` and the run logs `Preserved src/proxy.ts`. A stock proxy with a digest missing from the list gets `.new` too instead of a refresh. Files under `(systhema)` keep the `.bak` refresh. Disabling Next-only locales deletes `src/proxy.ts` only when its digest is known; any other existing proxy still stops the locale migration (#248).

### 4. New pnpm projects declare `packageManager`

`systhema create` writes `"packageManager": "pnpm@10.34.6"`. `--pm npm` and `--pm yarn` projects do not get it, because Corepack refuses to run another package manager in a project that names pnpm. The bundled templates take the pin from the monorepo's own `package.json`, and the CLI build fails if that pin ever moves to pnpm 11 or later (#255).

### 5. The General Settings hooks write typed columns and the existing row

Columns the SEO and Emails hooks add carry Payload's default, `NOT NULL` when the field is required, the enum of a radio or select, and the SQLite foreign key of an upload. An existing `general_settings` row takes those defaults, the same as a push would set them, and a value equal to the column default no longer counts as data that stops the legacy copy. On PostgreSQL a re-run repairs bare columns from an earlier hook: type and default change in one statement, so a stored value the enum rejects changes nothing and is logged; a non-enum type change happens only while the column holds no values. The hooks write into the row that exists (`ORDER BY id LIMIT 1`) and create one with the copied values when there is none, using a serial, `1` for a custom number ID, a UUID for a custom text ID or `idType: 'uuid'`, and a version 7 UUID for `uuidv7`. A row that can neither be read nor created fails the hook instead of writing locale rows with `_parent_id = NULL` (#252, #258, #260).

### 6. `systhema migrate --schema` plans default changes

A default-only difference used to be a blocker that needed a project-owned migration. It is now planned as `SET DEFAULT` or `DROP DEFAULT`, appended after the generated additive DDL and before enum additions, so the same-transaction enum-label check still covers a default that names a newly added label. Type, nullability, identity, generated and serial differences block as before (#249).

---

## 🐛 Fixes & Internal Improvements

### Bug Fixes

- **`migrate-header-nav-dbnames` works on PostgreSQL** — the rename engine read the identifier limit with the `{ rows }` shape while its client contract returns the rows array, so every PostgreSQL project crashed before the first rename. A contract-shaped test now drives the PostgreSQL path (#250)
- **Settings hooks on UUID projects** — PostgreSQL projects with UUID IDs failed the hook, and SQLite UUID projects got a second settings row keyed `"1"` (#260)
- **Bare hook columns are repaired** — a database where an earlier hook added nullable, default-less columns no longer blocks `migrate --schema` after a re-run (#258)
- **A new locale no longer blocks `migrate --schema`** on a project with localized drafts, and enum-array defaults and casts to a freshly extended enum block instead of failing mid-run (#259)
- **Over-long Payload index names** are matched by their truncated name instead of blocking on every run (#249)
- **Turning Next-only locales off keeps a customised proxy**, and `systhema upgrade` shows the `Preserved` lines from its `sync` step (#248)
- **`systhema create` installs with the pinned pnpm**, or pins the pnpm that cannot switch (#255)

### Internal / Monorepo

- **The payload typecheck covers `src/scripts/`** — the drizzle snapshot in `schemaMigration.ts` was silently typed `any`, which is how the rename-engine mismatch shipped; 53 type-only fixes (#256)
- **No more exit 13 in the payload tests on Node 22** — a resolve hook sends each Lexical `*.node.mjs` shim straight to the build it would have loaded, so the module graph has no unsettled top-level await (19 of 150 runs failed before, 0 after) (#254)
- **The General Settings hook tests stay off the schema-migration advisory lock**, which is keyed per database while CI shares one database across parallel test files (#252)
- **Two new doctor checks**, `package-manager-pin` (fast, `--fix`) and `replaced-proxy` (report-only) (#251, #248)
- **`CLAUDE.md` pins the robots template to the `robots-host-allowlist` codemod**; the 1.7.0 `host-aware-robots` codemod stays frozen (#257)

---

## List of all changes

### 🚀 Features

#### cli

- feat(cli): add a pnpm pin doctor check and new advisory floors (#251) (35e709a0)

#### payload

- feat(payload): build without database access (#253) (0354796d)

### 🐛 Bug fixes

#### cli

- fix(cli): pin new pnpm projects to pnpm 10 (#255) (a52a0ecc)

#### core

- fix(core): keep a customised src/proxy.ts that has no ledger entry (#248) (b62d0016)

#### next

- fix(next): return honest statuses for cached 404s and admin file probes (#247) (3f0a1cc6)

#### next, cli

- fix(next,cli): only name allowed hosts in the robots.txt sitemap line (#246) (43ae1bae)

#### payload

- fix(payload): stop a label added to several enums from blocking itself (#259) (7bb7f6c2)
- fix(payload): write settings hooks into the existing general settings row (#260) (f80fcc79)
- fix(payload): typecheck the database migration scripts (#256) (ccbbb3d9)
- fix(payload): repair bare columns earlier settings hooks added (#258) (11c93a2c)
- fix(payload): unblock default-only drift and long index names (#249) (5328ac93)
- fix(payload): read the postgres identifier limit as a rows array in the rename engine (#250) (4f1649d4)
- fix(payload): create general settings hook columns with payload's definition (#252) (564b043d)

### 🧪 Tests

#### payload

- test(payload): load lexical builds without the top-level-await shim (#254) (524bd6e3)

### 📚 Docs

#### claude

- docs(claude): pin the robots template to the robots-host-allowlist codemod (#257) (7bf4b73e)
