Vibecode Bear Blog
track this build5 phases, 10 steps, beginner friendly0%Bear is a deliberate minimum: Markdown, a list, RSS, no JavaScript. A static generator with a strict no-bloat rule reproduces it in a sitting. What you cannot reproduce is the discover feed of other Bear blogs, which is small and beloved.
You are building a lean indie version of Bear Blog. 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 ===== # Bear Blog · indie build A minimal blog with Bear's constitution: Markdown in, static HTML out, zero client-side JavaScript and one stylesheet under 5 KB. Tags, RSS, a sitemap, optional privacy analytics, and a custom domain. Everything you add is a reason it stops being Bear. Estimated effort: **one sitting**. 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 | | --- | --- | --- | | Build | One Node script with markdown-it, pinned | no framework, no bundler | | Styling | One inlined stylesheet under 5 KB | the constraint that makes it Bear | | 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 existing posts as Markdown** · free - Why: Phase 1 renders them. - Get it: Bear: Dashboard > Settings > Export. Otherwise copy your posts into Markdown files with a date. - [ ] **A static host** · free - Why: Deploy on push. - Get it: Cloudflare Pages, Netlify or GitHub Pages connected to the repo. - [ ] **A domain or subdomain** (optional) · roughly $10 a year, or free on an existing domain - Why: The point of leaving is a domain you own. - 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. ## Quick start ```sh mkdir bear && cd bear && git init && npm init -y && npm pkg set type=module && npm install markdown-it@14 gray-matter@4 mkdir -p posts static && cp .env.example .env ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The Bear discover feed; it is the community. - Newsletters, comments, themes. - the Bear discover feed and its upvotes - hosted privacy analytics - the built-in newsletter - someone else keeping it online for $5 If one of those is essential to you, that is the reason to keep paying for Bear Blog, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Bear Blog 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. Build me a minimal blog like Bear Blog. 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. The constraint that makes this Bear and not another blog: the built site ships zero client-side JavaScript and one stylesheet under 5 kB. Treat any phase that breaks that as failed. ### Stack (fixed, do not substitute) - A single Node build script rendering Markdown to static HTML. No framework, no bundler. marked or markdown-it for Markdown, pinned. - Deploy to any static host behind a custom domain. ### Data model (create this before Phase 1) posts/*.md with frontmatter: title, date, slug, tags, draft. The folder is the CMS. ### Phase 1 · Build pipeline Build: build.mjs reads posts, renders dist/index.html (a plain list, newest first) and dist/:slug/index.html, copies static assets. Drafts excluded. A frontmatter error fails the build with the file named. Done when: adding a post and rebuilding adds it to the list, a draft is absent, and a broken frontmatter fails loudly. Do not build yet: styling, feeds. ### Phase 2 · The stylesheet Build: one stylesheet, inlined, under 5 kB · a measured line length, system font stack, dark mode via prefers-color-scheme, readable code blocks, and nothing else. Done when: the built stylesheet is under 5 kB, the page ships no script tag, and Lighthouse scores 100 across the board. ### Phase 3 · Tags and RSS Build: /tags/:tag pages, a full-content RSS feed, a sitemap, and a robots.txt. Done when: the feed validates and every tag page lists only its posts. ### Phase 4 · Privacy analytics Build: a static site cannot count visitors alone; add one tiny server endpoint that logs a daily-salted hash of IP and user agent per path, and a /stats page behind basic auth. Or skip it and read your host's logs. Choose, and write the choice down. Done when: pageviews for today match a count of the day's rows, and no raw IP is stored anywhere. ### Phase 5 · Deploy Build: host config, the custom domain, and the README with the "how to write a post" path. Done when: a new post goes from a Markdown file to live on your domain with one command. ### Out of scope (and why) - The Bear discover feed. It is the community, not the code. - Newsletters, comments, themes. Every one of them is a reason this stops being Bear. ### README must contain - The zero-JavaScript rule, stated as the project's constitution. - How to add a post in three lines. ===== AGENTS.md ===== # Agent instructions · Bear Blog indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: One Node script with markdown-it, pinned, One inlined stylesheet under 5 KB, 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 · Bear Blog A minimal blog with Bear's constitution: Markdown in, static HTML out, zero client-side JavaScript and one stylesheet under 5 KB. Tags, RSS, a sitemap, optional privacy analytics, and a custom domain. Everything you add is a reason it stops being Bear. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Build pipeline posts/*.md to dist/, drafts excluded, frontmatter errors loud. ### Steps 1. Create the project and build.mjs Read posts/*.md with frontmatter (title, date, slug, tags, draft), render index.html newest first and one page per post, copy static assets. Files: `build.mjs` ```sh mkdir bear && cd bear && git init && npm init -y && npm pkg set type=module && npm install markdown-it@14 gray-matter@4 mkdir -p posts static && cp .env.example .env ``` 2. Fail the build on a missing title or date, naming the file ### Done when - [ ] Adding a post and rebuilding adds it to the list - [ ] A draft is absent - [ ] A broken frontmatter fails loudly ## Phase 2 · The stylesheet Under 5 KB, inlined, no script tag anywhere. ### Steps 1. Write styles.css: measured line length, system fonts, dark mode via prefers-color-scheme, readable code blocks Inline it in build.mjs. 2. Add a build check that fails if the CSS exceeds 5 KB or any script tag appears in dist/ ### Done when - [ ] The built stylesheet is under 5 KB - [ ] The page ships no script tag - [ ] Lighthouse 100 across the board ## Phase 3 · Tags and RSS Tag pages, a full-content feed, sitemap, robots. ### Steps 1. Render /tags/:tag pages 2. Render feed.xml with full content, sitemap.xml and robots.txt ### Done when - [ ] The feed validates - [ ] Every tag page lists only its posts ## Phase 4 · Privacy analytics (optional) Pageviews without a cookie or a raw IP, or nothing at all. ### Steps 1. Decide: read the host's logs, or add one tiny endpoint The endpoint logs a daily-salted hash of IP and user agent per path; /stats behind basic auth. Write the decision down. 2. If you add the endpoint, keep the page's zero-JavaScript rule by using a 1x1 image request, not a script ### Done when - [ ] Pageviews for today match the day's rows - [ ] No raw IP stored anywhere - [ ] Still no script tag in the HTML ## Phase 5 · Deploy One command from Markdown to live. ### Steps 1. Connect the host and the custom domain 2. Write the README with the zero-JavaScript constitution and how to add a post in three lines Files: `README.md` ### Done when - [ ] A new post goes from file to live with one command - [ ] The README states the constitution ## Not in this build - The Bear discover feed; it is the community. - Newsletters, comments, themes. ## After v1, if you want it - An email-me-new-posts option via RSS-to-email you do not run ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Canonical base for RSS and OG. SITE_URL=https://yourname.blog
You are building a lean indie version of Bear Blog. 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 ===== # Bear Blog · indie build A minimal blog with Bear's constitution: Markdown in, static HTML out, zero client-side JavaScript and one stylesheet under 5 KB. Tags, RSS, a sitemap, optional privacy analytics, and a custom domain. Everything you add is a reason it stops being Bear. Estimated effort: **one sitting**. 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 | | --- | --- | --- | | Build | One Node script with markdown-it, pinned | no framework, no bundler | | Styling | One inlined stylesheet under 5 KB | the constraint that makes it Bear | | 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 existing posts as Markdown** · free - Why: Phase 1 renders them. - Get it: Bear: Dashboard > Settings > Export. Otherwise copy your posts into Markdown files with a date. - [ ] **A static host** · free - Why: Deploy on push. - Get it: Cloudflare Pages, Netlify or GitHub Pages connected to the repo. - [ ] **A domain or subdomain** (optional) · roughly $10 a year, or free on an existing domain - Why: The point of leaving is a domain you own. - 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. ## Quick start ```sh mkdir bear && cd bear && git init && npm init -y && npm pkg set type=module && npm install markdown-it@14 gray-matter@4 mkdir -p posts static && cp .env.example .env ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The Bear discover feed; it is the community. - Newsletters, comments, themes. - the Bear discover feed and its upvotes - hosted privacy analytics - the built-in newsletter - someone else keeping it online for $5 If one of those is essential to you, that is the reason to keep paying for Bear Blog, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Bear Blog 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. Build me a minimal blog like Bear Blog. 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. The constraint that makes this Bear and not another blog: the built site ships zero client-side JavaScript and one stylesheet under 5 kB. Treat any phase that breaks that as failed. ### Stack (fixed, do not substitute) - A single Node build script rendering Markdown to static HTML. No framework, no bundler. marked or markdown-it for Markdown, pinned. - Deploy to any static host behind a custom domain. ### Data model (create this before Phase 1) posts/*.md with frontmatter: title, date, slug, tags, draft. The folder is the CMS. ### Phase 1 · Build pipeline Build: build.mjs reads posts, renders dist/index.html (a plain list, newest first) and dist/:slug/index.html, copies static assets. Drafts excluded. A frontmatter error fails the build with the file named. Done when: adding a post and rebuilding adds it to the list, a draft is absent, and a broken frontmatter fails loudly. Do not build yet: styling, feeds. ### Phase 2 · The stylesheet Build: one stylesheet, inlined, under 5 kB · a measured line length, system font stack, dark mode via prefers-color-scheme, readable code blocks, and nothing else. Done when: the built stylesheet is under 5 kB, the page ships no script tag, and Lighthouse scores 100 across the board. ### Phase 3 · Tags and RSS Build: /tags/:tag pages, a full-content RSS feed, a sitemap, and a robots.txt. Done when: the feed validates and every tag page lists only its posts. ### Phase 4 · Privacy analytics Build: a static site cannot count visitors alone; add one tiny server endpoint that logs a daily-salted hash of IP and user agent per path, and a /stats page behind basic auth. Or skip it and read your host's logs. Choose, and write the choice down. Done when: pageviews for today match a count of the day's rows, and no raw IP is stored anywhere. ### Phase 5 · Deploy Build: host config, the custom domain, and the README with the "how to write a post" path. Done when: a new post goes from a Markdown file to live on your domain with one command. ### Out of scope (and why) - The Bear discover feed. It is the community, not the code. - Newsletters, comments, themes. Every one of them is a reason this stops being Bear. ### README must contain - The zero-JavaScript rule, stated as the project's constitution. - How to add a post in three lines. ===== AGENTS.md ===== # Agent instructions · Bear Blog indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: One Node script with markdown-it, pinned, One inlined stylesheet under 5 KB, 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 · Bear Blog A minimal blog with Bear's constitution: Markdown in, static HTML out, zero client-side JavaScript and one stylesheet under 5 KB. Tags, RSS, a sitemap, optional privacy analytics, and a custom domain. Everything you add is a reason it stops being Bear. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Build pipeline posts/*.md to dist/, drafts excluded, frontmatter errors loud. ### Steps 1. Create the project and build.mjs Read posts/*.md with frontmatter (title, date, slug, tags, draft), render index.html newest first and one page per post, copy static assets. Files: `build.mjs` ```sh mkdir bear && cd bear && git init && npm init -y && npm pkg set type=module && npm install markdown-it@14 gray-matter@4 mkdir -p posts static && cp .env.example .env ``` 2. Fail the build on a missing title or date, naming the file ### Done when - [ ] Adding a post and rebuilding adds it to the list - [ ] A draft is absent - [ ] A broken frontmatter fails loudly ## Phase 2 · The stylesheet Under 5 KB, inlined, no script tag anywhere. ### Steps 1. Write styles.css: measured line length, system fonts, dark mode via prefers-color-scheme, readable code blocks Inline it in build.mjs. 2. Add a build check that fails if the CSS exceeds 5 KB or any script tag appears in dist/ ### Done when - [ ] The built stylesheet is under 5 KB - [ ] The page ships no script tag - [ ] Lighthouse 100 across the board ## Phase 3 · Tags and RSS Tag pages, a full-content feed, sitemap, robots. ### Steps 1. Render /tags/:tag pages 2. Render feed.xml with full content, sitemap.xml and robots.txt ### Done when - [ ] The feed validates - [ ] Every tag page lists only its posts ## Phase 4 · Privacy analytics (optional) Pageviews without a cookie or a raw IP, or nothing at all. ### Steps 1. Decide: read the host's logs, or add one tiny endpoint The endpoint logs a daily-salted hash of IP and user agent per path; /stats behind basic auth. Write the decision down. 2. If you add the endpoint, keep the page's zero-JavaScript rule by using a 1x1 image request, not a script ### Done when - [ ] Pageviews for today match the day's rows - [ ] No raw IP stored anywhere - [ ] Still no script tag in the HTML ## Phase 5 · Deploy One command from Markdown to live. ### Steps 1. Connect the host and the custom domain 2. Write the README with the zero-JavaScript constitution and how to add a post in three lines Files: `README.md` ### Done when - [ ] A new post goes from file to live with one command - [ ] The README states the constitution ## Not in this build - The Bear discover feed; it is the community. - Newsletters, comments, themes. ## After v1, if you want it - An email-me-new-posts option via RSS-to-email you do not run ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Canonical base for RSS and OG. SITE_URL=https://yourname.blog
You are building a production product version of Bear Blog. 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 ===== # Bear Blog · product brief ## Problem Bear is a deliberate minimum: Markdown, a list, RSS, no JavaScript. A static generator with a strict no-bloat rule reproduces it in a sitting. What you cannot reproduce is the discover feed of other Bear blogs, which is small and beloved. ## Product outcome A blog whose constraints are enforced by the build, so it stays fast and private for years. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - a static host - a domain ## Explicit non-goals for v1 - The Bear discover feed; it is the community. - Newsletters, comments, themes. - the Bear discover feed and its upvotes - hosted privacy analytics - the built-in newsletter - someone else keeping it online for $5 ## Success criteria - CI enforces the constitution - Lighthouse 100 ===== BRIEF.md ===== # Build brief · Bear Blog 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. Build me a minimal blog like Bear Blog. 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. The constraint that makes this Bear and not another blog: the built site ships zero client-side JavaScript and one stylesheet under 5 kB. Treat any phase that breaks that as failed. ### Stack (fixed, do not substitute) - A single Node build script rendering Markdown to static HTML. No framework, no bundler. marked or markdown-it for Markdown, pinned. - Deploy to any static host behind a custom domain. ### Data model (create this before Phase 1) posts/*.md with frontmatter: title, date, slug, tags, draft. The folder is the CMS. ### Phase 1 · Build pipeline Build: build.mjs reads posts, renders dist/index.html (a plain list, newest first) and dist/:slug/index.html, copies static assets. Drafts excluded. A frontmatter error fails the build with the file named. Done when: adding a post and rebuilding adds it to the list, a draft is absent, and a broken frontmatter fails loudly. Do not build yet: styling, feeds. ### Phase 2 · The stylesheet Build: one stylesheet, inlined, under 5 kB · a measured line length, system font stack, dark mode via prefers-color-scheme, readable code blocks, and nothing else. Done when: the built stylesheet is under 5 kB, the page ships no script tag, and Lighthouse scores 100 across the board. ### Phase 3 · Tags and RSS Build: /tags/:tag pages, a full-content RSS feed, a sitemap, and a robots.txt. Done when: the feed validates and every tag page lists only its posts. ### Phase 4 · Privacy analytics Build: a static site cannot count visitors alone; add one tiny server endpoint that logs a daily-salted hash of IP and user agent per path, and a /stats page behind basic auth. Or skip it and read your host's logs. Choose, and write the choice down. Done when: pageviews for today match a count of the day's rows, and no raw IP is stored anywhere. ### Phase 5 · Deploy Build: host config, the custom domain, and the README with the "how to write a post" path. Done when: a new post goes from a Markdown file to live on your domain with one command. ### Out of scope (and why) - The Bear discover feed. It is the community, not the code. - Newsletters, comments, themes. Every one of them is a reason this stops being Bear. ### README must contain - The zero-JavaScript rule, stated as the project's constitution. - How to add a post in three lines. ===== ARCHITECTURE.md ===== # Architecture · Bear Blog ## Stack | Part | Choice | Why | | --- | --- | --- | | Build | One Node script with markdown-it, pinned | no framework, no bundler | | Styling | One inlined stylesheet under 5 KB | the constraint that makes it Bear | | 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 | | --- | --- | --- | | Build | build.mjs and the checks | Any generator that passes the same checks | | Content | posts/ | Markdown is the format | | Analytics | the optional endpoint | Drop it entirely | ## 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 for RSS and OG. ## 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 · Bear Blog product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: One Node script with markdown-it, pinned, One inlined stylesheet under 5 KB, 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 · Bear Blog Estimated effort: **one sitting** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Build pipeline posts/*.md to dist/, drafts excluded, frontmatter errors loud. ### Steps 1. Create the project and build.mjs Read posts/*.md with frontmatter (title, date, slug, tags, draft), render index.html newest first and one page per post, copy static assets. Files: `build.mjs` ```sh mkdir bear && cd bear && git init && npm init -y && npm pkg set type=module && npm install markdown-it@14 gray-matter@4 mkdir -p posts static && cp .env.example .env ``` 2. Fail the build on a missing title or date, naming the file ### Done when - [ ] Adding a post and rebuilding adds it to the list - [ ] A draft is absent - [ ] A broken frontmatter fails loudly ## M2 · The stylesheet Under 5 KB, inlined, no script tag anywhere. ### Steps 1. Write styles.css: measured line length, system fonts, dark mode via prefers-color-scheme, readable code blocks Inline it in build.mjs. 2. Add a build check that fails if the CSS exceeds 5 KB or any script tag appears in dist/ ### Done when - [ ] The built stylesheet is under 5 KB - [ ] The page ships no script tag - [ ] Lighthouse 100 across the board ## M3 · Tags and RSS Tag pages, a full-content feed, sitemap, robots. ### Steps 1. Render /tags/:tag pages 2. Render feed.xml with full content, sitemap.xml and robots.txt ### Done when - [ ] The feed validates - [ ] Every tag page lists only its posts ## M4 · Privacy analytics (optional) Pageviews without a cookie or a raw IP, or nothing at all. ### Steps 1. Decide: read the host's logs, or add one tiny endpoint The endpoint logs a daily-salted hash of IP and user agent per path; /stats behind basic auth. Write the decision down. 2. If you add the endpoint, keep the page's zero-JavaScript rule by using a 1x1 image request, not a script ### Done when - [ ] Pageviews for today match the day's rows - [ ] No raw IP stored anywhere - [ ] Still no script tag in the HTML ## M5 · Deploy One command from Markdown to live. ### Steps 1. Connect the host and the custom domain 2. Write the README with the zero-JavaScript constitution and how to add a post in three lines Files: `README.md` ### Done when - [ ] A new post goes from file to live with one command - [ ] The README states the constitution ## M6 · Keep it Bear (production only) Guardrails so the blog stays small as it grows. ### Steps 1. Add a CI check that fails on any script tag, any external request, or CSS over 5 KB 2. Add a size budget for HTML per page and an accessibility audit in CI ### Done when - [ ] A commit adding a script tag fails CI - [ ] A commit growing CSS past 5 KB fails CI ===== OPERATIONS.md ===== # Operations · Bear Blog ## Backup The repo. ## Restore Redeploy. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Uptime check. ## 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 - [ ] CI enforces the constitution - [ ] Lighthouse 100 ## Launch constraint Do not market omitted Bear Blog 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 for RSS and OG. SITE_URL=https://yourname.blog
# Bear Blog · indie build A minimal blog with Bear's constitution: Markdown in, static HTML out, zero client-side JavaScript and one stylesheet under 5 KB. Tags, RSS, a sitemap, optional privacy analytics, and a custom domain. Everything you add is a reason it stops being Bear. Estimated effort: **one sitting**. 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 | | --- | --- | --- | | Build | One Node script with markdown-it, pinned | no framework, no bundler | | Styling | One inlined stylesheet under 5 KB | the constraint that makes it Bear | | 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 existing posts as Markdown** · free - Why: Phase 1 renders them. - Get it: Bear: Dashboard > Settings > Export. Otherwise copy your posts into Markdown files with a date. - [ ] **A static host** · free - Why: Deploy on push. - Get it: Cloudflare Pages, Netlify or GitHub Pages connected to the repo. - [ ] **A domain or subdomain** (optional) · roughly $10 a year, or free on an existing domain - Why: The point of leaving is a domain you own. - 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. ## Quick start ```sh mkdir bear && cd bear && git init && npm init -y && npm pkg set type=module && npm install markdown-it@14 gray-matter@4 mkdir -p posts static && cp .env.example .env ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The Bear discover feed; it is the community. - Newsletters, comments, themes. - the Bear discover feed and its upvotes - hosted privacy analytics - the built-in newsletter - someone else keeping it online for $5 If one of those is essential to you, that is the reason to keep paying for Bear Blog, and the README should say so rather than pretend.
# Build brief · Bear Blog 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. Build me a minimal blog like Bear Blog. 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. The constraint that makes this Bear and not another blog: the built site ships zero client-side JavaScript and one stylesheet under 5 kB. Treat any phase that breaks that as failed. ### Stack (fixed, do not substitute) - A single Node build script rendering Markdown to static HTML. No framework, no bundler. marked or markdown-it for Markdown, pinned. - Deploy to any static host behind a custom domain. ### Data model (create this before Phase 1) posts/*.md with frontmatter: title, date, slug, tags, draft. The folder is the CMS. ### Phase 1 · Build pipeline Build: build.mjs reads posts, renders dist/index.html (a plain list, newest first) and dist/:slug/index.html, copies static assets. Drafts excluded. A frontmatter error fails the build with the file named. Done when: adding a post and rebuilding adds it to the list, a draft is absent, and a broken frontmatter fails loudly. Do not build yet: styling, feeds. ### Phase 2 · The stylesheet Build: one stylesheet, inlined, under 5 kB · a measured line length, system font stack, dark mode via prefers-color-scheme, readable code blocks, and nothing else. Done when: the built stylesheet is under 5 kB, the page ships no script tag, and Lighthouse scores 100 across the board. ### Phase 3 · Tags and RSS Build: /tags/:tag pages, a full-content RSS feed, a sitemap, and a robots.txt. Done when: the feed validates and every tag page lists only its posts. ### Phase 4 · Privacy analytics Build: a static site cannot count visitors alone; add one tiny server endpoint that logs a daily-salted hash of IP and user agent per path, and a /stats page behind basic auth. Or skip it and read your host's logs. Choose, and write the choice down. Done when: pageviews for today match a count of the day's rows, and no raw IP is stored anywhere. ### Phase 5 · Deploy Build: host config, the custom domain, and the README with the "how to write a post" path. Done when: a new post goes from a Markdown file to live on your domain with one command. ### Out of scope (and why) - The Bear discover feed. It is the community, not the code. - Newsletters, comments, themes. Every one of them is a reason this stops being Bear. ### README must contain - The zero-JavaScript rule, stated as the project's constitution. - How to add a post in three lines.
# Agent instructions · Bear Blog indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: One Node script with markdown-it, pinned, One inlined stylesheet under 5 KB, 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 · Bear Blog A minimal blog with Bear's constitution: Markdown in, static HTML out, zero client-side JavaScript and one stylesheet under 5 KB. Tags, RSS, a sitemap, optional privacy analytics, and a custom domain. Everything you add is a reason it stops being Bear. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Build pipeline posts/*.md to dist/, drafts excluded, frontmatter errors loud. ### Steps 1. Create the project and build.mjs Read posts/*.md with frontmatter (title, date, slug, tags, draft), render index.html newest first and one page per post, copy static assets. Files: `build.mjs` ```sh mkdir bear && cd bear && git init && npm init -y && npm pkg set type=module && npm install markdown-it@14 gray-matter@4 mkdir -p posts static && cp .env.example .env ``` 2. Fail the build on a missing title or date, naming the file ### Done when - [ ] Adding a post and rebuilding adds it to the list - [ ] A draft is absent - [ ] A broken frontmatter fails loudly ## Phase 2 · The stylesheet Under 5 KB, inlined, no script tag anywhere. ### Steps 1. Write styles.css: measured line length, system fonts, dark mode via prefers-color-scheme, readable code blocks Inline it in build.mjs. 2. Add a build check that fails if the CSS exceeds 5 KB or any script tag appears in dist/ ### Done when - [ ] The built stylesheet is under 5 KB - [ ] The page ships no script tag - [ ] Lighthouse 100 across the board ## Phase 3 · Tags and RSS Tag pages, a full-content feed, sitemap, robots. ### Steps 1. Render /tags/:tag pages 2. Render feed.xml with full content, sitemap.xml and robots.txt ### Done when - [ ] The feed validates - [ ] Every tag page lists only its posts ## Phase 4 · Privacy analytics (optional) Pageviews without a cookie or a raw IP, or nothing at all. ### Steps 1. Decide: read the host's logs, or add one tiny endpoint The endpoint logs a daily-salted hash of IP and user agent per path; /stats behind basic auth. Write the decision down. 2. If you add the endpoint, keep the page's zero-JavaScript rule by using a 1x1 image request, not a script ### Done when - [ ] Pageviews for today match the day's rows - [ ] No raw IP stored anywhere - [ ] Still no script tag in the HTML ## Phase 5 · Deploy One command from Markdown to live. ### Steps 1. Connect the host and the custom domain 2. Write the README with the zero-JavaScript constitution and how to add a post in three lines Files: `README.md` ### Done when - [ ] A new post goes from file to live with one command - [ ] The README states the constitution ## Not in this build - The Bear discover feed; it is the community. - Newsletters, comments, themes. ## After v1, if you want it - An email-me-new-posts option via RSS-to-email you do not run
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Canonical base for RSS and OG. SITE_URL=https://yourname.blog
# Bear Blog · product brief ## Problem Bear is a deliberate minimum: Markdown, a list, RSS, no JavaScript. A static generator with a strict no-bloat rule reproduces it in a sitting. What you cannot reproduce is the discover feed of other Bear blogs, which is small and beloved. ## Product outcome A blog whose constraints are enforced by the build, so it stays fast and private for years. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - a static host - a domain ## Explicit non-goals for v1 - The Bear discover feed; it is the community. - Newsletters, comments, themes. - the Bear discover feed and its upvotes - hosted privacy analytics - the built-in newsletter - someone else keeping it online for $5 ## Success criteria - CI enforces the constitution - Lighthouse 100
# Build brief · Bear Blog 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. Build me a minimal blog like Bear Blog. 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. The constraint that makes this Bear and not another blog: the built site ships zero client-side JavaScript and one stylesheet under 5 kB. Treat any phase that breaks that as failed. ### Stack (fixed, do not substitute) - A single Node build script rendering Markdown to static HTML. No framework, no bundler. marked or markdown-it for Markdown, pinned. - Deploy to any static host behind a custom domain. ### Data model (create this before Phase 1) posts/*.md with frontmatter: title, date, slug, tags, draft. The folder is the CMS. ### Phase 1 · Build pipeline Build: build.mjs reads posts, renders dist/index.html (a plain list, newest first) and dist/:slug/index.html, copies static assets. Drafts excluded. A frontmatter error fails the build with the file named. Done when: adding a post and rebuilding adds it to the list, a draft is absent, and a broken frontmatter fails loudly. Do not build yet: styling, feeds. ### Phase 2 · The stylesheet Build: one stylesheet, inlined, under 5 kB · a measured line length, system font stack, dark mode via prefers-color-scheme, readable code blocks, and nothing else. Done when: the built stylesheet is under 5 kB, the page ships no script tag, and Lighthouse scores 100 across the board. ### Phase 3 · Tags and RSS Build: /tags/:tag pages, a full-content RSS feed, a sitemap, and a robots.txt. Done when: the feed validates and every tag page lists only its posts. ### Phase 4 · Privacy analytics Build: a static site cannot count visitors alone; add one tiny server endpoint that logs a daily-salted hash of IP and user agent per path, and a /stats page behind basic auth. Or skip it and read your host's logs. Choose, and write the choice down. Done when: pageviews for today match a count of the day's rows, and no raw IP is stored anywhere. ### Phase 5 · Deploy Build: host config, the custom domain, and the README with the "how to write a post" path. Done when: a new post goes from a Markdown file to live on your domain with one command. ### Out of scope (and why) - The Bear discover feed. It is the community, not the code. - Newsletters, comments, themes. Every one of them is a reason this stops being Bear. ### README must contain - The zero-JavaScript rule, stated as the project's constitution. - How to add a post in three lines.
# Architecture · Bear Blog ## Stack | Part | Choice | Why | | --- | --- | --- | | Build | One Node script with markdown-it, pinned | no framework, no bundler | | Styling | One inlined stylesheet under 5 KB | the constraint that makes it Bear | | 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 | | --- | --- | --- | | Build | build.mjs and the checks | Any generator that passes the same checks | | Content | posts/ | Markdown is the format | | Analytics | the optional endpoint | Drop it entirely | ## 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 for RSS and OG. ## 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 · Bear Blog product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: One Node script with markdown-it, pinned, One inlined stylesheet under 5 KB, 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 · Bear Blog Estimated effort: **one sitting** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Build pipeline posts/*.md to dist/, drafts excluded, frontmatter errors loud. ### Steps 1. Create the project and build.mjs Read posts/*.md with frontmatter (title, date, slug, tags, draft), render index.html newest first and one page per post, copy static assets. Files: `build.mjs` ```sh mkdir bear && cd bear && git init && npm init -y && npm pkg set type=module && npm install markdown-it@14 gray-matter@4 mkdir -p posts static && cp .env.example .env ``` 2. Fail the build on a missing title or date, naming the file ### Done when - [ ] Adding a post and rebuilding adds it to the list - [ ] A draft is absent - [ ] A broken frontmatter fails loudly ## M2 · The stylesheet Under 5 KB, inlined, no script tag anywhere. ### Steps 1. Write styles.css: measured line length, system fonts, dark mode via prefers-color-scheme, readable code blocks Inline it in build.mjs. 2. Add a build check that fails if the CSS exceeds 5 KB or any script tag appears in dist/ ### Done when - [ ] The built stylesheet is under 5 KB - [ ] The page ships no script tag - [ ] Lighthouse 100 across the board ## M3 · Tags and RSS Tag pages, a full-content feed, sitemap, robots. ### Steps 1. Render /tags/:tag pages 2. Render feed.xml with full content, sitemap.xml and robots.txt ### Done when - [ ] The feed validates - [ ] Every tag page lists only its posts ## M4 · Privacy analytics (optional) Pageviews without a cookie or a raw IP, or nothing at all. ### Steps 1. Decide: read the host's logs, or add one tiny endpoint The endpoint logs a daily-salted hash of IP and user agent per path; /stats behind basic auth. Write the decision down. 2. If you add the endpoint, keep the page's zero-JavaScript rule by using a 1x1 image request, not a script ### Done when - [ ] Pageviews for today match the day's rows - [ ] No raw IP stored anywhere - [ ] Still no script tag in the HTML ## M5 · Deploy One command from Markdown to live. ### Steps 1. Connect the host and the custom domain 2. Write the README with the zero-JavaScript constitution and how to add a post in three lines Files: `README.md` ### Done when - [ ] A new post goes from file to live with one command - [ ] The README states the constitution ## M6 · Keep it Bear (production only) Guardrails so the blog stays small as it grows. ### Steps 1. Add a CI check that fails on any script tag, any external request, or CSS over 5 KB 2. Add a size budget for HTML per page and an accessibility audit in CI ### Done when - [ ] A commit adding a script tag fails CI - [ ] A commit growing CSS past 5 KB fails CI
# Operations · Bear Blog ## Backup The repo. ## Restore Redeploy. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Uptime check. ## 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 - [ ] CI enforces the constitution - [ ] Lighthouse 100 ## Launch constraint Do not market omitted Bear Blog 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 for RSS and OG. SITE_URL=https://yourname.blog
$ choose a build depth, inspect the files, then open the complete pack in your agent
Five dollars for a blog that is fast, private and never needs a deploy is a fair trade for most writers, and the discover feed sends readers a personal domain never will.
xthe Bear discover feed and its upvotes
xhosted privacy analytics
xthe built-in newsletter
xsomeone else keeping it online for $5
Bear Blog pricing
premium$5/mo · monthly flat · $60/yr
free tierThe free tier is a full blog on a bearblog.dev subdomain without a custom domain or analytics.
verified 2026-09-04 · source ↗
Is Bear Blog free?
The free tier is a full blog on a bearblog.dev subdomain without a custom domain or analytics. Paid is Premium at $5/mo (checked 2026-09-04).
Vibecode Bear Blog
Yes. A competent AI coding agent (Claude Code, Codex, Cursor) can build a usable personal Bear Blog replacement in one session with the prompt on this page. It runs on your own machine or server with no subscription.
How much does Bear Blog cost?
Bear Blog costs about $5/month (Premium, checked 2026-09-04), which is $60 per year. That's what you save by replacing it with one prompt.
What do I lose by replacing Bear Blog?
Honestly: the Bear discover feed and its upvotes; hosted privacy analytics; the built-in newsletter; someone else keeping it online for $5. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Bear Blog?
Yes: Hugo (fast static site generator), WriteFreely (minimal self-hosted writing platform). Using prior art is also vibecoding; the prompt is for when you want it exactly your way.