Vibecode Medium
track this build5 phases, 11 steps, beginner friendly0%The software is a blog. The product is a hundred million readers and a partner program that pays writers from member fees. Neither is code, and the writers who leave for their own domain report the same thing: the words survive, the audience does not.
You are building a lean indie version of Medium. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Medium · indie build Not a Medium clone: the readership is the product and no code produces it. This is the consolation done right: your archive imported to your own domain, a typographic reading experience, syndication to Medium with canonical pointing home so the audience still finds you, and a reader list you own. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Framework | Astro, static, Markdown collections | validated content, zero client JavaScript | | Hosting | Any static host on your domain | nothing to run | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Everything in this build runs on it: the server, the scripts, the tests. - Get it: Download the LTS installer from nodejs.org, or install with your package manager (brew install node, or nvm install 22). Restart the terminal afterwards. - Verify: node --version prints v22 or higher - [ ] **A terminal and a code editor** · free - Why: Every step below is a command you type or a file you edit. - Get it: VS Code (code.visualstudio.com), Cursor or Zed. Open a folder for the project and use the editor's built-in terminal. - Verify: You can open a folder and run a command in its terminal - [ ] **Git** · free - Why: History for your code, and the way most hosts deploy. - Get it: Install from git-scm.com or with your package manager, then run git init in the project folder once it exists. - Verify: git --version prints a version - [ ] **Your Medium export** · free - Why: Phase 1 imports every past post with its date. - Get it: Medium > Settings > Security and apps > Download your information. You receive a zip of HTML files by email. - [ ] **A static host connected to the repo** · free - Why: Deploy on push. - Get it: Cloudflare Pages or Netlify. - [ ] **A domain or subdomain** (optional) · roughly $10 a year, or free on an existing domain - Why: Your archive at your address. - Get it: Register at Cloudflare Registrar, Porkbun or Namecheap, or use a subdomain of one you already own. You add one DNS record in the deploy phase. - [ ] **A Medium integration token (optional)** (optional) · free - Why: Phase 3 syndicates with canonical set. Medium's API is limited; the import-a-story tool is the fallback. - Get it: Medium > Settings > Security and apps > Integration tokens. If unavailable, use medium.com/p/import with your post URL. ## Quick start ```sh npm create astro@latest archive -- --template minimal --typescript strict --install --git ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The recommendation feed, claps, publications, Partner Program earnings: that is the network. - the readership that finds you without marketing - the Partner Program payouts - publications and their editors - claps, follows and the recommendation algorithm If one of those is essential to you, that is the reason to keep paying for Medium, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Medium The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Do not try to rebuild Medium. The blog is a weekend; the readership is the product and no prompt produces it. Build the honest consolation instead: a home for your writing you own, syndicated to Medium so the audience still finds it. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Astro, static output, Markdown content collections. Any static host, your domain. ### Phase 1 · Your archive Build: a Markdown blog with validated frontmatter (title, date, slug, tags, description, canonical), an index, tag pages, a full RSS feed and a sitemap. Export your existing Medium posts (Settings, Download your information), convert the HTML to Markdown, and import them with their original dates. Done when: every past post renders on your domain with its original date, and the feed validates. Do not build yet: syndication, signup. ### Phase 2 · Reading experience Build: a typographic layout, dark mode, reading time, heading anchors, zero client JavaScript except where a phase requires it. Done when: Lighthouse is 100 on Performance and Accessibility. ### Phase 3 · Syndication with canonical Build: a script that posts a new article to Medium through its API (or the import-a-story tool) with the canonical URL set to your domain. Medium reach, your ownership. Document that the reverse is the trap: publishing on Medium first and importing later leaves Medium canonical. Done when: a post published on your domain appears on Medium with canonical pointing home, verified in the page source. ### Phase 4 · Readers you own Build: a newsletter signup storing to SQLite behind a honeypot and a rate limit, with CSV export into a sender when you are ready. No sending in v1. Done when: a signup stores once and exports cleanly. ### Out of scope (and why) - The recommendation feed, claps, publications, Partner Program earnings. That is the network, and it is why people stay. ### README must contain - The publish-here-first, syndicate-second rule and why it matters for canonical. - The export and import path for existing posts. ===== AGENTS.md ===== # Agent instructions · Medium indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Astro, static, Markdown collections, Any static host on your domain. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ===== BUILD_PLAN.md ===== # Build plan · Medium Not a Medium clone: the readership is the product and no code produces it. This is the consolation done right: your archive imported to your own domain, a typographic reading experience, syndication to Medium with canonical pointing home so the audience still finds you, and a reader list you own. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Your archive Every past post on your domain with its original date. ### Steps 1. Create the Astro project and a posts collection with validated frontmatter ```sh npm create astro@latest archive -- --template minimal --typescript strict --install --git ``` 2. Convert the export's HTML to Markdown with original dates and canonical fields ```sh npm install turndown@7 node scripts/import-medium.mjs ~/Downloads/medium-export/posts ``` 3. Add index, tag pages, RSS with full content and a sitemap ### Done when - [ ] Every past post renders with its original date - [ ] The feed validates ## Phase 2 · Reading experience Typography worth reading, zero client JavaScript. ### Steps 1. Layout with a measured line length, dark mode, reading time, heading anchors 2. Run Lighthouse and fix what it names ### Done when - [ ] Lighthouse 100 on Performance and Accessibility ## Phase 3 · Syndication with canonical Publish here first, mirror to Medium with canonical home. ### Steps 1. Write scripts/syndicate.mjs using the integration token with canonicalUrl, or document the import tool The reverse order leaves Medium canonical; do not do it. 2. Mirror one post and verify canonical in the page source on Medium ### Done when - [ ] A post published on your domain appears on Medium with canonical pointing home ## Phase 4 · Readers you own A signup list stored locally, exported into a sender when ready. ### Steps 1. One small Node endpoint with honeypot and rate limit storing to SQLite 2. A form on every post and a CSV export ### Done when - [ ] A signup stores once - [ ] The export opens cleanly ## Phase 5 · Deploy Live on your domain, documented. ### Steps 1. Connect host and domain 2. README: publish-here-first rule, the import path, the signup export Files: `README.md` ### Done when - [ ] A new post is live and mirrored - [ ] The README states the canonical rule ## Not in this build - The recommendation feed, claps, publications, Partner Program earnings: that is the network. ## After v1, if you want it - Syndicate to Substack Notes or dev.to with the same rule ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Canonical base. SITE_URL=https://yourname.com # Optional · secret. Integration token for syndication. Empty means use the import tool by hand. MEDIUM_TOKEN=...
You are building a lean indie version of Medium. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Medium · indie build Not a Medium clone: the readership is the product and no code produces it. This is the consolation done right: your archive imported to your own domain, a typographic reading experience, syndication to Medium with canonical pointing home so the audience still finds you, and a reader list you own. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Framework | Astro, static, Markdown collections | validated content, zero client JavaScript | | Hosting | Any static host on your domain | nothing to run | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Everything in this build runs on it: the server, the scripts, the tests. - Get it: Download the LTS installer from nodejs.org, or install with your package manager (brew install node, or nvm install 22). Restart the terminal afterwards. - Verify: node --version prints v22 or higher - [ ] **A terminal and a code editor** · free - Why: Every step below is a command you type or a file you edit. - Get it: VS Code (code.visualstudio.com), Cursor or Zed. Open a folder for the project and use the editor's built-in terminal. - Verify: You can open a folder and run a command in its terminal - [ ] **Git** · free - Why: History for your code, and the way most hosts deploy. - Get it: Install from git-scm.com or with your package manager, then run git init in the project folder once it exists. - Verify: git --version prints a version - [ ] **Your Medium export** · free - Why: Phase 1 imports every past post with its date. - Get it: Medium > Settings > Security and apps > Download your information. You receive a zip of HTML files by email. - [ ] **A static host connected to the repo** · free - Why: Deploy on push. - Get it: Cloudflare Pages or Netlify. - [ ] **A domain or subdomain** (optional) · roughly $10 a year, or free on an existing domain - Why: Your archive at your address. - Get it: Register at Cloudflare Registrar, Porkbun or Namecheap, or use a subdomain of one you already own. You add one DNS record in the deploy phase. - [ ] **A Medium integration token (optional)** (optional) · free - Why: Phase 3 syndicates with canonical set. Medium's API is limited; the import-a-story tool is the fallback. - Get it: Medium > Settings > Security and apps > Integration tokens. If unavailable, use medium.com/p/import with your post URL. ## Quick start ```sh npm create astro@latest archive -- --template minimal --typescript strict --install --git ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The recommendation feed, claps, publications, Partner Program earnings: that is the network. - the readership that finds you without marketing - the Partner Program payouts - publications and their editors - claps, follows and the recommendation algorithm If one of those is essential to you, that is the reason to keep paying for Medium, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Medium The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Do not try to rebuild Medium. The blog is a weekend; the readership is the product and no prompt produces it. Build the honest consolation instead: a home for your writing you own, syndicated to Medium so the audience still finds it. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Astro, static output, Markdown content collections. Any static host, your domain. ### Phase 1 · Your archive Build: a Markdown blog with validated frontmatter (title, date, slug, tags, description, canonical), an index, tag pages, a full RSS feed and a sitemap. Export your existing Medium posts (Settings, Download your information), convert the HTML to Markdown, and import them with their original dates. Done when: every past post renders on your domain with its original date, and the feed validates. Do not build yet: syndication, signup. ### Phase 2 · Reading experience Build: a typographic layout, dark mode, reading time, heading anchors, zero client JavaScript except where a phase requires it. Done when: Lighthouse is 100 on Performance and Accessibility. ### Phase 3 · Syndication with canonical Build: a script that posts a new article to Medium through its API (or the import-a-story tool) with the canonical URL set to your domain. Medium reach, your ownership. Document that the reverse is the trap: publishing on Medium first and importing later leaves Medium canonical. Done when: a post published on your domain appears on Medium with canonical pointing home, verified in the page source. ### Phase 4 · Readers you own Build: a newsletter signup storing to SQLite behind a honeypot and a rate limit, with CSV export into a sender when you are ready. No sending in v1. Done when: a signup stores once and exports cleanly. ### Out of scope (and why) - The recommendation feed, claps, publications, Partner Program earnings. That is the network, and it is why people stay. ### README must contain - The publish-here-first, syndicate-second rule and why it matters for canonical. - The export and import path for existing posts. ===== AGENTS.md ===== # Agent instructions · Medium indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Astro, static, Markdown collections, Any static host on your domain. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ===== BUILD_PLAN.md ===== # Build plan · Medium Not a Medium clone: the readership is the product and no code produces it. This is the consolation done right: your archive imported to your own domain, a typographic reading experience, syndication to Medium with canonical pointing home so the audience still finds you, and a reader list you own. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Your archive Every past post on your domain with its original date. ### Steps 1. Create the Astro project and a posts collection with validated frontmatter ```sh npm create astro@latest archive -- --template minimal --typescript strict --install --git ``` 2. Convert the export's HTML to Markdown with original dates and canonical fields ```sh npm install turndown@7 node scripts/import-medium.mjs ~/Downloads/medium-export/posts ``` 3. Add index, tag pages, RSS with full content and a sitemap ### Done when - [ ] Every past post renders with its original date - [ ] The feed validates ## Phase 2 · Reading experience Typography worth reading, zero client JavaScript. ### Steps 1. Layout with a measured line length, dark mode, reading time, heading anchors 2. Run Lighthouse and fix what it names ### Done when - [ ] Lighthouse 100 on Performance and Accessibility ## Phase 3 · Syndication with canonical Publish here first, mirror to Medium with canonical home. ### Steps 1. Write scripts/syndicate.mjs using the integration token with canonicalUrl, or document the import tool The reverse order leaves Medium canonical; do not do it. 2. Mirror one post and verify canonical in the page source on Medium ### Done when - [ ] A post published on your domain appears on Medium with canonical pointing home ## Phase 4 · Readers you own A signup list stored locally, exported into a sender when ready. ### Steps 1. One small Node endpoint with honeypot and rate limit storing to SQLite 2. A form on every post and a CSV export ### Done when - [ ] A signup stores once - [ ] The export opens cleanly ## Phase 5 · Deploy Live on your domain, documented. ### Steps 1. Connect host and domain 2. README: publish-here-first rule, the import path, the signup export Files: `README.md` ### Done when - [ ] A new post is live and mirrored - [ ] The README states the canonical rule ## Not in this build - The recommendation feed, claps, publications, Partner Program earnings: that is the network. ## After v1, if you want it - Syndicate to Substack Notes or dev.to with the same rule ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Canonical base. SITE_URL=https://yourname.com # Optional · secret. Integration token for syndication. Empty means use the import tool by hand. MEDIUM_TOKEN=...
You are building a production product version of Medium. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Medium · product brief ## Problem The software is a blog. The product is a hundred million readers and a partner program that pays writers from member fees. Neither is code, and the writers who leave for their own domain report the same thing: the words survive, the audience does not. ## Product outcome An archive that outlives any platform, with the audience rented by canonical syndication rather than owned by Medium. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - a static host and domain ## Explicit non-goals for v1 - The recommendation feed, claps, publications, Partner Program earnings: that is the network. - the readership that finds you without marketing - the Partner Program payouts - publications and their editors - claps, follows and the recommendation algorithm ## Success criteria - Canonical verified on Medium - Lighthouse 100 - One restore of the signup database ===== BRIEF.md ===== # Build brief · Medium The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Do not try to rebuild Medium. The blog is a weekend; the readership is the product and no prompt produces it. Build the honest consolation instead: a home for your writing you own, syndicated to Medium so the audience still finds it. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Astro, static output, Markdown content collections. Any static host, your domain. ### Phase 1 · Your archive Build: a Markdown blog with validated frontmatter (title, date, slug, tags, description, canonical), an index, tag pages, a full RSS feed and a sitemap. Export your existing Medium posts (Settings, Download your information), convert the HTML to Markdown, and import them with their original dates. Done when: every past post renders on your domain with its original date, and the feed validates. Do not build yet: syndication, signup. ### Phase 2 · Reading experience Build: a typographic layout, dark mode, reading time, heading anchors, zero client JavaScript except where a phase requires it. Done when: Lighthouse is 100 on Performance and Accessibility. ### Phase 3 · Syndication with canonical Build: a script that posts a new article to Medium through its API (or the import-a-story tool) with the canonical URL set to your domain. Medium reach, your ownership. Document that the reverse is the trap: publishing on Medium first and importing later leaves Medium canonical. Done when: a post published on your domain appears on Medium with canonical pointing home, verified in the page source. ### Phase 4 · Readers you own Build: a newsletter signup storing to SQLite behind a honeypot and a rate limit, with CSV export into a sender when you are ready. No sending in v1. Done when: a signup stores once and exports cleanly. ### Out of scope (and why) - The recommendation feed, claps, publications, Partner Program earnings. That is the network, and it is why people stay. ### README must contain - The publish-here-first, syndicate-second rule and why it matters for canonical. - The export and import path for existing posts. ===== ARCHITECTURE.md ===== # Architecture · Medium ## Stack | Part | Choice | Why | | --- | --- | --- | | Framework | Astro, static, Markdown collections | validated content, zero client JavaScript | | Hosting | Any static host on your domain | nothing to run | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Archive | posts and the import script | Any Markdown source | | Site | Astro layouts and feeds | Another generator | | Syndicate | the canonical rule and script | Add other platforms with the same rule | | Signup | the endpoint | A hosted form | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `SITE_URL` · required · Canonical base. - `MEDIUM_TOKEN` · optional, secret · Integration token for syndication. Empty means use the import tool by hand. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Medium product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Astro, static, Markdown collections, Any static host on your domain. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ===== MILESTONES.md ===== # Delivery milestones · Medium Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Your archive Every past post on your domain with its original date. ### Steps 1. Create the Astro project and a posts collection with validated frontmatter ```sh npm create astro@latest archive -- --template minimal --typescript strict --install --git ``` 2. Convert the export's HTML to Markdown with original dates and canonical fields ```sh npm install turndown@7 node scripts/import-medium.mjs ~/Downloads/medium-export/posts ``` 3. Add index, tag pages, RSS with full content and a sitemap ### Done when - [ ] Every past post renders with its original date - [ ] The feed validates ## M2 · Reading experience Typography worth reading, zero client JavaScript. ### Steps 1. Layout with a measured line length, dark mode, reading time, heading anchors 2. Run Lighthouse and fix what it names ### Done when - [ ] Lighthouse 100 on Performance and Accessibility ## M3 · Syndication with canonical Publish here first, mirror to Medium with canonical home. ### Steps 1. Write scripts/syndicate.mjs using the integration token with canonicalUrl, or document the import tool The reverse order leaves Medium canonical; do not do it. 2. Mirror one post and verify canonical in the page source on Medium ### Done when - [ ] A post published on your domain appears on Medium with canonical pointing home ## M4 · Readers you own A signup list stored locally, exported into a sender when ready. ### Steps 1. One small Node endpoint with honeypot and rate limit storing to SQLite 2. A form on every post and a CSV export ### Done when - [ ] A signup stores once - [ ] The export opens cleanly ## M5 · Deploy Live on your domain, documented. ### Steps 1. Connect host and domain 2. README: publish-here-first rule, the import path, the signup export Files: `README.md` ### Done when - [ ] A new post is live and mirrored - [ ] The README states the canonical rule ## M6 · Operate the signup endpoint (production only) Monitor and back up the one server you run. ### Steps 1. /healthz, uptime check, structured logs 2. Nightly backup off the box and one restore ### Done when - [ ] An alert fires when the endpoint stops - [ ] A restore contains yesterday's signups ===== OPERATIONS.md ===== # Operations · Medium ## Backup The repo; the signup database nightly. ## Restore Redeploy; copy the database. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Uptime on both. ## Incident checklist Revert with git. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Canonical verified on Medium - [ ] Lighthouse 100 - [ ] One restore of the signup database ## Launch constraint Do not market omitted Medium capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Canonical base. SITE_URL=https://yourname.com # Optional · secret. Integration token for syndication. Empty means use the import tool by hand. MEDIUM_TOKEN=...
# Medium · indie build Not a Medium clone: the readership is the product and no code produces it. This is the consolation done right: your archive imported to your own domain, a typographic reading experience, syndication to Medium with canonical pointing home so the audience still finds you, and a reader list you own. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Framework | Astro, static, Markdown collections | validated content, zero client JavaScript | | Hosting | Any static host on your domain | nothing to run | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Everything in this build runs on it: the server, the scripts, the tests. - Get it: Download the LTS installer from nodejs.org, or install with your package manager (brew install node, or nvm install 22). Restart the terminal afterwards. - Verify: node --version prints v22 or higher - [ ] **A terminal and a code editor** · free - Why: Every step below is a command you type or a file you edit. - Get it: VS Code (code.visualstudio.com), Cursor or Zed. Open a folder for the project and use the editor's built-in terminal. - Verify: You can open a folder and run a command in its terminal - [ ] **Git** · free - Why: History for your code, and the way most hosts deploy. - Get it: Install from git-scm.com or with your package manager, then run git init in the project folder once it exists. - Verify: git --version prints a version - [ ] **Your Medium export** · free - Why: Phase 1 imports every past post with its date. - Get it: Medium > Settings > Security and apps > Download your information. You receive a zip of HTML files by email. - [ ] **A static host connected to the repo** · free - Why: Deploy on push. - Get it: Cloudflare Pages or Netlify. - [ ] **A domain or subdomain** (optional) · roughly $10 a year, or free on an existing domain - Why: Your archive at your address. - Get it: Register at Cloudflare Registrar, Porkbun or Namecheap, or use a subdomain of one you already own. You add one DNS record in the deploy phase. - [ ] **A Medium integration token (optional)** (optional) · free - Why: Phase 3 syndicates with canonical set. Medium's API is limited; the import-a-story tool is the fallback. - Get it: Medium > Settings > Security and apps > Integration tokens. If unavailable, use medium.com/p/import with your post URL. ## Quick start ```sh npm create astro@latest archive -- --template minimal --typescript strict --install --git ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The recommendation feed, claps, publications, Partner Program earnings: that is the network. - the readership that finds you without marketing - the Partner Program payouts - publications and their editors - claps, follows and the recommendation algorithm If one of those is essential to you, that is the reason to keep paying for Medium, and the README should say so rather than pretend.
# Build brief · Medium The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Do not try to rebuild Medium. The blog is a weekend; the readership is the product and no prompt produces it. Build the honest consolation instead: a home for your writing you own, syndicated to Medium so the audience still finds it. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Astro, static output, Markdown content collections. Any static host, your domain. ### Phase 1 · Your archive Build: a Markdown blog with validated frontmatter (title, date, slug, tags, description, canonical), an index, tag pages, a full RSS feed and a sitemap. Export your existing Medium posts (Settings, Download your information), convert the HTML to Markdown, and import them with their original dates. Done when: every past post renders on your domain with its original date, and the feed validates. Do not build yet: syndication, signup. ### Phase 2 · Reading experience Build: a typographic layout, dark mode, reading time, heading anchors, zero client JavaScript except where a phase requires it. Done when: Lighthouse is 100 on Performance and Accessibility. ### Phase 3 · Syndication with canonical Build: a script that posts a new article to Medium through its API (or the import-a-story tool) with the canonical URL set to your domain. Medium reach, your ownership. Document that the reverse is the trap: publishing on Medium first and importing later leaves Medium canonical. Done when: a post published on your domain appears on Medium with canonical pointing home, verified in the page source. ### Phase 4 · Readers you own Build: a newsletter signup storing to SQLite behind a honeypot and a rate limit, with CSV export into a sender when you are ready. No sending in v1. Done when: a signup stores once and exports cleanly. ### Out of scope (and why) - The recommendation feed, claps, publications, Partner Program earnings. That is the network, and it is why people stay. ### README must contain - The publish-here-first, syndicate-second rule and why it matters for canonical. - The export and import path for existing posts.
# Agent instructions · Medium indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Astro, static, Markdown collections, Any static host on your domain. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs".
# Build plan · Medium Not a Medium clone: the readership is the product and no code produces it. This is the consolation done right: your archive imported to your own domain, a typographic reading experience, syndication to Medium with canonical pointing home so the audience still finds you, and a reader list you own. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Your archive Every past post on your domain with its original date. ### Steps 1. Create the Astro project and a posts collection with validated frontmatter ```sh npm create astro@latest archive -- --template minimal --typescript strict --install --git ``` 2. Convert the export's HTML to Markdown with original dates and canonical fields ```sh npm install turndown@7 node scripts/import-medium.mjs ~/Downloads/medium-export/posts ``` 3. Add index, tag pages, RSS with full content and a sitemap ### Done when - [ ] Every past post renders with its original date - [ ] The feed validates ## Phase 2 · Reading experience Typography worth reading, zero client JavaScript. ### Steps 1. Layout with a measured line length, dark mode, reading time, heading anchors 2. Run Lighthouse and fix what it names ### Done when - [ ] Lighthouse 100 on Performance and Accessibility ## Phase 3 · Syndication with canonical Publish here first, mirror to Medium with canonical home. ### Steps 1. Write scripts/syndicate.mjs using the integration token with canonicalUrl, or document the import tool The reverse order leaves Medium canonical; do not do it. 2. Mirror one post and verify canonical in the page source on Medium ### Done when - [ ] A post published on your domain appears on Medium with canonical pointing home ## Phase 4 · Readers you own A signup list stored locally, exported into a sender when ready. ### Steps 1. One small Node endpoint with honeypot and rate limit storing to SQLite 2. A form on every post and a CSV export ### Done when - [ ] A signup stores once - [ ] The export opens cleanly ## Phase 5 · Deploy Live on your domain, documented. ### Steps 1. Connect host and domain 2. README: publish-here-first rule, the import path, the signup export Files: `README.md` ### Done when - [ ] A new post is live and mirrored - [ ] The README states the canonical rule ## Not in this build - The recommendation feed, claps, publications, Partner Program earnings: that is the network. ## After v1, if you want it - Syndicate to Substack Notes or dev.to with the same rule
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Canonical base. SITE_URL=https://yourname.com # Optional · secret. Integration token for syndication. Empty means use the import tool by hand. MEDIUM_TOKEN=...
# Medium · product brief ## Problem The software is a blog. The product is a hundred million readers and a partner program that pays writers from member fees. Neither is code, and the writers who leave for their own domain report the same thing: the words survive, the audience does not. ## Product outcome An archive that outlives any platform, with the audience rented by canonical syndication rather than owned by Medium. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - a static host and domain ## Explicit non-goals for v1 - The recommendation feed, claps, publications, Partner Program earnings: that is the network. - the readership that finds you without marketing - the Partner Program payouts - publications and their editors - claps, follows and the recommendation algorithm ## Success criteria - Canonical verified on Medium - Lighthouse 100 - One restore of the signup database
# Build brief · Medium The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Do not try to rebuild Medium. The blog is a weekend; the readership is the product and no prompt produces it. Build the honest consolation instead: a home for your writing you own, syndicated to Medium so the audience still finds it. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Astro, static output, Markdown content collections. Any static host, your domain. ### Phase 1 · Your archive Build: a Markdown blog with validated frontmatter (title, date, slug, tags, description, canonical), an index, tag pages, a full RSS feed and a sitemap. Export your existing Medium posts (Settings, Download your information), convert the HTML to Markdown, and import them with their original dates. Done when: every past post renders on your domain with its original date, and the feed validates. Do not build yet: syndication, signup. ### Phase 2 · Reading experience Build: a typographic layout, dark mode, reading time, heading anchors, zero client JavaScript except where a phase requires it. Done when: Lighthouse is 100 on Performance and Accessibility. ### Phase 3 · Syndication with canonical Build: a script that posts a new article to Medium through its API (or the import-a-story tool) with the canonical URL set to your domain. Medium reach, your ownership. Document that the reverse is the trap: publishing on Medium first and importing later leaves Medium canonical. Done when: a post published on your domain appears on Medium with canonical pointing home, verified in the page source. ### Phase 4 · Readers you own Build: a newsletter signup storing to SQLite behind a honeypot and a rate limit, with CSV export into a sender when you are ready. No sending in v1. Done when: a signup stores once and exports cleanly. ### Out of scope (and why) - The recommendation feed, claps, publications, Partner Program earnings. That is the network, and it is why people stay. ### README must contain - The publish-here-first, syndicate-second rule and why it matters for canonical. - The export and import path for existing posts.
# Architecture · Medium ## Stack | Part | Choice | Why | | --- | --- | --- | | Framework | Astro, static, Markdown collections | validated content, zero client JavaScript | | Hosting | Any static host on your domain | nothing to run | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Archive | posts and the import script | Any Markdown source | | Site | Astro layouts and feeds | Another generator | | Syndicate | the canonical rule and script | Add other platforms with the same rule | | Signup | the endpoint | A hosted form | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `SITE_URL` · required · Canonical base. - `MEDIUM_TOKEN` · optional, secret · Integration token for syndication. Empty means use the import tool by hand. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Medium product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Astro, static, Markdown collections, Any static host on your domain. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone.
# Delivery milestones · Medium Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Your archive Every past post on your domain with its original date. ### Steps 1. Create the Astro project and a posts collection with validated frontmatter ```sh npm create astro@latest archive -- --template minimal --typescript strict --install --git ``` 2. Convert the export's HTML to Markdown with original dates and canonical fields ```sh npm install turndown@7 node scripts/import-medium.mjs ~/Downloads/medium-export/posts ``` 3. Add index, tag pages, RSS with full content and a sitemap ### Done when - [ ] Every past post renders with its original date - [ ] The feed validates ## M2 · Reading experience Typography worth reading, zero client JavaScript. ### Steps 1. Layout with a measured line length, dark mode, reading time, heading anchors 2. Run Lighthouse and fix what it names ### Done when - [ ] Lighthouse 100 on Performance and Accessibility ## M3 · Syndication with canonical Publish here first, mirror to Medium with canonical home. ### Steps 1. Write scripts/syndicate.mjs using the integration token with canonicalUrl, or document the import tool The reverse order leaves Medium canonical; do not do it. 2. Mirror one post and verify canonical in the page source on Medium ### Done when - [ ] A post published on your domain appears on Medium with canonical pointing home ## M4 · Readers you own A signup list stored locally, exported into a sender when ready. ### Steps 1. One small Node endpoint with honeypot and rate limit storing to SQLite 2. A form on every post and a CSV export ### Done when - [ ] A signup stores once - [ ] The export opens cleanly ## M5 · Deploy Live on your domain, documented. ### Steps 1. Connect host and domain 2. README: publish-here-first rule, the import path, the signup export Files: `README.md` ### Done when - [ ] A new post is live and mirrored - [ ] The README states the canonical rule ## M6 · Operate the signup endpoint (production only) Monitor and back up the one server you run. ### Steps 1. /healthz, uptime check, structured logs 2. Nightly backup off the box and one restore ### Done when - [ ] An alert fires when the endpoint stops - [ ] A restore contains yesterday's signups
# Operations · Medium ## Backup The repo; the signup database nightly. ## Restore Redeploy; copy the database. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Uptime on both. ## Incident checklist Revert with git. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Canonical verified on Medium - [ ] Lighthouse 100 - [ ] One restore of the signup database ## Launch constraint Do not market omitted Medium capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Canonical base. SITE_URL=https://yourname.com # Optional · secret. Integration token for syndication. Empty means use the import tool by hand. MEDIUM_TOKEN=...
$ choose a build depth, inspect the files, then open the complete pack in your agent
Readers pay to read without a meter and to fund writers; writers stay because a first post can reach thousands of people on day one, which no personal domain does.
xthe readership that finds you without marketing
xthe Partner Program payouts
xpublications and their editors
xclaps, follows and the recommendation algorithm
Medium pricing
member$5/mo · monthly flat · $60/yr
free tierReading is metered without membership; publishing is free.
verified 2026-09-04 · source ↗
Is Medium free?
Reading is metered without membership; publishing is free. Paid is Member at $5/mo (checked 2026-09-04).
Vibecode Medium
Not really. Medium's value is not the code: Own your words, rent their audience with canonical links. See the honest breakdown above.
How much does Medium cost?
Medium costs about $5/month (Member, checked 2026-09-04), which is $60 per year.
What do I lose by replacing Medium?
Honestly: the readership that finds you without marketing; the Partner Program payouts; publications and their editors; claps, follows and the recommendation algorithm. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Medium?
Yes: WriteFreely (minimal federated writing platform). Using prior art is also vibecoding; the prompt is for when you want it exactly your way.