Vibecode Placid
track this build5 phases, 10 steps, beginner friendly0%Same engine as the Bannerbear build: satori and resvg render a template with data into a PNG for free. The editor, the video output and the many integrations are what Placid charges for.
You are building a lean indie version of Placid. 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 ===== # Placid · indie build A template-to-image service with a spreadsheet mindset: templates as code, batch rendering from CSV, and a signed HTTP endpoint with a cache. The same engine as the Bannerbear build, plus the CSV step Placid users think in. 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 | | --- | --- | --- | | Rendering | satori, @resvg/resvg-js, sharp | no headless browser | | Runtime | Node 22 | a CLI and one endpoint | ## 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 - [ ] **Font files for your templates** · free - Why: satori needs files. - Get it: Google Fonts, committed to the repo. - [ ] **A CSV of rows to render** · free - Why: Phase 2 renders one image per row. - Get it: Export from your spreadsheet: one column per template field plus a name column. - [ ] **An HMAC signing key** · free - Why: For the endpoint. - Get it: openssl rand -hex 32. - [ ] **A small always-on server (VPS)** (optional) · about $5 a month - Why: This needs one process running all the time with a public address. - Get it: Hetzner Cloud (from about 4 EUR), DigitalOcean or Fly.io. Ubuntu 24.04, the smallest size. You need SSH access and a public IP. Only needed for the deploy phase; develop locally first. - [ ] **Caddy on the server** (optional) · free - Why: Automatic HTTPS in front of the Node process. Without TLS the browser features this relies on (and your visitors' trust) do not work. - Get it: On the VPS: follow the install steps at caddyserver.com/docs/install for Ubuntu. One Caddyfile with your domain and a reverse_proxy line is the whole config. - Verify: caddy version prints a version on the server ## Quick start ```sh mkdir placid-diy && cd placid-diy && git init && npm init -y && npm pkg set type=module && npm install satori@0.29.0 @resvg/resvg-js@2.6.2 sharp@0.35.3 csv-parse@5 mkdir -p templates renders fonts && 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 editor, video, the integrations catalogue. - the drag-and-drop template editor - video rendering - WordPress, Webflow, Ghost and Zapier integrations - hosted delivery If one of those is essential to you, that is the reason to keep paying for Placid, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Placid 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 template-to-image service like Placid. 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. This is the same build as the Bannerbear entry on this site; the difference is a CSV batch step, because Placid users think in spreadsheets. ### Stack (fixed, do not substitute) - Node 22, satori, @resvg/resvg-js, sharp. Fonts as files. No headless browser. ### Phase 1 · One template, one image Build: templates as code exporting a satori tree, a CLI that renders one with params to PNG. Done when: a 1200x630 card renders crisp with wrapped text and the intended font. Do not build yet: HTTP. ### Phase 2 · Batch from CSV Build: render --template card --csv rows.csv writes one image per row, named by a column, skipping rows already rendered by hash. Done when: 200 rows render with a progress line and a rerun does nothing. ### Phase 3 · The endpoint Build: GET /img/:template with params, signed with HMAC, cached by hash, with a timeout and schema validation. Done when: a tampered URL is refused and a repeat request is served from cache. ### Phase 4 · Formats Build: JPEG and WebP output and a size parameter; three templates. Done when: each renders at two sizes in each format. ### Phase 5 · Operate Build: cache eviction, /healthz, a systemd unit, a template-authoring README. Done when: a new template is live by following the README. ### Out of scope (and why) - The editor, video, the integrations catalogue. ### README must contain - The CSV batch command and the signing scheme. ===== AGENTS.md ===== # Agent instructions · Placid indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: satori, @resvg/resvg-js, sharp, Node 22. 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 · Placid A template-to-image service with a spreadsheet mindset: templates as code, batch rendering from CSV, and a signed HTTP endpoint with a cache. The same engine as the Bannerbear build, plus the CSV step Placid users think in. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · One template, one image A crisp card from a satori tree. ### Steps 1. Install and write templates/card.mjs ```sh mkdir placid-diy && cd placid-diy && git init && npm init -y && npm pkg set type=module && npm install satori@0.29.0 @resvg/resvg-js@2.6.2 sharp@0.35.3 csv-parse@5 mkdir -p templates renders fonts && cp .env.example .env ``` 2. A CLI rendering one with params ### Done when - [ ] A 1200x630 card renders crisp ## Phase 2 · Batch from CSV One image per row, named by a column, resumable. ### Steps 1. render --template card --csv rows.csv writing to out/ 2. Skip rows already rendered by hash; a progress line ### Done when - [ ] 200 rows render with progress - [ ] A rerun does nothing ## Phase 3 · The endpoint Signed, cached, validated. ### Steps 1. GET /img/:template with HMAC and schema validation 2. Cache by hash ### Done when - [ ] A tampered URL is refused - [ ] A repeat is served from cache ## Phase 4 · Formats JPEG, WebP, sizes, three templates. ### Steps 1. Format and size parameters 2. Two more templates ### Done when - [ ] Each renders at two sizes in each format ## Phase 5 · Operate Eviction, healthz, README. ### Steps 1. Cache eviction, /healthz, systemd 2. README with the CSV command and signing Files: `README.md` ### Done when - [ ] A new template is live from the README ## Not in this build - The editor, video, the integrations catalogue. ## After v1, if you want it - A Google Sheets source - A preview page per template ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Any free port. PORT=3000 # Required · secret. openssl rand -hex 32. SIGN_KEY=hex # Required. Cache. CACHE_DIR=./renders
You are building a lean indie version of Placid. 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 ===== # Placid · indie build A template-to-image service with a spreadsheet mindset: templates as code, batch rendering from CSV, and a signed HTTP endpoint with a cache. The same engine as the Bannerbear build, plus the CSV step Placid users think in. 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 | | --- | --- | --- | | Rendering | satori, @resvg/resvg-js, sharp | no headless browser | | Runtime | Node 22 | a CLI and one endpoint | ## 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 - [ ] **Font files for your templates** · free - Why: satori needs files. - Get it: Google Fonts, committed to the repo. - [ ] **A CSV of rows to render** · free - Why: Phase 2 renders one image per row. - Get it: Export from your spreadsheet: one column per template field plus a name column. - [ ] **An HMAC signing key** · free - Why: For the endpoint. - Get it: openssl rand -hex 32. - [ ] **A small always-on server (VPS)** (optional) · about $5 a month - Why: This needs one process running all the time with a public address. - Get it: Hetzner Cloud (from about 4 EUR), DigitalOcean or Fly.io. Ubuntu 24.04, the smallest size. You need SSH access and a public IP. Only needed for the deploy phase; develop locally first. - [ ] **Caddy on the server** (optional) · free - Why: Automatic HTTPS in front of the Node process. Without TLS the browser features this relies on (and your visitors' trust) do not work. - Get it: On the VPS: follow the install steps at caddyserver.com/docs/install for Ubuntu. One Caddyfile with your domain and a reverse_proxy line is the whole config. - Verify: caddy version prints a version on the server ## Quick start ```sh mkdir placid-diy && cd placid-diy && git init && npm init -y && npm pkg set type=module && npm install satori@0.29.0 @resvg/resvg-js@2.6.2 sharp@0.35.3 csv-parse@5 mkdir -p templates renders fonts && 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 editor, video, the integrations catalogue. - the drag-and-drop template editor - video rendering - WordPress, Webflow, Ghost and Zapier integrations - hosted delivery If one of those is essential to you, that is the reason to keep paying for Placid, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Placid 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 template-to-image service like Placid. 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. This is the same build as the Bannerbear entry on this site; the difference is a CSV batch step, because Placid users think in spreadsheets. ### Stack (fixed, do not substitute) - Node 22, satori, @resvg/resvg-js, sharp. Fonts as files. No headless browser. ### Phase 1 · One template, one image Build: templates as code exporting a satori tree, a CLI that renders one with params to PNG. Done when: a 1200x630 card renders crisp with wrapped text and the intended font. Do not build yet: HTTP. ### Phase 2 · Batch from CSV Build: render --template card --csv rows.csv writes one image per row, named by a column, skipping rows already rendered by hash. Done when: 200 rows render with a progress line and a rerun does nothing. ### Phase 3 · The endpoint Build: GET /img/:template with params, signed with HMAC, cached by hash, with a timeout and schema validation. Done when: a tampered URL is refused and a repeat request is served from cache. ### Phase 4 · Formats Build: JPEG and WebP output and a size parameter; three templates. Done when: each renders at two sizes in each format. ### Phase 5 · Operate Build: cache eviction, /healthz, a systemd unit, a template-authoring README. Done when: a new template is live by following the README. ### Out of scope (and why) - The editor, video, the integrations catalogue. ### README must contain - The CSV batch command and the signing scheme. ===== AGENTS.md ===== # Agent instructions · Placid indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: satori, @resvg/resvg-js, sharp, Node 22. 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 · Placid A template-to-image service with a spreadsheet mindset: templates as code, batch rendering from CSV, and a signed HTTP endpoint with a cache. The same engine as the Bannerbear build, plus the CSV step Placid users think in. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · One template, one image A crisp card from a satori tree. ### Steps 1. Install and write templates/card.mjs ```sh mkdir placid-diy && cd placid-diy && git init && npm init -y && npm pkg set type=module && npm install satori@0.29.0 @resvg/resvg-js@2.6.2 sharp@0.35.3 csv-parse@5 mkdir -p templates renders fonts && cp .env.example .env ``` 2. A CLI rendering one with params ### Done when - [ ] A 1200x630 card renders crisp ## Phase 2 · Batch from CSV One image per row, named by a column, resumable. ### Steps 1. render --template card --csv rows.csv writing to out/ 2. Skip rows already rendered by hash; a progress line ### Done when - [ ] 200 rows render with progress - [ ] A rerun does nothing ## Phase 3 · The endpoint Signed, cached, validated. ### Steps 1. GET /img/:template with HMAC and schema validation 2. Cache by hash ### Done when - [ ] A tampered URL is refused - [ ] A repeat is served from cache ## Phase 4 · Formats JPEG, WebP, sizes, three templates. ### Steps 1. Format and size parameters 2. Two more templates ### Done when - [ ] Each renders at two sizes in each format ## Phase 5 · Operate Eviction, healthz, README. ### Steps 1. Cache eviction, /healthz, systemd 2. README with the CSV command and signing Files: `README.md` ### Done when - [ ] A new template is live from the README ## Not in this build - The editor, video, the integrations catalogue. ## After v1, if you want it - A Google Sheets source - A preview page per template ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Any free port. PORT=3000 # Required · secret. openssl rand -hex 32. SIGN_KEY=hex # Required. Cache. CACHE_DIR=./renders
You are building a production product version of Placid. 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 ===== # Placid · product brief ## Problem Same engine as the Bannerbear build: satori and resvg render a template with data into a PNG for free. The editor, the video output and the many integrations are what Placid charges for. ## Product outcome Creative automation for your own campaigns at zero per-image cost. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node 22 - font files ## Explicit non-goals for v1 - The editor, video, the integrations catalogue. - the drag-and-drop template editor - video rendering - WordPress, Webflow, Ghost and Zapier integrations - hosted delivery ## Success criteria - Signing verified - Batch resumability verified ===== BRIEF.md ===== # Build brief · Placid 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 template-to-image service like Placid. 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. This is the same build as the Bannerbear entry on this site; the difference is a CSV batch step, because Placid users think in spreadsheets. ### Stack (fixed, do not substitute) - Node 22, satori, @resvg/resvg-js, sharp. Fonts as files. No headless browser. ### Phase 1 · One template, one image Build: templates as code exporting a satori tree, a CLI that renders one with params to PNG. Done when: a 1200x630 card renders crisp with wrapped text and the intended font. Do not build yet: HTTP. ### Phase 2 · Batch from CSV Build: render --template card --csv rows.csv writes one image per row, named by a column, skipping rows already rendered by hash. Done when: 200 rows render with a progress line and a rerun does nothing. ### Phase 3 · The endpoint Build: GET /img/:template with params, signed with HMAC, cached by hash, with a timeout and schema validation. Done when: a tampered URL is refused and a repeat request is served from cache. ### Phase 4 · Formats Build: JPEG and WebP output and a size parameter; three templates. Done when: each renders at two sizes in each format. ### Phase 5 · Operate Build: cache eviction, /healthz, a systemd unit, a template-authoring README. Done when: a new template is live by following the README. ### Out of scope (and why) - The editor, video, the integrations catalogue. ### README must contain - The CSV batch command and the signing scheme. ===== ARCHITECTURE.md ===== # Architecture · Placid ## Stack | Part | Choice | Why | | --- | --- | --- | | Rendering | satori, @resvg/resvg-js, sharp | no headless browser | | Runtime | Node 22 | a CLI and one endpoint | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Templates | code modules | JSON templates later | | Batch | CSV rendering | A spreadsheet API source | | Edge | signing and cache | A CDN | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `PORT` · required · Any free port. - `SIGN_KEY` · required, secret · openssl rand -hex 32. - `CACHE_DIR` · required · Cache. ## 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 · Placid product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: satori, @resvg/resvg-js, sharp, Node 22. - 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 · Placid Estimated effort: **one sitting** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · One template, one image A crisp card from a satori tree. ### Steps 1. Install and write templates/card.mjs ```sh mkdir placid-diy && cd placid-diy && git init && npm init -y && npm pkg set type=module && npm install satori@0.29.0 @resvg/resvg-js@2.6.2 sharp@0.35.3 csv-parse@5 mkdir -p templates renders fonts && cp .env.example .env ``` 2. A CLI rendering one with params ### Done when - [ ] A 1200x630 card renders crisp ## M2 · Batch from CSV One image per row, named by a column, resumable. ### Steps 1. render --template card --csv rows.csv writing to out/ 2. Skip rows already rendered by hash; a progress line ### Done when - [ ] 200 rows render with progress - [ ] A rerun does nothing ## M3 · The endpoint Signed, cached, validated. ### Steps 1. GET /img/:template with HMAC and schema validation 2. Cache by hash ### Done when - [ ] A tampered URL is refused - [ ] A repeat is served from cache ## M4 · Formats JPEG, WebP, sizes, three templates. ### Steps 1. Format and size parameters 2. Two more templates ### Done when - [ ] Each renders at two sizes in each format ## M5 · Operate Eviction, healthz, README. ### Steps 1. Cache eviction, /healthz, systemd 2. README with the CSV command and signing Files: `README.md` ### Done when - [ ] A new template is live from the README ## M6 · Operate it like a product (production only) Only for the product-builder path: know when the renderer is down, never lose the database, and keep the server patched. ### Steps 1. Add a /healthz endpoint and an external uptime check against it Answer 200 with the build id and a quick database read. Point a free uptime monitor (or your own, from the Healthchecks entry on this site) at it so an outage is noticed before a user notices. 2. Write structured request logs and rotate them One JSON line per request: method, path, status, duration, no raw IPs. Rotate weekly with logrotate, keep eight. 3. Back the SQLite file up off the machine nightly and test a restore SQLite's .backup command makes a consistent copy while the app runs. Copy it to object storage or a second machine; then, once, restore it into a fresh checkout and confirm the app reads it. ```sh sqlite3 data/app.db ".backup '/tmp/app-$(date +%F).db'" rclone copy /tmp/app-$(date +%F).db remote:backups/ ``` 4. Lock the box down Firewall allowing only 22, 80 and 443; unattended security updates on; the app running as an unprivileged user under systemd with Restart=on-failure. ### Done when - [ ] Stopping the service triggers an uptime alert within a few minutes - [ ] A restore from last night's backup contains yesterday's data - [ ] A port scan from another machine shows only 22, 80 and 443 ===== OPERATIONS.md ===== # Operations · Placid ## Backup Templates in git. ## Restore Redeploy. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Uptime. ## Incident checklist Rotate SIGN_KEY. 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 - [ ] Signing verified - [ ] Batch resumability verified ## Launch constraint Do not market omitted Placid 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. Any free port. PORT=3000 # Required · secret. openssl rand -hex 32. SIGN_KEY=hex # Required. Cache. CACHE_DIR=./renders
# Placid · indie build A template-to-image service with a spreadsheet mindset: templates as code, batch rendering from CSV, and a signed HTTP endpoint with a cache. The same engine as the Bannerbear build, plus the CSV step Placid users think in. 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 | | --- | --- | --- | | Rendering | satori, @resvg/resvg-js, sharp | no headless browser | | Runtime | Node 22 | a CLI and one endpoint | ## 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 - [ ] **Font files for your templates** · free - Why: satori needs files. - Get it: Google Fonts, committed to the repo. - [ ] **A CSV of rows to render** · free - Why: Phase 2 renders one image per row. - Get it: Export from your spreadsheet: one column per template field plus a name column. - [ ] **An HMAC signing key** · free - Why: For the endpoint. - Get it: openssl rand -hex 32. - [ ] **A small always-on server (VPS)** (optional) · about $5 a month - Why: This needs one process running all the time with a public address. - Get it: Hetzner Cloud (from about 4 EUR), DigitalOcean or Fly.io. Ubuntu 24.04, the smallest size. You need SSH access and a public IP. Only needed for the deploy phase; develop locally first. - [ ] **Caddy on the server** (optional) · free - Why: Automatic HTTPS in front of the Node process. Without TLS the browser features this relies on (and your visitors' trust) do not work. - Get it: On the VPS: follow the install steps at caddyserver.com/docs/install for Ubuntu. One Caddyfile with your domain and a reverse_proxy line is the whole config. - Verify: caddy version prints a version on the server ## Quick start ```sh mkdir placid-diy && cd placid-diy && git init && npm init -y && npm pkg set type=module && npm install satori@0.29.0 @resvg/resvg-js@2.6.2 sharp@0.35.3 csv-parse@5 mkdir -p templates renders fonts && 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 editor, video, the integrations catalogue. - the drag-and-drop template editor - video rendering - WordPress, Webflow, Ghost and Zapier integrations - hosted delivery If one of those is essential to you, that is the reason to keep paying for Placid, and the README should say so rather than pretend.
# Build brief · Placid 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 template-to-image service like Placid. 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. This is the same build as the Bannerbear entry on this site; the difference is a CSV batch step, because Placid users think in spreadsheets. ### Stack (fixed, do not substitute) - Node 22, satori, @resvg/resvg-js, sharp. Fonts as files. No headless browser. ### Phase 1 · One template, one image Build: templates as code exporting a satori tree, a CLI that renders one with params to PNG. Done when: a 1200x630 card renders crisp with wrapped text and the intended font. Do not build yet: HTTP. ### Phase 2 · Batch from CSV Build: render --template card --csv rows.csv writes one image per row, named by a column, skipping rows already rendered by hash. Done when: 200 rows render with a progress line and a rerun does nothing. ### Phase 3 · The endpoint Build: GET /img/:template with params, signed with HMAC, cached by hash, with a timeout and schema validation. Done when: a tampered URL is refused and a repeat request is served from cache. ### Phase 4 · Formats Build: JPEG and WebP output and a size parameter; three templates. Done when: each renders at two sizes in each format. ### Phase 5 · Operate Build: cache eviction, /healthz, a systemd unit, a template-authoring README. Done when: a new template is live by following the README. ### Out of scope (and why) - The editor, video, the integrations catalogue. ### README must contain - The CSV batch command and the signing scheme.
# Agent instructions · Placid indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: satori, @resvg/resvg-js, sharp, Node 22. 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 · Placid A template-to-image service with a spreadsheet mindset: templates as code, batch rendering from CSV, and a signed HTTP endpoint with a cache. The same engine as the Bannerbear build, plus the CSV step Placid users think in. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · One template, one image A crisp card from a satori tree. ### Steps 1. Install and write templates/card.mjs ```sh mkdir placid-diy && cd placid-diy && git init && npm init -y && npm pkg set type=module && npm install satori@0.29.0 @resvg/resvg-js@2.6.2 sharp@0.35.3 csv-parse@5 mkdir -p templates renders fonts && cp .env.example .env ``` 2. A CLI rendering one with params ### Done when - [ ] A 1200x630 card renders crisp ## Phase 2 · Batch from CSV One image per row, named by a column, resumable. ### Steps 1. render --template card --csv rows.csv writing to out/ 2. Skip rows already rendered by hash; a progress line ### Done when - [ ] 200 rows render with progress - [ ] A rerun does nothing ## Phase 3 · The endpoint Signed, cached, validated. ### Steps 1. GET /img/:template with HMAC and schema validation 2. Cache by hash ### Done when - [ ] A tampered URL is refused - [ ] A repeat is served from cache ## Phase 4 · Formats JPEG, WebP, sizes, three templates. ### Steps 1. Format and size parameters 2. Two more templates ### Done when - [ ] Each renders at two sizes in each format ## Phase 5 · Operate Eviction, healthz, README. ### Steps 1. Cache eviction, /healthz, systemd 2. README with the CSV command and signing Files: `README.md` ### Done when - [ ] A new template is live from the README ## Not in this build - The editor, video, the integrations catalogue. ## After v1, if you want it - A Google Sheets source - A preview page per template
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Any free port. PORT=3000 # Required · secret. openssl rand -hex 32. SIGN_KEY=hex # Required. Cache. CACHE_DIR=./renders
# Placid · product brief ## Problem Same engine as the Bannerbear build: satori and resvg render a template with data into a PNG for free. The editor, the video output and the many integrations are what Placid charges for. ## Product outcome Creative automation for your own campaigns at zero per-image cost. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node 22 - font files ## Explicit non-goals for v1 - The editor, video, the integrations catalogue. - the drag-and-drop template editor - video rendering - WordPress, Webflow, Ghost and Zapier integrations - hosted delivery ## Success criteria - Signing verified - Batch resumability verified
# Build brief · Placid 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 template-to-image service like Placid. 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. This is the same build as the Bannerbear entry on this site; the difference is a CSV batch step, because Placid users think in spreadsheets. ### Stack (fixed, do not substitute) - Node 22, satori, @resvg/resvg-js, sharp. Fonts as files. No headless browser. ### Phase 1 · One template, one image Build: templates as code exporting a satori tree, a CLI that renders one with params to PNG. Done when: a 1200x630 card renders crisp with wrapped text and the intended font. Do not build yet: HTTP. ### Phase 2 · Batch from CSV Build: render --template card --csv rows.csv writes one image per row, named by a column, skipping rows already rendered by hash. Done when: 200 rows render with a progress line and a rerun does nothing. ### Phase 3 · The endpoint Build: GET /img/:template with params, signed with HMAC, cached by hash, with a timeout and schema validation. Done when: a tampered URL is refused and a repeat request is served from cache. ### Phase 4 · Formats Build: JPEG and WebP output and a size parameter; three templates. Done when: each renders at two sizes in each format. ### Phase 5 · Operate Build: cache eviction, /healthz, a systemd unit, a template-authoring README. Done when: a new template is live by following the README. ### Out of scope (and why) - The editor, video, the integrations catalogue. ### README must contain - The CSV batch command and the signing scheme.
# Architecture · Placid ## Stack | Part | Choice | Why | | --- | --- | --- | | Rendering | satori, @resvg/resvg-js, sharp | no headless browser | | Runtime | Node 22 | a CLI and one endpoint | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Templates | code modules | JSON templates later | | Batch | CSV rendering | A spreadsheet API source | | Edge | signing and cache | A CDN | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `PORT` · required · Any free port. - `SIGN_KEY` · required, secret · openssl rand -hex 32. - `CACHE_DIR` · required · Cache. ## 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 · Placid product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: satori, @resvg/resvg-js, sharp, Node 22. - 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 · Placid Estimated effort: **one sitting** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · One template, one image A crisp card from a satori tree. ### Steps 1. Install and write templates/card.mjs ```sh mkdir placid-diy && cd placid-diy && git init && npm init -y && npm pkg set type=module && npm install satori@0.29.0 @resvg/resvg-js@2.6.2 sharp@0.35.3 csv-parse@5 mkdir -p templates renders fonts && cp .env.example .env ``` 2. A CLI rendering one with params ### Done when - [ ] A 1200x630 card renders crisp ## M2 · Batch from CSV One image per row, named by a column, resumable. ### Steps 1. render --template card --csv rows.csv writing to out/ 2. Skip rows already rendered by hash; a progress line ### Done when - [ ] 200 rows render with progress - [ ] A rerun does nothing ## M3 · The endpoint Signed, cached, validated. ### Steps 1. GET /img/:template with HMAC and schema validation 2. Cache by hash ### Done when - [ ] A tampered URL is refused - [ ] A repeat is served from cache ## M4 · Formats JPEG, WebP, sizes, three templates. ### Steps 1. Format and size parameters 2. Two more templates ### Done when - [ ] Each renders at two sizes in each format ## M5 · Operate Eviction, healthz, README. ### Steps 1. Cache eviction, /healthz, systemd 2. README with the CSV command and signing Files: `README.md` ### Done when - [ ] A new template is live from the README ## M6 · Operate it like a product (production only) Only for the product-builder path: know when the renderer is down, never lose the database, and keep the server patched. ### Steps 1. Add a /healthz endpoint and an external uptime check against it Answer 200 with the build id and a quick database read. Point a free uptime monitor (or your own, from the Healthchecks entry on this site) at it so an outage is noticed before a user notices. 2. Write structured request logs and rotate them One JSON line per request: method, path, status, duration, no raw IPs. Rotate weekly with logrotate, keep eight. 3. Back the SQLite file up off the machine nightly and test a restore SQLite's .backup command makes a consistent copy while the app runs. Copy it to object storage or a second machine; then, once, restore it into a fresh checkout and confirm the app reads it. ```sh sqlite3 data/app.db ".backup '/tmp/app-$(date +%F).db'" rclone copy /tmp/app-$(date +%F).db remote:backups/ ``` 4. Lock the box down Firewall allowing only 22, 80 and 443; unattended security updates on; the app running as an unprivileged user under systemd with Restart=on-failure. ### Done when - [ ] Stopping the service triggers an uptime alert within a few minutes - [ ] A restore from last night's backup contains yesterday's data - [ ] A port scan from another machine shows only 22, 80 and 443
# Operations · Placid ## Backup Templates in git. ## Restore Redeploy. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Uptime. ## Incident checklist Rotate SIGN_KEY. 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 - [ ] Signing verified - [ ] Batch resumability verified ## Launch constraint Do not market omitted Placid 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. Any free port. PORT=3000 # Required · secret. openssl rand -hex 32. SIGN_KEY=hex # Required. Cache. CACHE_DIR=./renders
$ choose a build depth, inspect the files, then open the complete pack in your agent
Non-developers design the template and connect a form; nobody writes a satori tree.
xthe drag-and-drop template editor
xvideo rendering
xWordPress, Webflow, Ghost and Zapier integrations
xhosted delivery
Vibecode Placid
Yes. A competent AI coding agent (Claude Code, Codex, Cursor) can build a usable personal Placid replacement in one session with the prompt on this page. It runs on your own machine or server with no subscription.
How much does Placid cost?
Placid costs about $19/month (Basic, checked 2026-09-04), which is $228 per year. That's what you save by replacing it with one prompt.
What do I lose by replacing Placid?
Honestly: the drag-and-drop template editor; video rendering; WordPress, Webflow, Ghost and Zapier integrations; hosted delivery. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Placid?
Yes: satori (HTML and CSS to SVG). Using prior art is also vibecoding; the prompt is for when you want it exactly your way.