WEBVTT - The full stack of an agent-era blog

1
00:00:00.000 --> 00:00:03.674
This post is the technical map of everything running behind mondello.dev right now. Not a tutorial — a reference. If you're building something similar, this is the stack trace.

2
00:00:03.674 --> 00:00:03.928
The layers

3
00:00:03.928 --> 00:00:07.982
The blog runs as a single Cloudflare Worker. One binary, one deployment, one wrangler deploy command. Everything underneath is bindings, integrations, and plugins — no separate services, no containers, no multi-deploy pipeline.

4
00:00:07.982 --> 00:00:08.362
Compute: Cloudflare Workers

5
00:00:08.362 --> 00:00:10.009
The Worker is an Astro 6 server-rendered application using @astrojs/cloudflare as the adapter.

6
00:00:10.009 --> 00:00:11.023
Every page is rendered on-demand at the edge.

7
00:00:11.023 --> 00:00:15.077
There is no build step that produces static HTML — Astro's output: "server" mode means the entire site is dynamic, which is what makes EmDash's admin, preview mode, and Live Collections work.

8
00:00:15.077 --> 00:00:18.498
The Worker costs $0/month on the free tier. The only thing I'm missing is Dynamic Worker Loaders (sandboxed plugin execution), which requires the $5/month Workers Paid plan.

9
00:00:18.498 --> 00:00:18.878
Database: Cloudflare D1

10
00:00:18.878 --> 00:00:21.285
All content, media metadata, users, settings, menus, widgets, taxonomies, bylines, and plugin state live in a single D1 database.

11
00:00:21.285 --> 00:00:22.045
D1 is SQLite-over-HTTP with read replicas.

12
00:00:22.045 --> 00:00:24.452
EmDash manages the schema entirely — 51 tables as of this writing, including FTS5 virtual tables for full-text search.

13
00:00:24.452 --> 00:00:25.213
I don't touch the schema directly.

14
00:00:25.213 --> 00:00:27.113
EmDash's seed system creates the initial tables on first boot, and the ORM handles migrations.

15
00:00:27.113 --> 00:00:30.407
The only raw SQL I've written is for bulk operations (seeding skills, creating tags, populating byline assignments) where the REST API was slower or missing features.

16
00:00:30.407 --> 00:00:30.787
Storage: Cloudflare R2

17
00:00:30.787 --> 00:00:32.181
Media files (hero images, narration audio) live in an R2 bucket.

18
00:00:32.181 --> 00:00:32.814
EmDash serves them through /_emdash/api/media/file/{storageKey}.

19
00:00:32.814 --> 00:00:33.701
R2 is S3-compatible with zero egress fees.

20
00:00:33.701 --> 00:00:35.475
The backup cron also writes JSON table dumps to R2 under a backups/ prefix.

21
00:00:35.475 --> 00:00:35.855
Sessions: Cloudflare KV

22
00:00:35.855 --> 00:00:39.910
The @astrojs/cloudflare adapter auto-provisions a KV namespace for session storage. EmDash's passkey auth uses it to persist login sessions across requests. I don't configure it manually — it's injected at deploy time.

23
00:00:39.910 --> 00:00:40.290
CMS: EmDash 0.1.1

24
00:00:40.290 --> 00:00:41.303
EmDash is the entire content layer. It provides:

25
00:00:41.303 --> 00:00:42.697
Admin panel at /_emdash/admin with visual editing, media library, plugin manager

26
00:00:42.697 --> 00:00:43.710
REST API at /_emdash/api/* for programmatic content management

27
00:00:43.710 --> 00:00:44.597
MCP server at /_emdash/api/mcp for agent-to-site interaction

28
00:00:44.597 --> 00:00:46.118
Plugin system with definePlugin(), KV storage, admin widgets, lifecycle hooks, API routes

29
00:00:46.118 --> 00:00:46.498
Passkey-first authentication (WebAuthn)

30
00:00:46.498 --> 00:00:47.638
Portable Text content model (structured JSON, not serialized HTML)

31
00:00:47.638 --> 00:00:47.891
WordPress importer

32
00:00:47.891 --> 00:00:48.398
Full-text search via FTS5

33
00:00:48.398 --> 00:00:49.158
Sitemap, robots.txt, and SEO metadata generation

34
00:00:49.158 --> 00:00:50.679
I'm using 5 registered plugins, all running in-process (trusted mode, no sandboxing):

35
00:00:50.679 --> 00:00:50.805
1.

36
00:00:50.805 --> 00:00:51.819
emdash-forms — first-party form builder + submissions 2.

37
00:00:51.819 --> 00:00:52.833
posthog-analytics — analytics snippet with admin settings 3.

38
00:00:52.833 --> 00:00:53.213
agent-seo — robots.

39
00:00:53.213 --> 00:00:53.593
txt + llms.

40
00:00:53.593 --> 00:00:54.353
txt generation with bot catalog 4.

41
00:00:54.353 --> 00:00:55.240
minimax-image — hero image generation settings 5.

42
00:00:55.240 --> 00:00:55.873
minimax-tts — narration audio settings

43
00:00:55.873 --> 00:00:56.127
Payments: x402

44
00:00:56.127 --> 00:00:57.014
The @emdash-cms/x402 integration provides per-page payment enforcement.

45
00:00:57.014 --> 00:00:59.674
When an agent fetches a gated page, the middleware returns a 402 response with payment instructions (wallet, network, price, facilitator URL).

46
00:00:59.674 --> 00:01:01.195
The agent's x402 client settles USDC on Base and re-fetches with proof.

47
00:01:01.195 --> 00:01:04.615
Configuration is 6 lines in astro.config.mjs. The wallet is my hardware wallet (0x0E4d...1028). Revenue goes directly to me — no intermediary, no marketplace fee, no account system.

48
00:01:04.615 --> 00:01:06.262
Currently enforcing on /skills/[slug] and /skills/[slug]/raw with botOnly: true so humans browse free.

49
00:01:06.262 --> 00:01:06.643
Media generation: MiniMax

50
00:01:06.643 --> 00:01:08.796
Two Worker bridge routes call the MiniMax platform API using a secret stored in the Worker environment:

51
00:01:08.796 --> 00:01:10.190
/xmm/image — generates hero images via MiniMax image-01 with style presets

52
00:01:10.190 --> 00:01:12.217
/xmm/tts — generates narration audio via MiniMax speech-2.8-hd with automatic Portable Text to narration script conversion

53
00:01:12.217 --> 00:01:14.498
Both routes are admin-gated (require authenticated session + x-emdash-request CSRF header). The API key never leaves the Worker.

54
00:01:14.498 --> 00:01:14.751
Agent discovery

55
00:01:14.751 --> 00:01:15.891
Four files make the site discoverable to LLM agents:

56
00:01:15.891 --> 00:01:17.285
/llms.txt — discovery manifest per llmstxt.org listing all posts and pages

57
00:01:17.285 --> 00:01:18.425
/llms-full.txt — full post content inlined for single-fetch ingestion

58
00:01:18.425 --> 00:01:19.692
/robots.txt — explicit Allow: / for 16 known AI crawlers

59
00:01:19.692 --> 00:01:20.452
/.well-known/oauth-protected-resource — MCP server discovery metadata

60
00:01:20.452 --> 00:01:22.226
Plus RSS autodiscovery (&lt;link rel="alternate" type="application/rss+xml"&gt;) and the sitemap at /sitemap.xml with 9 URLs.

61
00:01:22.226 --> 00:01:22.480
Automated backups

62
00:01:22.480 --> 00:01:25.140
Four Cloudflare Cron Triggers fire the Worker's scheduled() handler, which self-invokes /api/backup to dump 19 D1 tables as JSON into R2:

63
00:01:25.140 --> 00:01:25.647
Hourly at :17 past

64
00:01:25.647 --> 00:01:26.154
Daily at 03:43 UTC

65
00:01:26.154 --> 00:01:26.914
Weekly on Sundays at 04:23 UTC

66
00:01:26.914 --> 00:01:27.801
Monthly on the 1st at 05:53 UTC

67
00:01:27.801 --> 00:01:29.448
Each backup writes a _manifest.json with table counts, byte totals, and error log.

68
00:01:29.448 --> 00:01:29.701
Content inventory

69
00:01:29.701 --> 00:01:31.475
4 published blog posts with hero images, bylines, and 13 tags across 16 assignments

70
00:01:31.475 --> 00:01:33.122
4 published skills in a custom "skills" collection with raw_markdown for agent download

71
00:01:33.122 --> 00:01:33.502
1 About page

72
00:01:33.502 --> 00:01:34.389
Primary nav menu (Posts, Skills, RSS, About)

73
00:01:34.389 --> 00:01:34.769
What's not here

74
00:01:34.769 --> 00:01:36.416
No CI/CD pipeline. pnpm run deploy from the laptop is the deploy mechanism.

75
00:01:36.416 --> 00:01:37.937
No custom theme. Using the default EmDash blog-cloudflare template with minor fixes.

76
00:01:37.937 --> 00:01:39.457
No email/newsletter. The forms plugin is registered but no forms are configured.

77
00:01:39.457 --> 00:01:41.104
No session recording or advanced analytics. PostHog plugin is registered but not keyed.

78
00:01:41.104 --> 00:01:42.118
No Stripe. x402 is the only payment path.

79
00:01:42.118 --> 00:01:42.244
Cost

80
00:01:42.244 --> 00:01:46.299
$0/month. Everything runs on Cloudflare's free tier. The only paid upgrade that would change anything is Workers Paid ($5/month) to unlock Dynamic Worker Loaders for sandboxed plugin execution and the EmDash marketplace.

81
00:01:46.299 --> 00:01:49.593
The MiniMax API is on a dev-tier key with usage limits. The hero images and narration audio were generated within those limits at zero dollar cost.

82
00:01:49.593 --> 00:01:52.000
Total infrastructure spend for a production blog with micropayment revenue, agent discovery, automated backups, and a skills marketplace: zero.
