eratic / CLAUDE.md
1,552 words

eratic — operating manual

This file is for whoever is doing the work: usually an LLM, occasionally a human. Read it before touching anything. It is deliberately concrete — exact paths, exact frontmatter, exact commands — because the whole point of this setup is that publishing does not require remembering how it works.

What this is

Four static sites, one repository, one small server.

Site Address What goes there
home eratic.ro Front door. Shows the latest few things from the other three. Nothing is authored here.
blog blog.eratic.ro Essays and photo sets, in one feed.
fav fav.eratic.ro Objects, books, films, music — with notes.
io io.eratic.ro One issue a week of things read/watched/heard.

Astro 7, no JavaScript shipped unless an article asks for it, no database, no CMS, no third-party anything. Content is Markdown files in git. Deploying is npm run deploy.

The five commands

npm run dev:blog      # local preview at :4321 (also dev:home, dev:fav, dev:io)
npm run build         # build all four, after checking photos for GPS
npm run check         # TypeScript + Astro diagnostics; must be clean
npm run deploy        # build, upload, go live, verify
./deploy/rollback.sh  # back to the previous release, in seconds

Drafts and future-dated entries appear in dev and are excluded from build. That is the whole staging story — there is no staging server.


Adding things

A blog post

sites/blog/src/content/posts/<year>/<slug>.md → blog.eratic.ro/<year>/<slug>/

---
title: On making a place instead of a profile
summary: >-
  One or two sentences. Required. Used as the meta description, the listing
  blurb, and the text feed readers get when the full body cannot travel.
pubDate: 2026-08-05
tags: [meta, design]
---

Body in Markdown.

Optional: updatedDate, draft: true, cover: ./images/x.jpg + coverAlt, canvas, theme (see Designing a single article).

Use .md unless you need components. Plain Markdown is rendered to HTML at build time, so the whole post travels in the RSS and JSON feeds. An .mdx post compiles to a component instead, so subscribers get the summary and a link. That is a real trade-off, not a technicality — most posts should be .md.

A photo set

sites/blog/src/content/photos/<year>/<slug>.mdx, images in sites/blog/src/content/photos/<year>/images/.

---
title: Harbour, early
summary: Three frames from a morning at the water.
pubDate: 2026-08-03
place: Constanța        # a place name, never coordinates
columns: 2              # 1, 2 or 3
exif: true              # show camera settings under each frame
photos:
  - src: ./images/harbour-01.jpg
    alt: A wide view of still water with moored boats   # required
    caption: The water had not decided what colour it was yet.
---

Optional prose after the grid.

Photo sets share the blog's feed and URL space. Camera settings are read from the files at build time; the served images carry no metadata at all.

A fav

sites/fav/src/content/favs/<year>/<slug>.md

---
title: A Pattern Language
summary: Required, as everywhere.
pubDate: 2026-08-02
kind: book              # object | book | film | music | other
creator: [redacted]topher Alexander et al.
year: 1977              # when the thing was made
rating: 5               # 1–5, optional — omit and nothing renders
link: https://…         # optional, never an affiliate link
---

The review.

kind drives the filter pages (/books/, /films/, …), which appear automatically once a kind has something in it.

An io issue

sites/io/src/content/issues/<year>/<number>.md

---
issue: 31
summary: Required.
pubDate: 2026-08-03
links:
  - title: Practical Typography
    url: https://practicaltypography.com/
    kind: read          # read | watched | listened | other
    via: a friend       # optional, credit costs nothing
    note: Why it is here.
---

Optional intro.

title is optional here and nowhere else — without one it renders as "Issue 31". The links are frontmatter rather than prose because io's feed carries the full list, rendered; the web page is the copy.


Designing a single article

The design system is a floor, not a ceiling. Two escape hatches, both frontmatter:

theme — re-declare any token from packages/core/src/styles/tokens.css for this entry only:

theme:
  accent: 'oklch(64% 0.19 42)'
  font-prose: "'Fraunces Variable', Georgia, serif"
  measure: '40rem'
  surface: 'oklch(97% 0.02 85)'

Write keys without the leading --. They are applied as inline custom properties on <html>, so everything inside re-tunes coherently.

canvas — how much of the site to keep:

Components inside .mdx

Available without any import: <Figure>, <PhotoGrid>, <Video>, <Bleed>, <Aside>. Explicit imports still work and take precedence — that is how you use a component built for one article.

<Figure src={frontmatter.cover} alt="…" width="wide" exif />
<Aside label="Note">A note in the margin on wide screens.</Aside>
<Bleed width="full">…anything that needs the whole viewport…</Bleed>

width is content | wide | full on all of them.

Interactive elements

Astro <script> tags are bundled to external files, so they work under the site's Content-Security-Policy. Inline <script> will be blocked — this is deliberate, and it is what stops a third-party tracker from ever being added by accident. Inline <style> and style= attributes are fine.


Rules that are enforced, not just requested

Things that will waste your afternoon

Learned the hard way while building this. All are fixed; do not undo them.

Showing and hiding a whole section

Each site in packages/core/src/config.ts has a listed flag. false means the section is still built, still deployed and still reachable at its own address — it is simply not linked from the header, the home page or the footer. That is how fav and io are currently parked: no dead ends, no "coming soon" page, and one line to flip when there is something there.

What to write next

IDEAS.md — essay ideas distilled from the note archive, each with a thesis, a suggested design treatment and a rough length. Read it before starting a post.

Layout

packages/core/           the design system — tokens, layouts, components, feeds
  src/config.ts          identity, the four sites, media resolution  ← start here
  src/styles/tokens.css  every value the design depends on
sites/{home,blog,fav,io} one Astro project each
deploy/                  bootstrap.sh, deploy.sh, rollback.sh, eratic.caddy
scripts/                 photos-check, photos-strip, prune-originals
docs/                    the runbooks

Identity, domains and the media base live in packages/core/src/config.ts. Change them there and nowhere else.

Infrastructure, briefly

A dedicated VPS at 178.104.116.235 (ird tier 0x), Ubuntu 24.04, firewall open only on 22/80/443. Caddy owns 80/443 and gets TLS automatically. Releases are directories under /opt/web/sites/eratic/releases/, and current is a relative symlink; deploying flips it atomically, rolling back flips it back.

It is not the server the Agora API runs on, and adding a second domain later means one file in /opt/web/caddy/etc/conf.d/ plus a DNS record.

Full detail in docs/03-deploy.md. DNS in docs/04-dns-and-tls.md.

static mirror of HEAD · about · clone: git clone https://git.makefuture.ro/eratic.git