Repository navigation
feat(blog): an optional, owner-configured collections page replaces the hard-wired research page - #484
Merged
Conversation
added 10 commits
October 15, 2026 11:27
A strict, read-only loader for <CROW_HOME>/blog/collections.json (CROW_BLOG_COLLECTIONS_CONFIG overrides). A missing file is off and silent; an unreadable, malformed or out-of-schema file is off, never throws, and warns once per (mtime, code) with the path and issue codes only. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
em, strong and a[href] with an absolute http(s) URL only; relative, protocol-relative and non-http hrefs are dropped and kept links get rel="noopener noreferrer". Built on the existing sanitize-html dependency. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
…m the collections config With no config the page is a 404, the nav carries no link to it and posts get no chapter nav. With one, the page renders configured case-study and tag sections (project id bound as a parameter, LIMIT 200, escaped strings, sanitized featured blurb), pageShell takes a lang option, and the chapter nav is scoped to the configured project. A host without the data-dashboard table renders the empty state instead of an error. New en/es strings under blog.collections.*. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
/blog/sources/* served one owner's private archive from a table the core schema does not create, so on most hosts it returned 500. The routes, their per-route Funnel block and their test go; they now 404. The general Funnel invariant that test carried (/blog stays a public Funnel prefix) moves into tests/auth-network.test.js. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
… become instance settings CROW_BLOG_FIGURES_BUCKET (default blog-figures) replaces the hard-coded bucket in the embed API and the data-dashboard server, read per call. The internal gateway URL defaults to this gateway's own listen port instead of a fixed :3002 (BLOG_FIGURE_GATEWAY_URL still overrides). Hydrate assets come from CROW_BLOG_HYDRATE_ASSETS_DIR, else <CROW_HOME>/bundles/tea-maps, with no home-directory fallbacks. data-dashboard 1.1.1 declares the bucket var. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
servers/ and bundles/ may not name the old private tables, a fixed project id, private bucket names or a personal checkout path, and only the read-only loader may name the blog collections config. Rewords the tea-maps mount comment. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
Declaring CROW_BLOG_FIGURES_BUCKET in the bundle's env_vars let a value set in the Extensions UI win inside the bundle child while the gateway's /blog/figures route kept reading its own env: uploads and reads could hit different buckets. The bundle inherits the gateway env, so the declaration goes (1.1.1 stays). The guide says to set it in the gateway's .env or a systemd drop-in, and its docLinks example uses an https base, noting that a /blog/ base needs something to serve that path. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
%2e, %2f and %5c (either case) are refused alongside literal .., // and backslashes. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
…error text
A 500 from /blog/api/chart/:id/data.json sent err.message (SQL errors,
table names) to anonymous Funnel callers. The response is now just
{error:"internal_error"}; the message stays in the server log.
Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
kh0pper
added a commit
that referenced
this pull request
Oct 15, 2026
…he hard-wired research page (#484) Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
/blog/researchused to be wired to one specific project: its id, titles and blurbs were in the code. It is now an optional collections page driven by an owner-written~/.crow/blog/collections.json. With no file, the page is off and its nav link is hidden.Server
loadCollectionsConfigvalidates the file with zod and caches it by mtime.projectIdmust be a positive integer, passed as a bound parameter.docLinks.basemust be a relative/blog/path or an http(s) URL.<html lang>and the locale./blog/sources/*) leave the public router. They currently answer 500 on hosts without their table and are now 404. The general Funnel invariant test ("/blog stays in PUBLIC_FUNNEL_PREFIXES") moves totests/auth-network.test.js.CROW_BLOG_FIGURES_BUCKET, defaultblog-figures), the internal gateway URL (defaults to this gateway's own port) and the hydrate-asset folder become instance settings.CROW_BLOG_FIGURES_BUCKET, with no default so it can't override the gateway's value.Tests: full suite 8815 run, 8814 pass, 0 fail, 1 skipped (the opt-in backup rehearsal). Every new behaviour was test-first. Public-hygiene is clean.
Deploy note: an instance that wants the page writes its own config, and setting the figures bucket is optional. A gateway restart is needed after merge.
https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp