Changelog
Changelog, emin.de
2026-10-06 (magazine redesign, emin.de as the original, free speech)
A bright personal magazine instead of a docs page, emin.de as the canonical home of every post, and free speech as the site's core rule.
- Free speech is the core principle. Nothing is hidden for its topic, its words, its provenance or a pending review. The provenance-based
noindexpolicy (indexing.noindexProvenance) is gone; all 197 posts are indexable and in the sitemap, feeds andllms.txt. Onlydraft: true, written by the author, hides a post. A test fails if any content post isnoindex. Why: Emin is a journalist and the publisher of proud magazine; "never ever again censor free speech". /principlesand/de/principles(with Markdown mirrors and hreflang), a footer line "Free speech. Nothing here is censored. AI help is labelled, the words are mine.", andpublishingPrinciplesin Organization and BlogPosting schema.- Publisher identity in
site.config.mjs(publisher: journalist, publisher of proud magazine). Person schema:jobTitle"Journalist, publisher",worksForproud magazine,knowsAboutjournalism,sameAsGitHub, LinkedIn, X, Medium, Substack and proud.de (which 302-redirects to the Issuu archive). - The 7 previously quarantined posts (Zanzibar land and solar guides, JustMusic Berlin, the flywheel and Lightning/Nostr pieces) are back, imported with their original text.
- emin.de is the original. Every imported post now has its canonical on emin.de. The first-published URL moved to
original_url(189 files, front matter only, bodies untouched;scripts/migrate-canonical.mjs, idempotent). It appears in JSON-LD assameAs/isBasedOn, in the Markdown mirror and as "First published on Medium/Substack/emino.app, " under each post.datePublishedstays the original date;dateModifiedis the rewrite date. The loader rejects any canonical off this origin. - Sitemap lists every indexable post with
lastmodand reciprocal hreflang where translations exist. - Redirects:
redirectsmap insite.config.mjsplus every old emino.app slug, generated intosrc/generated/redirects.json(served as 301 by the Worker and the Node origin),public/_redirects, anddocs/REDIRECTS-emino-app.md(every emino.app URL to emin.de URL pair, plus the shared/ambiguous ones that need a decision). - robots.txt adds
Applebot-Extendedto the welcomed crawlers (GPTBot, OAI-SearchBot, ChatGPT-User, Google-Extended, Googlebot, Bingbot, PerplexityBot, ClaudeBot, CCBot and others). - BlogPosting: author is a full Person with
sameAs;speakablefrom the TL;DR (or headline + dek); BreadcrumbList kept. verification: { google, bing }slots insite.config.mjs; a meta tag is emitted only when filled.- Design. Newsreader display serif, self-hosted from
@fontsource-variable/newsreader(no third-party font request), system sans for UI. Warm paper#FAF7F0, ink#141414, one accent (#D9481C, darkened to#B93C14for text at 5.3:1). Masthead with a large wordmark; heroclamp(3rem, 7vw, 6.5rem); lead story with a 3:2 cover; 3-column image grid; theme rows (Nuri & Bitcoin, Agents & AI, Building, Science, Culture); compact "More" list. Hairline rules, no boxed cards. Article body in serif at 1.2-1.3rem, line-height 1.6, ~68 characters per line, with a drop cap on prose-first posts. Dates read "16 September 2026" / "16. September 2026" with reading time. Every text is at least 17px (tested). Mobile: single column at 700px and below, no horizontal scroll. - Covers. Each post's
image/cover, or else the first image in its media folder, is cropped to 3:2 WebP at 480/960/1440px (scripts/lib/covers.mjs, attention-based crop). Without an image, a typographic SVG cover is generated at build time (no paid API, no network). 174 photo covers, 23 generated. - Photos.
/photosgallery frompublic/photos/*.jpg|webpwith captions frompublic/photos/photos.json(schema inphotos.schema.json, guide indocs/PHOTOS.md). A portrait slot on the home page and/aboutreadspublic/photos/portrait.(jpg|webp); until it exists, a monogram fallback is shown (hidden on phones). - AI disclosure. One small provenance badge per post (human-written / human + AI / AI-written) and one explainer line in the footer and on
/about. The long "AI-written, human-supervised: not yet reviewed" sentence no longer repeats on cards; it stays on the post page footer, in the Markdown mirror and in schema (creativeWorkStatus), unchanged. - Bug fixes (display only, bodies untouched): "Untitled" titles fall back to the first H1, or to "Photo, 6 January 2026"; entities decoded once (including the broken
&x27;); URLs,[image: ...], pasted front matter and blockquote markers stripped from excerpts; dangling colons and full stops trimmed from titles (abbreviations such as "A.M." kept); a paragraph that is only a YouTube/Vimeo URL renders as "Watch the video on YouTube" (same href); "supervised byEmin" spacing. npm run import:staging -- <dir> [--dry-run]: re-runnable import of posts and media (also from aquarantine/folder). It never drops or hides a post, removesnoindex/index/quarantinekeys, moves external canonicals tooriginal_url, refuses to change a body, then runs the content validator and the word-preservation check. The Emin-voice rewrites are not imported yet.- Tests: 26 engine tests (new: original/canonical, schema, free speech, import policy, redirects, robots, design gates, display cleanup).
scripts/screens.mjstakes 1440px and 390px full-page screenshots with Playwright.
2026-10-06
Markdown-first blog engine, usable as a template.
- Content moved from
src/content/articles.mjsto Markdown files undercontent/posts/<lang>/<yyyy>/<slug>.md. Why: the owner wants every post public and readable on GitHub, and a Markdown file is the format both people and models already read. - One config file,
site.config.mjs, holds all site identity (origin, authors, locales, categories, formats, analytics, Cloudflare account). Why: emin.de is the template for further sites; forking should mean editing one file pluscontent/. - One loader validates front matter and renders every surface from the same parse: HTML,
index.mdmirrors, JSON API, MCP, RSS/Atom/JSON Feed per language, sitemap with hreflang,llms.txt,llms-full.txt, OG cards. Invalid content fails the build. - Listing routes per category, format, author, year archive,
/talks(VideoObject schema) and/changelog, per language. A language route exists only where a post in that language exists; translations must link back. - Authors registry with three honest types:
human,ai_editorial(openly labelled AI desk, Organization in schema, no bio or photo) andguest(credited byoriginal_author). The build rejects AI text under a human byline. - Imported 3 Substack posts (canonical to Substack) and 186 staged posts from emino.app and Medium (canonical to the originals).
mixedandunknownprovenance rendernoindexuntil reviewed. Raw HTML in imports is sanitised, Hugo shortcodes become links, and a text-preservation test proves no word is dropped. - Hosting decided: Cloudflare Workers + Static Assets, deployed by a GitHub Action on push to
main(prepared, not deployed). GitHub Pages rejected: no response headers, no content negotiation. - Analytics hooks, off by default: PostHog in
cookieless_mode: "always", GA4 only after explicit opt-in. Daily/weekly/monthly HogQL traffic report as a GitHub Action, no LLM. - IndexNow ping for changed posts on deploy; key file in
public/. skills/(transcript-to-post, daily AI-desk post, repurpose-to-social, weekly traffic report, monthly SEO/AIO audit) andplugins/(draft checker, traffic report, IndexNow, Substack import).- Discovery documents that were advertised but missing (
/openapi.json,/auth.md,/.well-known/api-catalog,/.well-known/mcp/server-card.json) are now generated from the code that serves them; links to never-implemented documents were removed from the Link header. - Removed the stub
/.well-known/oauth-protected-resource/[...path]route: it brokeastro buildonmainand the site has no OAuth.
2026-09-11
Initial public scaffold.
- Content architecture:
src/content/articles.mjsas the single source for HTML, Markdown mirrors, sitemap,llms.txt,llms-full.txt, the JSON API, MCP tool output and the x402 corpus. Why: hand-maintained parallel surfaces drift, and every new scanner check would otherwise be a multi-file diff. - First article: What agent ready actually means, 1573 words, seven sections, each with a standalone
Basically,takeaway, a scanner comparison table and real RFC sources. - Flagship
/agent-readypage: 10 FAQ entries with FAQPage schema, a HowTo, and a table of live machine surfaces that can be fetched as proof rather than claimed. - Pages: home, posts index, article route,
/agent-ready,/developers,/about,/contact,/privacy, each with a Markdown mirror. - Entity layer: Organization with knowsAbout, WebSite with SearchAction, Person with sameAs, BlogPosting plus BreadcrumbList per article.
llms.txtwritten with Markdown links only. Bare URLs fail the Lighthousellms-txtaudit, and a present-but-invalid file scores worse than no file at all.