Astro 7 moved `csp` out of `experimental` — but to `security.csp`, not to
the top level, and I put it at the top level. Astro's config schema drops
keys it does not know without a word: the build stayed green, the types
stayed green, and every response went out with no Content-Security-Policy
at all. Only looking at a live response header showed it.
Fixed, and now guarded: tests/config.test.ts reads the config and asserts
the policy sits where Astro actually reads it. Checked that the test goes
red against the broken config before trusting it green against this one.
Verified on a running instance: the header is back, with the same
per-script hashes as before.
actions/checkout and actions/setup-node at v4 target Node 20, which GitHub
runners now force onto Node 24 with a deprecation notice on every run. v5 of
both targets Node 24 natively. No change to what the workflow does.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nh4A79bbqT9dcd2inzYUdg
`IMAGE_API_BASE_URL` decides where the image requests go. The default is still
OpenRouter — one key, many models, and the model list in the admin area fills
itself in — but the request body was already written in the OpenAI image shape,
so pointing it at a LiteLLM gateway, a company proxy or a server of your own
works. `GET /images/models` is an OpenRouter route; a base that answers 404 is
treated as "cannot list models", not as an error, and the model id is typed
instead. `IMAGE_API_KEY` is the new name; `OPENROUTER_API_KEY` is still read so
existing instances keep working.
Also: the seeded example recipes now show four different task combinations
rather than four sizes — clean up, poster, cut out to transparent PNG, sticker
with contour, square for social. They are examples to read and replace.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nh4A79bbqT9dcd2inzYUdg
The critical advisory in the dependency tree (GHSA-26w7-cxv4-gfx2, remote code
execution through AVIF image optimisation) has no fix inside Astro 5, so this
moves to Astro 7.3.2 with @astrojs/node 11 and current sharp. `npm audit` now
reports nothing. The only code the jump needed: `csp` graduated out of
`experimental`, and sharp's `Sharp` type is a named export now.
Also fixed, both found by testing with every module switched on rather than
only the print-only default:
- `src/pages/index.astro` passed `dict` to the studio island without ever
defining it, so the studio page returned 500 on any instance with the AI
module on. The print-only default redirects away from that page, which is
why the earlier run never hit it.
- The print studio fetched `/api/delivery-targets` even with the delivery
module off. The route answered 404 exactly as designed, but it left a red
line in the console of every print-only instance, which reads like a fault.
It now asks only when there is a module to ask, and hides the card otherwise.
Plus: the first-run box sizes itself to its content instead of miscounting
padding, and the README's counts are recounted rather than remembered.
Verified against a live instance on a fresh database, in both module sets:
sign-in, module gating, the German interface, an eight-up passport sheet and
its PDF. 124 tests, type check, build and secret scan all clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nh4A79bbqT9dcd2inzYUdg
- CI pinned Node 20, but `node --test` with type stripping needs 22.6, so the
workflow could never have gone green. CI and `engines` now say 22.6.
- `docker compose up -d` failed on the documented path: the compose files
require DB_PASSWORD and .env.example never mentioned it. Documented, and the
README no longer claims "three secrets".
- The secret scanner treated any comment line as safe, so a pasted `.env`
fragment — the usual way a key gets committed — slipped through. Verified
against planted keys before and after.
- Module pages were not guarded: with KLARBILD_MODULES=print a signed-in user
could still open /shares, /library and /queue and read about features the
instance does not have. They now redirect, matching the API's 404 behaviour.
- German left in the English build: `bild.<ext>` as a download fallback, and
31 t() keys with no German at all — the whole main navigation among them, so
a German instance rendered its nav in English. Dictionary now covers every
key in the source (1,067 entries).
- Third-party names removed from the data as well as the labels: the `theframe`
format id is now `tv169`, and the dead Cricut admin field is gone.
- Dockerfile ignored the committed lockfile; it now copies it and uses `npm ci`.
- Dropped an unused dependency (@aws-sdk/s3-request-presigner — storage.ts
explains why presigned URLs are not needed), a dead `/g/` entry in the
unauthenticated path list, and an undocumented N8N_WEBHOOK_URL that nothing
read. Documented TRUSTED_PROXIES, which the rate limiter does read.
- Migration 004 still had "nas" in its filename; README said A6–A2 where the
list runs A6–A1; two committed screenshots were referenced nowhere.
124 tests green, type check clean, build clean, secret scan clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nh4A79bbqT9dcd2inzYUdg
A self-hosted print studio: images at exact physical sizes, sheet layout with
crop marks, tiling, 1:1 PDF. Everything else — image generation, delivery,
share links, mail, Telegram — is an optional module that is off by default.
This is the neutral public twin of a private tool. Compared to that original:
- optional modules behind KLARBILD_MODULES; the default install is print-only
- English interface with German as a full second language (1,038 strings);
the English sentence is the translation key
- no built-in accounts — first boot creates one admin from ADMIN_USERNAME /
ADMIN_PASSWORD, or generates a password and logs it once
- delivery and backup targets are generic SFTP/FTPS endpoints, off by default
- English routes, English identifiers, English comments throughout
- two compose files: print-only by default, everything in the full one
- AGPL-3.0, CI (build, tests, type check, secret scan), CONTRIBUTING, SECURITY
124 tests green, type check clean, verified end to end against a live instance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nh4A79bbqT9dcd2inzYUdg