LiveOctober to November 2026
Outlet Powered
Backup power for US homes, with every spec linked to the maker's own page or manual.
I built Outlet Powered to help US households choose a portable power station for outages. Every figure on a model page links to the maker's US product page or manual, with the date it was checked, and a figure the maker does not state reads "Not stated". Runtimes are calculations shown as ranges with the maths beside them, and the site says plainly that it does not test products. The content lives in Postgres behind an admin, pages can be scheduled to go live by themselves, and an AI guide answers questions from the same data.
- US models live on 11 November 2026
- 33
- maker pages and manuals linked (11 November 2026)
- 103
- appliances with published wattage
- 54
- brands
- 7
- Vitest tests passing on 10 November 2026
- 1,137
- database tables
- 15
- scheduled cron entries
- 5
- commits, 9 October to 10 November 2026
- 134
Architecture
Next.js 16 with Postgres content and tag-based caching
My role: Founder, editor and developer. I set the requirements, reviewed and tested the work, and review each page before it goes live, working with AI coding assistants.
seed/The content in Git: one JSON file per model, the appliances with their sources, and the pages in Markdown. It is also the fallback when the database cannot be read.
src/dbA Drizzle schema of 15 tables, the seed loader, the deploy sync and the export that writes the database back to Git.
src/lib/dataCached readers for products, appliances, pages, settings and redirects, one cache tag each. Items with a future publish time are dropped here.
src/app/adminThe admin: pages, products, appliances, settings, redirects, messages, the audit log, guide questions and images.
src/lib/runtime.tsThe runtime and fit maths, shared by the calculators in the browser and the tables built ahead of time. Its assumptions are named constants.
src/lib/guideThe AI guide: the Claude call, three read-only tools on the site's own maths, the limits and the answer check.
src/lib/jevJev for the finder, contact triage and the admin's spec check, with request guards, a 24-hour cache and keyword rules as the fallback.
- Next.js 16
- React 19
- TypeScript
- Drizzle ORM
- Neon Postgres
- PGlite
- Zod
- unified (remark and rehype)
- sharp
- Vercel Blob
- Vercel Cron
- Cloudflare Turnstile
- Claude Haiku 5.5
- Jev (TypeSafe AI)
- Vitest
- Playwright
- axe-core
The site
Seven pages from the live site
Taken from outletpowered.com on 11 November 2026. The numbers in them are the site's own data on that day.
(opens the full-size screenshot in a new tab)
(opens the full-size screenshot in a new tab)Database explorer: filters, a card or table view, and up to three models compared side by side.
(opens the full-size screenshot in a new tab)A model page: every maker's figure carries a source link, and a missing one reads "Not stated".
(opens the full-size screenshot in a new tab)Runtime calculator: a range, with the 80 to 90% efficiency assumption stated next to the answer.
(opens the full-size screenshot in a new tab)Will it run: one appliance, every model, with runtimes as ranges and a start-up check.
(opens the full-size screenshot in a new tab)Compare: a verdict table by need, then each difference worked through with the numbers.
(opens the full-size screenshot in a new tab)Recall tracker: each brand checked against the CPSC recall database, with the date of the check.
Evidence
A sourced product database
A figure goes on the site only with a link to the maker's US product page or manual and the date it was checked.
The site covers portable power stations, solar generators and plug-in solar for US homes facing outages. It collects the makers' own figures, shows where each one came from and does the runtime arithmetic on the page. It does not test products. The site is built to be funded by affiliate links.
Where the figures come from
- Each spec value is stored with its source and a note, and each model lists its source links with the date they were checked.
- When the maker does not state a figure, the page says "Not stated". Gaps are never filled with an estimate or a figure from another model.
- When a product page and the manual disagree, the manual wins and the page notes the difference.
- Tests published by other outlets appear under "Measured by others", credited by name with the date and a link.
Rules the build enforces
- A unit test fails the build on any claim of first-hand testing in the content and data files.
- No prices are published until a store feed supplies a price with its timestamp.
- Every store button reads "Check price at" and the links come from one data file.
Still open
- Each model page says whether I have finished a second check of its figures against the printed manual. None is finished yet, and the About page says so.
- Runtimes are calculations from rated capacity, not measurements, so the site shows them as ranges.
Content
Postgres content with the seed in Git
Pages, products, appliances, settings and redirects live in Neon Postgres and are edited in the admin. The same content ships in Git as the seed.
Saving a change
- Every admin save is validated against the whole site, then stored with a revision and an audit row.
- A save expires only the cache tags it touched, so the affected pages rebuild on their next visit without a redeploy.
- Public pages are prerendered and served from the CDN, so a page view costs no database query.
Git and the database
- Without a database the site renders from the bundled seed. If a database read fails, the seed is shown for five minutes only.
- Deploys add new seed pages and products and refresh rows nobody has edited. A row saved in the admin is never overwritten by a deploy.
- An export command writes the database back to the seed, so Git keeps a reviewable history of admin edits.
Images
- Admin uploads are decoded by sharp, re-encoded to 640, 1024 and 1600 px WebP with metadata removed, and stored in Vercel Blob in a new folder each time.
Tools
Runtime calculator, appliance tables and comparisons
The calculators and the tables built ahead of time share one module of runtime maths, and its assumptions are named constants shown on the pages that use them.
Runtime calculator
- Rated watt-hours times an efficiency range (80 to 90% on AC, 85 to 95% on DC), divided by the load, shown as a range.
- For a chosen station it checks running watts against the continuous rating and start-up watts against the surge rating, and warns about DC-only stations.
Will it run
- 54 appliances with published wattage, each tied to its source.
- Refrigerator, freezer and CPAP pages each carry a runtime table for every model, and a size calculator works out the battery a plan needs.
- CPAP figures use ResMed's own numbers: 56.1 W typical and 73.2 W peak for the AirSense 11 with a heated humidifier.
Database explorer and comparisons
- A filterable card grid with a table view and a dialog that compares up to three models.
- 12 comparison pages, brand against brand and model against model, with spec tables that highlight the rows that differ.
Recalls
- A recall tracker built from searches of the CPSC recalls API, with the date each brand was checked.
AI
An AI guide whose answers are checked on the server
The "Ask the AI guide" box runs on Claude Haiku 5.5. It answers from a catalog context and three read-only tools on the site's own maths, and code checks every reply before a visitor sees it.
What the check holds back
- A number that is not in the database, the tool results or the visitor's own message. Small counts pass, but a small number with a unit, such as "8 hours", needs a source like any other.
- A price, a claim of testing, wording about wiring a station into a house, or a link to another website.
- Markup, an email address, or talk about its own prompt or rules, which the model has no reason to write.
- Links are kept only when they point at an existing page on the site.
Limits
- Per visitor: 10 questions per 10 minutes and 30 a day. Site-wide: 1,000 answers a day, then the guide shows the contact form.
- 500 characters a question, 6 turns, a 15-second timeout and 3 tool rounds.
- Questions are kept for 30 days without the IP address and reviewed in the admin.
Describe your home
- In the finder, a visitor can describe a home in a sentence. Jev, TypeSafe AI's System One model, decides which of the site's appliances were named, how long the outage is and whether medical equipment is involved. It writes no text.
- Code does every calculation. Jev's answers count only for options the site offered: 0.7 adds an item and 0.45 suggests it.
- After a 4-second timeout, or past the daily cap, keyword rules answer instead.
Publishing
Scheduled publishing in one push a week
A page or a model can carry a publish time and go live by itself, so a week of content ships in one push and one build.
How it works
- A publishAt time in UTC on a page or a product file. The cached public readers drop anything whose time has not come.
- A release cron runs at 13, 14, 18 and 19 UTC, the 9 AM and 2 PM Eastern slots in winter and summer. When something fell due, it expires the page and product caches and sends the new URLs to IndexNow.
- The admin has a Scheduled status with a US Eastern time and refuses a time in the past.
- Saves are validated with scheduled items counted as live, so a page cannot be saved if it would break once everything scheduled is out.
Checks
- A unit test parses the seed at every scheduled instant, so no live page can point at a model that is not out yet.
- An end-to-end test schedules a page, refuses a past time and publishes.
Daily job
- A daily cron at 04:17 UTC cleans up and sends IndexNow every sitemap URL whose last change was today or yesterday.
Security
Spam defences, headers and the admin
Forms and paid calls
- The contact form, the AI guide and the finder sit behind Cloudflare Turnstile. The widget is interaction-only, and a passed check sets a signed 10-minute cookie.
- If Cloudflare cannot be reached, the contact form still takes the message, and the guide and finder fall back to the free answers, never the paid model.
- The contact form has a honeypot field and a minimum fill time.
- Messages that look like spam, such as many links, a throwaway email domain or text addressed to AI readers, are stored under a spam label. The rest are posted to a private Discord channel.
Headers
- A Content-Security-Policy that defaults to the site's own origin and adds a host only for a service that is configured.
- HSTS for two years, X-Frame-Options DENY, nosniff, a Referrer-Policy, a Permissions-Policy and a same-origin opener policy.
Admin
- Passwords hashed with scrypt, optional TOTP two-factor sign-in and an HMAC-signed session cookie.
- Every admin action checks the session on the server first.
Testing
One release gate before every push
There is no GitHub CI, by choice. One script is the release gate, and it runs before every push to the main branch.
The gate
- npm run verify runs the contrast check, unit tests, the type check, the build, the writing check and the end-to-end suite on Chromium, plus WebKit in WSL.
- A pass is tied to the exact tree it checked, and the pre-push hook skips the checks only when the pushed tree matches.
Tests
- 1,137 Vitest tests in 25 files passed on 10 November 2026.
- The end-to-end server uses a throwaway PGlite database, a test-only admin and a mocked Jev, and checks that an admin save shows on the public page straight away.
- Accessibility is checked with axe in the end-to-end suite.
Writing rules
- A checker fails the build on em dashes, invisible characters, testing claims and three one-sentence paragraphs in a row.
Growth
Technologies and skills used
- Data modelling
- Spec values stored with their source and check date, Zod schemas, and whole-site validation on every save.
- Full-stack TypeScript
- Next.js 16 with Cache Components, tag-based cache expiry, server actions and an admin.
- Databases
- A 15-table Postgres schema with Drizzle, revisions and audit rows, and a Git seed as the fallback.
- AI engineering
- A Claude guide with read-only tools and a server-side answer check, and Jev for typed judgement calls.
- Publishing
- Scheduled releases on fixed cron slots, cache expiry on release and IndexNow pings.
- Security
- Cloudflare Turnstile with a signed pass cookie, CSP and HSTS, spam rules and scrypt with TOTP.
- Testing
- Vitest, Playwright on Chromium and WebKit, axe checks and a single release gate.