Skip to content
Projects

CuB3y0nd/entropic has 36 GitHub stars and 8 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-14 18:40 UTC. Last push 2026-10-12.

How these counts are measured

Measured star growth

in 24h
0
in 7 days
Collecting history
in 15 days
Collecting history
in 1 month
Collecting history

Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.

About

A retro textmode blog theme for purists.

Security

README.md

Original

Entropic

Entropic is a retro textmode blog theme for purists.

The implementation uses Astro, Vite, and Tailwind CSS utilities. The surface uses fixed-width text, ASCII frames, volume indexes, ANSI colors, bitmap CJK glyphs, and plain .phile documents.

Configuration

Edit entropic.config.ts at the repository root. It is the single entry for site identity, home sections, 88x31 buttons, decorative artwork, colors, effects, volume metadata, CVE records, analytics, and optional WKD publishing. You do not need to edit files inside src/ to customize these settings.

The checked-in file is also the personal site configuration. home.asciiArt inherits the banner from src/config/defaults.ts unless overridden; WKD and the CVE page are explicitly enabled. Set cves.enabled: false to omit the CVE page, its homepage links, and its sitemap entry; the records can remain in cves.records for later use. Article inspection and fragment links are enabled by default. Select article text to inspect a value or prepare a link to that passage, then copy with your browser's native Copy action. Fragment links locate the quoted text and briefly highlight it; repeated passages need enough context to identify one match. Set philes.inspect: false or philes.fragmentLinks: false to disable either tool. Disabling fragment links also disables incoming passage navigation.

The file is a plain TypeScript object with as const satisfies EntropicConfig. Your editor provides field descriptions and completion, including artwork preset names and rotation modes. Omitted settings inherit internal defaults; objects merge at documented leaves and arrays replace their defaults.

pnpm check:config
    validate values, artwork selection, and optional public key input

pnpm check
    also check unknown fields/types, source boundaries, and formatting

pnpm dev
    preview configuration edits locally

Field comments in entropic.config.ts describe supported values, examples, and default inheritance. The configuration types live in src/config/types.ts.

Directory Layout

entropic.config.ts      the user-editable configuration
src/config/             internal types, defaults, validation, and projections
src/content/philes/     article source files
src/pages/              Astro file routes and HTTP output adapters
src/features/           home, site-badges, philes, volumes, cves, seo, and wkd
src/shared/textmode/    text cells, ANSI, bitmap CJK, and browser behaviors
src/shared/browser/     browser lifecycle helpers
src/layouts/            shared page shells
src/styles/             foundation, layer ordering, and responsive rules
src/assets/decorations/  original artwork grouped by series
src/generated/          generated glyph metadata
scripts/                font generation, checks, and Git hooks
tests/                  configuration, content, browser, and security regressions

Features own their data access, rendering, components, and styles. Shared textmode primitives have separate pure, server, and browser entry points. Cross-feature imports use public entries; pnpm check:boundaries verifies these interfaces and keeps server dependencies out of browser modules.

Tailwind handles common layout and spacing. Pixel typography, ANSI/CJK output, and animation rules live in native CSS. Theme utilities use the typed configuration; Preflight is not enabled. Astro bundles styles from their owning components and modules instead of loading every feature's CSS on every page.

Content

Articles are .phile files under:

src/content/philes/volume-*/**/*.phile

A phile is a frontmatter block followed by plain text.

Required fields:

title
date

Optional fields:

order
slug
redacted
lang
decoration
math

Credits come from each file's Git history, including renames and Co-authored-by trailers. The original author appears first, followed by contributors in order of first participation. GitHub nicknames are preferred; accounts without a nickname use their login. Unlinked identities keep their Git author name. A new, uncommitted article has no credits yet.

Names are resolved once per production build and embedded in the static pages. No GitHub requests run in the browser. Builds restore shallow history before reading credits; Vercel checkouts without an origin use its Git system variables. An optional build-time GITHUB_TOKEN increases the API request allowance. Cached profiles provide a fallback when GitHub is unavailable.

The body is rendered as textmode content. It preserves layout, wraps to the configured article width, renders images, accepts ANSI escape sequences, and supports the custom ink block syntax.

Set math: true in an article's frontmatter to render LaTeX formulas with Temml. Use $...$ or \(...\) inline, and $$...$$ or \[...\] for display equations. Standalone tall expressions also receive display space. Math is converted to native MathML at build time; unsupported commands fail the build. Code spans and fenced code keep their TeX literal. The bundled Entropic Math font derives from STIX Two Math's CFF font: https://github.com/stipub/stixfonts/releases/latest Its license is included in fonts/STIXTwoMath.LICENSE. Radical size variants have their short horizontal caps trimmed so MathML's overbar joins directly to the diagonal without a pixel step. Other glyph outlines are unchanged. Fraction, radical, overbar and underbar rule thicknesses use 0.05em to avoid two-pixel bars at the article's 16/17px sizes. Run pnpm fonts:math to regenerate from the latest upstream release (requires uv). The script checks the radical geometry before replacing the font and records the resolved source URL and SHA-256 in the font's metadata. Normal builds and page visits use the bundled font without fetching upstream. Temml requires a browser with native MathML support. Equation references (\ref and \eqref) are not wired into the site's rendering pipeline yet.

Math articles retain the textmode font and colors, with unframed equations aligned to the body. Prose wraps to the viewport; long equations scroll within their own region instead of shrinking the entire article. Articles without math: true retain the original text rendering, including literal dollar signs.

Ink style reference:

https://www.cubeyond.net/volume/3/ansi-ink-phile/

Article header artwork is chosen from the article's public URL. It stays the same across reloads, rebuilds, and adding/removing other articles. Renaming its slug or moving it to another volume can choose a different effect. The default pool is life, maze (depth-first maze carving), sort (insertion sort), bits (four byte registers), asm (a small assembly loop), and reorder (out-of-order execution with in-order retirement). No extra controls interrupt the reading layout.

Configure the pool and playback in entropic.config.ts:

philes: {
  decoration: {
    effects: ["life", "maze", "sort", "bits", "asm", "reorder"],
    animated: true,
    speed: 1
  }
}

Omitted options inherit these defaults. effects must contain at least one unique algorithm; use a single item to show the same effect everywhere. Reordering the pool does not reshuffle articles. For the new effects, speed ranges from 0.25 to 4 and animated: false keeps a still frame. The new artwork also stays visible without JavaScript. Life uses its original renderer, grid, color, and timing; these playback options do not change it. All effects pause offscreen, in hidden tabs, and for reduced motion.

Set decoration: maze (or any other effect name) in an article's frontmatter to pin its effect, including one outside the global pool. decoration: false removes that article's artwork and closes the unused header space. philes.decoration: false disables all article artwork, including overrides.

The processor illustrations use native Gohu text at the article's font size. bits highlights the bits changed by XOR, rotate, and shift operations. asm shows an executed instruction with its resulting registers and flags. reorder shows dependency waits and two execution slots; completed instructions remain in the queue until all earlier instructions can retire.

Example:

---
title: "Example"
date: 2026-05-31
---

--[ Summary ]-------------------------------------------------------------

    target    : ...
    primitive : ...

--[ ink ]--
| 00400000-00401000 r--p 00000000 ./a.out     ; ELF headers
~ BBBBBBBB.BBBBBBBB GGKK KKKKKKKK MMMMMMM
| 00401000-00402000 r-xp 00001000 ./a.out     ; executable text
~ BBBBBBBB.BBBBBBBB GGRR KKKKKKKK MMMMMMM

#[B|B] address range
#[G|G] readable permission
#[R|R] executable permission
#[K|K] offset / padding
#[M|M] mapped object

Volumes

Volume numbers come from the directory name:

volume-0
volume-1
volume-2

The public routes are:

/volume/<n>/
/volume/<n>/<phile-name>/

Per-volume behavior is configured in entropic.config.ts under volumes. Only the fields supplied for a volume override its generated defaults.

Set decoration to circuit, archive, study, or prism for a themed header illustration. life keeps the original Game of Life frame and is the default for unconfigured volumes; false removes the decoration. For example:

volumes: {
  0: { title: "Security Research", decoration: "circuit" },
  1: { title: "Historical Philes", decoration: "archive" }
}

The four illustrations also accept an options object:

decoration: {
  kind: "study",
  animated: true,
  speed: 1,
  calendar: { month: 12, day: 31 }
}

animated: false keeps a static illustration. speed is a playback multiplier from 0.25 to 4: 0.5 is half speed, 2 is double speed. These optional settings default to true and 1. calendar is only for study and defaults to December 31; use a numeric month and a valid day (February 29 is supported). The original life frame uses the string shorthand. Every CPU transfer independently samples an 8-bit value, including 00 and FF; consecutive random values may repeat.

The new illustrations remain visible without JavaScript. Their motion pauses offscreen, in hidden tabs, and when reduced motion is requested. Article headers use the separate philes.decoration settings described above.

Redactions

A redacted phile uses:

redacted: true

The loader discards redacted bodies from the content store and the public page renders the redacted screen. The pre-commit hook parses staged YAML with the same parser and unstages redacted philes, leaving the worktree untouched. Install dependencies before using the hook. Invalid frontmatter blocks the commit before the hook changes the index.

Redaction is a publishing flag, not secret storage: it does not erase files from existing Git history. Keep confidential source material outside Git.

Enable hooks in a fresh clone:

git config core.hooksPath scripts/hooks

Site Badges and Artwork

entropic.config.ts groups links and decoration under buttons. The default rotates through all 25 presets daily in Asia/Shanghai:

buttons: {
  items: [/* your 88x31 links or copy controls */],
  shuffleOnBuild: true,
  artwork: {
    mode: "daily",
    timeZone: "Asia/Shanghai",
    seed: "entropic-artwork-v1",
    fallback: "nagaraNozomi"
  }
}

All visitors with the same date see the same composition, including after a refresh. Each cycle shuffles every selected preset once and avoids repeats at the cycle boundary. The date is checked on load and when returning to a tab or cached page; a continuously open page does not swap on a timer. No daily rebuild, endpoint, cookie, or browser storage is needed.

Use artwork: false for links alone, or artwork: { mode: "fixed", preset: "nagaraNozomi" } for a fixed image. An empty items array hides the panel. The inline configuration comments cover custom images, preset pools, interval/visit modes, and the no-JavaScript fallback.

shuffleOnBuild: true shuffles buttons once per production build and keeps that order across visits and artwork changes. Local development uses items order. Each item supplies label, imageSrc, and either href or copyText.

Artwork originals live under src/assets/decorations/<series>/. Presets retain the approved display sizes, overlap, and companion placement. Astro builds 1x and 2x WebP images with lossless encoding; originals remain unchanged. Inert HTML templates keep unselected compositions from fetching images. The shareable animated site badge stays at /assets/88x31/button.gif for other sites to link to.

CSS grid and container queries own layout. Overlapped badges appear above the artwork on hover or keyboard focus; touch devices show them immediately. Artwork is decorative and cannot intercept link activation.

Commands

Use Node.js 24 or newer and the pinned pnpm 12.3.4. pnpm check:types runs Astro diagnostics with the official TS 6 compatibility API and the native TypeScript 7 checker. Keep both aliases until Astro supports the new API.

pnpm install --frozen-lockfile
    install dependencies

pnpm dev
    run the local development server

pnpm check
    run Astro and TypeScript diagnostics, formatting/lint, and module boundaries

pnpm format
    format TS/JS/CSS/JSON with Biome and Astro templates with Prettier

pnpm lint
    lint the files covered by Biome

pnpm build
    build the static site

pnpm test
    run content, textmode, redaction-hook, and WKD regression tests

pnpm exec playwright install chromium firefox
    install the browser revisions used by this Playwright version

pnpm test:browser http://127.0.0.1:4329
    check fitting, animation lifecycle, and site badges against a production preview
    set PLAYWRIGHT_BROWSER=firefox to use Firefox

pnpm test:wkd
    verify optional WKD generation and key handling

pnpm assets:fonts
    regenerate font subsets and CJK atlases

Font assets are committed. Normal production builds do not require local font tooling.

Run pnpm assets:fonts only when the committed font outputs need to change:

after editing font source files under `fonts/`
after changing CJK glyph coverage or bitmap atlas generation
after changing text that introduces new CJK glyphs which must render via
    the bitmap CJK atlas
after changing the font subset scripts themselves

The command regenerates:

public/assets/fonts/gohu-subset.woff
public/assets/cjk/wqy-cjk-body.png
public/assets/cjk/wqy-cjk-link.png
src/generated/cjk-atlas.ts

Regenerating these assets requires fonttools. If pyftsubset is already on PATH, the scripts use it directly. Otherwise, with uv installed, the scripts resolve fonttools automatically and keep the uv cache under /tmp.

Branches

master is stable. dev is integration.

Feature and fix work should branch from dev. Pull requests should be squash-merged. Commits should be signed and should use Conventional Commits.

Public Key Discovery

This personal site enables WKD for [email protected] in entropic.config.ts. A new site that omits WKD settings leaves publication disabled. To configure it:

wkd: {
  enabled: true,
  email: "[email protected]",
  publicKeyPath: "public/assets/pgp/public-key.asc"
}

Set wkd.enabled: false to disable publication while retaining the email and path. Replace the referenced file with your public key export; its manual download URL also works without WKD. Key changes require replacing only that file and rebuilding.

The configuration check rejects missing settings, private keys, and public keys whose UIDs do not match the email. The development server watches the configured public key file. Production requires a new deployment.

Migration: move the former WKD_EMAIL and WKD_PUBLIC_KEY_PATH values from .env.local or the hosting environment into this object. Those old environment variables no longer configure WKD; there is one source of truth. The email and public-key path are public publishing settings, not credentials.

The build derives the mail domain and lookup hash, then generates the policy and binary public key under dist/.well-known/openpgpkey/. Generated WKD files are not committed. The Vercel header rules work with any mail domain.

For pnpm 12 on Vercel, use the pinned packageManager via Corepack (ENABLE_EXPERIMENTAL_COREPACK=1 in the build environment). Confirm the actual Node.js and pnpm versions in the deployment log; Vercel's default package-manager detection is not the same as honoring this pin. Local builds do not verify the remote environment.

For Vercel, deploy the configured public key and attach openpgpkey.<mail-domain> directly to that environment, without a domain redirect. Redeploy after changing the configuration. In Cloudflare DNS, add a CNAME named openpgpkey using Vercel's displayed target, with Proxied (orange cloud) and TTL Auto. Use Full (strict) for the connection to Vercel and wait for domain verification, HTTPS certificates, and DNS caches to update.

Preserve Vercel's proxy prerequisites for certificate issuance and renewal: allow HTTP access to /.well-known/acme-challenge/* without redirects or browser checks, and leave /.well-known/vercel/* uncached. See: https://vercel.com/docs/security/reverse-proxy

The proxy setting reflects deployment testing: direct Vercel connections failed during TLS 1.3 shutdown with GnuPG 2.5.21 / GnuTLS 3.8.13, while the same WKD lookup succeeded through Cloudflare with default TLS settings. Other static hosts need equivalent WKD response headers and their own live discovery checks.

WKD clients need public access without authentication or browser challenges. Cloudflare's Browser Integrity Check can reject non-browser clients with HTTP 403 / error 1010 even when GnuPG succeeds. Add a scoped exception:

  1. Select your domain in Cloudflare, then open Rules > Overview.

  2. Select Create rule > Configuration Rule and name it WKD public access.

  3. Under When incoming requests match, choose a custom filter expression and open Edit expression. Replace the example hostname with your WKD hostname:

    (http.host eq "openpgpkey.example.org"
     and starts_with(http.request.uri.path, "/.well-known/openpgpkey/")
     and http.request.method in {"GET" "HEAD"})
    
  4. Under Then the settings are, add Browser Integrity Check and set it to Off. This exception applies only to WKD GET/HEAD requests on that host.

  5. Select Deploy.

Cloudflare documents configuration rules and selective BIC exceptions here: https://developers.cloudflare.com/rules/configuration-rules/create-dashboard/ https://developers.cloudflare.com/waf/tools/browser-integrity-check/#disable-selectively

After DNS changes have propagated, verify discovery with a fresh keyring:

gpg --homedir "$(mktemp -d)" --auto-key-locate clear,wkd \
    --locate-keys [email protected]

Use your configured email and compare the complete primary fingerprint with your trusted public key export. Check that policy and key URLs accept both GET and HEAD with the expected content types and CORS headers. Test generic HTTP clients too: a successful GnuPG lookup alone does not rule out a Cloudflare browser check blocking another client. Local builds cannot verify production DNS, HTTPS, or the hosting provider's access rules.