A personal site, portfolio, and writing surface built with
❤️
by
The Lossless Group
SSR + content-driven with
Astro 6,
Tailwind CSS v4, and
@lossless-group/lfm
- Stack
- What this site does
- Content Authoring and Management
- LFM Dev Mode
- Site Development and Specifications
- Project Structure
- Getting Started
- Available Scripts
- Deployment
•
•
@lossless-group/lfm
•
Vercel adapter (server output)
Modern site generation with shared markdown rendering, citations, mermaid diagrams, and bare-URL auto-unfurl — all flowing through one MDAST pipeline.
This is Michael Staton's personal site. It carries:
- A short portfolio and CV at
/portfolioand/cv. - Long-form writing: essays, posts, and notes — all consumed through
@lossless-group/lfmso authoring lives in plain markdown with rich rendering. - A Context-Vigilance document surface at
/context-vigilance/that fetches specs, blueprints, prompts, and reminders from the parentastro-knotsGitHub repo at build time. - A gated
/promotesurface for sharing investment-opportunity decks and memos with a small circle of co-investors. Polite access gate, multi-format decks, version-pinned memos, and a hub-per-opportunity. - Dynamic OpenGraph image generation at
/api/og— Satori → SVG → resvg-js → PNG, so every shared link unfurls with a brand-styled card.
All long-form content (essays, notes, context-v docs, promote memos) is plain markdown rendered through the shared LFM pipeline. Authoring features supported today:
- Standard CommonMark + GFM — tables (alignment), strikethrough, task lists, footnotes, autolinks.
- Hex-code citations —
[^a1b2c3]references with[^a1b2c3]: 2025. [Title](URL). Published: YYYY-MM-DDdefinitions get renumbered to display indices[1],[2], … and a Sources section renders at the bottom of the article. The hex code anchors the link from the inline citation marker to its source-list entry. - Obsidian-style callouts —
> [!note] Titleblocks normalize to LFM directives and render asCalloutcomponents. - Image directives —
:::image{src caption credit alt}for rich metadata on inline images. - Mermaid diagrams — fenced
```mermaidblocks render via the CDN-loadedmermaid@10ESM module with theme-token parity. An expand-to-modal affordance handles complex diagrams. See changelog2026-05-03_02.md. - Bare URL auto-unfurl — paste a YouTube / YouTube Short / YouTube Playlist URL on its own line and it embeds with the right aspect ratio and a copy-URL button. See changelog
2026-05-03_03.md. Inline links stay autolinks.
content line content line
https://youtu.be/jCe2wg1ulus?si=oplqTdsbv8sv2JfH
content line content lineThe renderer (src/components/markdown/AstroMarkdown.astro) walks the MDAST tree from parseMarkdown(entry.body) and dispatches each node type — headings, paragraphs, code blocks (with a Mermaid branch), bare-URL paragraphs (with a YouTube branch), images, callouts, citations — to the appropriate component.
The /context-vigilance/ route renders the specs / blueprints / prompts / reminders authored in the parent astro-knots repo's context-v/ directory. Documents are fetched at build time by scripts/fetch-context-v.ts:
- The script reads the repo list from
context-v-sources.yamlat the site root. - For each source repo, it pulls the
context-v/tree via the GitHub API, filters to.mdfiles in configured categories (specs,blueprints,prompts,reminders), and writes them undersrc/content/context-v/{repo-slug}/{category}/. - Frontmatter is enriched with a
_context_vprovenance block (repo, branch, commit, file path, GitHub URL). - Caching is by tree SHA — unchanged repos skip the per-file fetch.
To add a new source repo, edit context-v-sources.yaml and run pnpm fetch-context.
Essays live in a shared content repo (lossless-group/lossless-content/essays/) and are pulled at build time by scripts/fetch-essays.ts. Each essay is a markdown file with portrait/banner images and rich frontmatter.
src/content/notes/from-the-rabbit-hole/ — short notes co-located with the site. Editorial cadence is faster than essays; the rendering pipeline is identical (LFM → AstroMarkdown).
A multi-tenant opportunity-promotion surface for sharing investment opportunities with a small circle of co-investors. Three-surface architecture:
- Public index at
/promote— listed opportunities with status pills (Active / Closing soon / Paused / Closed). - Gated opportunity hub at
/promote/{slug}— materials chooser (memo, deck variants). - Gated materials at
/promote/{slug}/{type}/{format?}/{version?}— the actual deck or memo content.
A polite access gate (signed cookie, scrypt-hashed master code, optional per-opportunity overrides) protects every gated path. See astro-knots/context-v/blueprints/Build-a-Promotion-Surface-for-Investment-Opportunities.md for the full architecture and changelog 2026-05-03_01.md for the implementation summary.
Each opportunity is a directory at src/content/promote/{slug}/:
src/content/promote/{slug}/
├── opportunity.yaml # metadata: company, status, materials list, gate config
├── variants.yaml # registry of (format, version) deck pairs
├── data.yaml # shared structured data the deck composes against
├── narratives/ # one .md per slide (frontmatter + prose)
└── memo/v{N}.md # versioned memo content
Privacy convention (this is a public repo): real opportunity slugs are gitignored under src/content/promote/, src/layouts/sections/promote/, and public/promote/. Directories prefixed with _ (e.g., _demo, _template) are committed as scaffolding seeds. See the .gitignore in this directory.
Site-level changelog lives at changelog/{YYYY-MM-DD}_{NN}.md — the repo root, matching the tree-wide Lossless convention. Each entry is a full markdown post: feature name + body, with diagrams allowed.
Entries surface through /llms.txt and /llms-full.txt, and the same directory is what the Lossless corpus ingester reads. There are no per-entry changelog pages on this site today.
When developing LFM features against this site, the standard @lossless-group/lfm dependency points at the JSR-published version. To pick up unreleased LFM changes from packages/lfm/ in the parent monorepo, switch the site to "local" mode:
# Switch to workspace LFM (use only on a `development` branch)
pnpm lfm:local
# Switch back to JSR (use before merging to main)
pnpm lfm:jsr
# Auto-detect from the current git branch (development/develop/dev → local; else JSR)
pnpm lfm:autoBehind the scenes the script flips package.json's LFM specifier between npm:@jsr/lossless-group__lfm@^0.2.1 and workspace:^, toggles .npmrc's ignore-workspace flag, and regenerates pnpm-lock.yaml with the right --ignore-workspace semantics. Never commit a workspace-mode package.json or lockfile to a deployable branch — Vercel can't resolve workspace links from the standalone site repo. Full convention in astro-knots/context-v/blueprints/Maintain-Branch-Aware-LFM-Dev-Mode.md.
- Standard markdown + GFM — tables, strikethrough, footnotes, autolinks
- Hex-code citations — inline renumbering + Sources component
- Obsidian callouts —
> [!type] Titledirective normalization - Image directives — caption / credit / alt with rich metadata
- Mermaid diagrams — CDN runtime, theme parity, expand-to-modal
- Bare URL auto-unfurl — YouTube watch / Shorts / Playlists
- Vimeo / Loom / Spotify / SoundCloud auto-unfurl — planned in catalog
- Inline link previews —
LinkPreview__*--Row/Card/Thumbfamily - Wikilinks + backlinks
- Highlights (
==text==) and inline tags (#Tag-Name)
- Polite access gate — signed cookie, scrypt-hashed master code, per-opportunity overrides
- Materials chooser — 0/1/2-3/4+ rendering branches
- Scroll-deck rendering — copy of the calmstorm-decks pattern, adapted to mpstaton-site tokens
- Versioned memos —
memo/v{N}.mdwithversion-{N}URL grammar - OpenGraph + Twitter card meta — DMs unfurl with brand image and headline
- Demo opportunity (
_demo) — end-to-end smoke test scaffold
- Context-V GitHub fetcher — pulls docs from configured repos, caches by tree SHA, enriches with provenance
- Essays fetcher — pulls essays from
lossless-group/lossless-contentat build
- Dynamic OG image generation — Satori + resvg-js, branded templates per page type
- Astro — v6.0.8 — server-output mode via
@astrojs/verceladapter - Tailwind CSS — v4.1.14 —
@tailwindcss/viteplugin, semantic-token CSS architecture - @lossless-group/lfm —
npm:@jsr/lossless-group__lfm@^0.2.2— shared markdown pipeline (JSR canonical) - Satori — v0.26.0 — JSX → SVG for OG image generation
- @resvg/resvg-js — v2.6.2 — SVG → PNG rasterization
- mdast-util-to-string — v4.0.0 — heading-ID generation in the renderer
- yaml — v2.8.3 — YAML parsing for opportunity / variants / data files
- mermaid — v10 (CDN, runtime-loaded) — diagram rendering
- pnpm — v10+ — workspace-aware package manager (always use pnpm; never npm/yarn)
- Bun — used for the build-time fetcher scripts (
bun scripts/fetch-*.ts)
mpstaton-site/
├── src/
│ ├── components/
│ │ ├── markdown/ # AstroMarkdown, Callout, CodeBlock, MermaidChartDisplay,
│ │ │ # MarkdownImage, Sources, YouTubeEmbed/Shorts/Playlist
│ │ └── promote/ # OpportunityCard, MaterialsChooser, StatusPill, etc.
│ ├── content/
│ │ ├── changelog/ # site-level change history
│ │ ├── essays/ # FETCHED — see scripts/fetch-essays.ts
│ │ ├── context-v/ # FETCHED — see scripts/fetch-context-v.ts
│ │ ├── notes/ # local notes from the rabbit hole
│ │ ├── promote/ # opportunity content (gitignored except _-prefixed seeds)
│ │ └── content.config.ts # collection schemas (Zod)
│ ├── lib/
│ │ ├── markdown/ # classify-bare-link.ts (renderer-side, mirrors LFM catalog)
│ │ └── promote/ # opportunities, memos, sections, urls (server-side loaders)
│ ├── pages/
│ │ ├── api/og.ts # dynamic OG image generation
│ │ ├── context-vigilance/# context-v document index + detail
│ │ ├── essays/ # essays index + detail
│ │ ├── notes/ # notes index + detail
│ │ ├── promote/ # /promote surface (index + [slug]/memo + [slug]/deck)
│ │ ├── portfolio/ # portfolio
│ │ ├── about.astro # about
│ │ ├── cv.astro # CV
│ │ └── index.astro # homepage
│ ├── styles/ # tokens.css (semantic + brand vars), globals, prose
│ └── middleware.ts # /promote gate cookie validation
├── scripts/
│ ├── fetch-context-v.ts # build-time context-v document fetcher
│ ├── fetch-essays.ts # build-time essays fetcher
│ └── lfm-mode.mjs # local ↔ JSR LFM swap (see "LFM Dev Mode" above)
├── public/ # static assets (favicon, photos, share banners)
├── astro.config.mjs
├── package.json
└── README.md # this file
- Node.js (v18 or later)
- pnpm v10+ (
npm i -g pnpm) - bun (for build-time fetcher scripts) —
curl -fsSL https://bun.sh/install | bash - A GitHub Personal Access Token in
GITHUB_CONTENT_PAT(only needed if you exceed the unauthenticated 60/hr rate limit while fetching context-v / essays)
cd sites/mpstaton-site
pnpm install --ignore-workspaceThe --ignore-workspace flag is critical — it makes pnpm treat this site as standalone (matching Vercel's deploy environment) rather than substituting workspace packages. The site's .npmrc already has ignore-workspace=true so subsequent installs honor it automatically.
Copy .env.example to .env and fill in:
GITHUB_CONTENT_PAT=ghp_... # optional — only for higher fetch rate limits
PROMOTE_SESSION_SECRET=... # 32-byte hex; HMAC signing key for /promote cookie
PROMOTE_MASTER_CODE_HASH=... # scrypt of the master access code
PROMOTE_OVERRIDE_CODES_JSON={"slug":"..."} # optional per-opportunity override map
PROMOTE_DEV_BYPASS=1 # local convenience — accepts any codepnpm devSite available at http://localhost:4321. The dev script runs both fetchers first, so the first start may take a few seconds while context-v and essays sync from GitHub.
| Command | Action |
|---|---|
pnpm install |
Install dependencies (with --ignore-workspace if not in .npmrc) |
pnpm dev |
Fetch content, then start the dev server |
pnpm build |
Fetch content, then build for production (Vercel server output) |
pnpm preview |
Preview the production build locally |
pnpm fetch-all |
Run both fetchers (context-v + essays) without starting the server |
pnpm fetch-context |
Just the context-v fetcher |
pnpm fetch-essays |
Just the essays fetcher |
pnpm lfm:auto |
Swap LFM mode based on git branch (development → local, else JSR) |
pnpm lfm:local |
Force LFM to the workspace package (development branches only) |
pnpm lfm:jsr |
Force LFM to JSR (always before merging to a deployable branch) |
pnpm astro ... |
Run Astro CLI commands directly |
The site deploys to Vercel as a server-output Astro app via @astrojs/vercel. Vercel watches this site's repo (the submodule) directly — not the parent astro-knots monorepo — so this site must be self-contained and never depend on workspace packages on a deployable branch.
pnpm buildVerifies cleanly with:
pnpm install --frozen-lockfile --ignore-workspace --lockfile-onlyThat's exactly what Vercel runs. If it passes locally, Vercel will pass.
astro.config.mjs— Vercel adapter, Tailwind v4 Vite plugin, server output.npmrc—ignore-workspace=true,@jsr:registry=https://npm.jsr.io, JSR-aliased LFMpnpm-workspace.yaml(parent) — site is a workspace member for development convenience only; production install ignores the workspacetsconfig.json— extendsastro/tsconfigs/strict, alias@brand→src/config/brand.ts
- Astro Documentation
- Tailwind CSS v4
- @lossless-group/lfm on JSR
- Parent monorepo:
astro-knots/CLAUDE.md(philosophy + cross-site patterns) - Markdown features spec:
astro-knots/context-v/specs/Codifying-a-Comprehensive-Extended-Markdown-Flavor-and-Shared-Package.md - Branch-aware LFM dev:
astro-knots/context-v/blueprints/Maintain-Branch-Aware-LFM-Dev-Mode.md - Promote surface:
astro-knots/context-v/blueprints/Build-a-Promotion-Surface-for-Investment-Opportunities.md
Built by The Lossless Group with ❤️ using Astro