معماری

Overview

content/  →  Gleam SSG  →  static HTML + feeds + search JSON  →  CDN
                    └──→  Lustre ES-module closure             ┘

browser islands  →  HTTP  →  Wisp handlers  →  Pog  →  PostgreSQL
live component   →  WS    →  Mist bridge     →  Lustre on BEAM
                                                └── OTP supervision

Three packages share types through shared/: komeil_site, komeil_server, and komeil_shared.

Static generation pipeline

  1. Walk content/en and content/fa for posts and pages.
  2. Parse TOML frontmatter (+++ fences).
  3. Render Markdown via Maud/Mork; Djot via Jot.
  4. Run checks: translation pairing, draft leak, internal links, UI string completeness.
  5. Render every route in the route table to dist/<path>/index.html.
  6. Emit Atom + RSS feeds, sitemap.xml, robots.txt, and per-language search indices.
  7. Copy public/ assets into the output tree.

The browser target compiles four independent Lustre islands for theme, search, clipboard, and contact. A dependency-closure step copies only modules reachable from the browser entry point; no npm application or client router is involved.

Content model

Every file carries: title, description, date, updated, language, slug, translation_key, tags, draft, canonical, excerpt.

translation_key connects English and Persian versions of the same logical page. The build fails if a key is missing a language or duplicated within one language.

Projects are structured JSON in content/projects/projects.json — data, not prose.

Admin editing model

Decap CMS lives at /admin. It edits files in content/ and commits via Git. No content database. Schema matches frontmatter requirements; incompatible drafts are caught by the build.

Backend model

Wisp handlers run behind Mist with explicit request/response contracts and PostgreSQL access through Pog. A RestForOne supervisor owns the database pool, rate limiter, named Lustre server component, and HTTP/WebSocket server. Migrations run before the tree starts. Admin routes remain disabled unless ADMIN_TOKEN is set.

The panel below is not a simulated widget: its timer, client count, and ping counter live inside the supervised BEAM process. Mist carries DOM events to the server and compact Lustre patches back to the browser.

Deployment model

  • Static output: Vercel, Cloudflare Pages, or any CDN serving only site/dist/.
  • API: Docker image on Fly.io, a VPS, or similar — PostgreSQL alongside or managed.
  • DNS + TLS + caching: Cloudflare.

Build and validation pipeline

CI runs: gleam format, tests, Erlang and JavaScript target compilation, content validation, translation validation, link checks, static build, server build, nix flake check, and Docker build verification.

Lustre server component · WebSocket · OTP supervised

Connecting to the BEAM runtime…