Deploy / build (push) Successful in 22s
1) the-upstream-was-deleted-then-came-back-rewritten (devops, pubDate 2026-10-06) docker pull hectorqin/reader -> 404; the repo was re-initialised 2026-09-16 and now carries 233 commits, a TypeScript/Node rewrite, port 5888, SQLite /data, config moved into the admin UI, and an image on cnb.cool with no tags or releases. Covers telling dead from rewriting, the migration boundary, and keeping the front door outside the app image. Facts re-verified live 2026-10-06. 2) putting-a-front-door-on-an-app-you-cant-modify (devops, pubDate 2025-06-24 backdated into the empty 2025-06 window, updatedDate 2026-10-06 so lastmod stays honest) The reader-gateway pattern: a second nginx container owning the public port, the request-time resolver, why a proxy body rewrite cannot touch a client-rendered SPA, the gate-bounce injection with its pass token, the app's own CSS hook for branding, and the silent-failure check to run after every app upgrade. Both: EN + ZH twins, TERMINALS/BANNERS entries appended via append_generator_entries.py (additive, deletions 0, node --check OK), og/banner generated and measured PASS (og rows=1 overflow=0 missingHash=0; banner 8 rows, delta 2, overflow 0).
banner-gen — per-post banner/hero image generator
Generates a branded 1600×900 (16:9) banner image for a blog post — shown at
the top of the article, above the title. Matches the site's cobalt-blue
branding and the "terminal / debugging" visual language (siblings with
scripts/og-gen/, which produces the 1200×630 social-share image).
Usage
node scripts/banner-gen/generate.mjs <post-slug>
Reads the post's category from frontmatter, fills template.html, renders
with headless Chrome, and writes public/banners/<slug>.png.
Then add banner: /banners/<slug>.png to the post frontmatter (en + zh) —
[...slug].astro renders it above the H1 title.
Customizing content
Each post's banner content lives in BANNERS in generate.mjs, keyed by
slug, with three parts:
titlebar— the terminal window's title-bar textlines— terminal body rows:{ t, text }wheret∈cmd·dim·err(red) ·ok(green) ·hl(amber) ·prompt($)flow— bottom pipeline steps:{ n: '1', label: '...', err?: true }
No entry → a generic default banner.
Requirements
- Google Chrome at
C:\Program Files\Google\Chrome\Application\chrome.exe(CHROME_PATHto override). - Node 20+.
Relationship to og-gen
- og-gen →
public/og/<slug>.png(1200×630, 2:1) for social sharing. - banner-gen →
public/banners/<slug>.png(1600×900, 16:9) for the article hero. The banner is a "filled" visual (terminal + flow pipeline), not a poster — avoid large empty margins.