Build Cargo

KINDAback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A site builder whose hard problems are a content model that survives a redesign, images that do not cost a visitor their data allowance, and a publish that is atomic and reversible rather than a file by file sync over a live site.

estimated effort weekendthe files for this build are in the project pack

EditorNext.js with TypeScript, one local user, no accountsContentSQLite plus a JSON export written on every saveOutputA deterministic static build into a release folderImagesDerivatives generated at publish time from untouched originalsPublishingUpload a whole release then switch, with the previous release kept

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the editor and the build.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why Derivative generation at several widths is the difference between a fast site and a slow one, and doing it by hand does not happen.

    Get it Any maintained library that resizes and re-encodes, installed as a dependency.

    Verify It produces a resized file at three widths from one original

  3. free to a few pounds a month

    Why Atomic publishing needs a host where you can upload a release and then switch, rather than one that only offers a file by file sync.

    Get it Any static hosting or object storage you already have, plus its published limits page.

    Verify You can upload a directory and serve it, and you have read the limits page

  4. have readyaround ten pounds a year

    Why Domain and certificate handling is the part that breaks on a Friday, and testing it against a real name is the only test that counts.

    Get it Any registrar, with access to the DNS records.

    Verify A record you add resolves within an hour

  5. decidefree

    Why Web embedding is a separate permission from desktop use, and the build refuses to run without the note.

    Get it For each font, record the licence and one sentence on whether web embedding is permitted.

    Verify Every font file in the project has a note beside it and the build passes

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- sites(id, name, base_url, primary_domain, created_at)
- pages(id, site_id, slug, title, description, status, ordinal, updated_at) · slug is unique per site and changing it records a redirect
- sections(id, page_id, ordinal, kind, content_json, layout_json) · typed sections with layout options, never absolute pixel positions
- assets(id, site_id, original_path, sha256, width, height, bytes, alt_text) · alt text lives with the asset, not with one use of it
- derivatives(asset_id, width, format, path, bytes) · generated at publish, safe to delete, regenerated on the next build
- releases(id, site_id, built_at, output_dir, file_count, bytes_total, published_at, rolled_back_at)
- redirects(site_id, from_path, to_path, created_at) · because a renamed page that returns nothing is how a site loses its links

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_PATHrequired./data/site.dbPages, sections and assets. A working copy of the JSON export.
CONTENT_EXPORT_DIRrequired./contentJSON written on every save. This is the copy that outlives the editor.
ASSETS_DIRrequired./assets/originalsUntouched originals. Never included in a release.
BUILD_DIRrequired./releasesOne directory per release, kept for rollback.
IMAGE_WIDTHSrequired400,800,1200,2000Derivative widths emitted into srcset.
MAX_FILE_BYTESrequired1500000The build fails if any released file exceeds this.
RELEASES_KEEPrequired5Previous releases retained. This is what rollback uses.
DEPLOY_TOKENsecretoptionalsk-...redactedOnly if your host needs one to upload. Unset means you publish by hand.

The build, in order

  1. A content model, not a canvas

    Pages are typed sections, so a redesign changes the renderer and not the content.

    1. Five kinds that fit is better than one kind that holds anything.

      Files src/schema/sections.ts

    2. Options survive a redesign; coordinates are the redesign.

      Files src/render/section.tsx

    3. So the content is readable without the editor from day one.

      Files src/content-export.ts

    done when · tick each as it passes
    watch out
    • A freeform canvas that stores each element at an absolute pixel position looks like the product people want and produces a site that is unreadable on a phone, impossible to restyle, and stuck with whichever font metrics the editor had that day. Store typed sections with layout options and let the renderer decide where things go.
  2. Images that do not cost a visitor their data allowance

    Every image ships at a sensible size with dimensions declared.

    1. Not at request time, because there is no server to request from.

      Files src/build/images.ts

    2. The cheapest fix for a page that jumps while it loads.

      Files src/render/image.tsx

    3. A warning nobody reads is how a site ends up with none of it.

      Files src/build/checks.tstests/alt-text.test.ts

    done when · tick each as it passes
    watch out
    • Shipping the original from a camera and constraining it with a hundred percent width rule sends a twelve megabyte file to a phone to be displayed at four hundred pixels, and without declared dimensions the page jumps as each one lands. Generate derivatives at publish, emit srcset, and always write width and height.
  3. A build that is just a folder

    The same content builds to the same bytes and needs nothing running.

    1. Sort everything, and keep timestamps out of the output.

      Files src/build/index.tstests/deterministic-build.test.ts

    2. So caching is aggressive and still correct.

      Files src/build/assets.ts

    3. It is what publishing, rollback and the size check all read.

      Files src/build/manifest.ts

    done when · tick each as it passes
    watch out
    • Rendering pages from the database on request means the site is only alive while your builder is, so the moment the process dies or the disk fills the whole site is gone rather than merely un-editable. Build to files, and treat the database as a working copy of something that already exists on disk.
  4. Publishing that is all or nothing

    Visitors see the old site or the new one, never a mixture, and you can go back.

    1. The switch is the only moment anything changes for a visitor.

      Files src/publish/upload.ts

    2. Count and hashes, because a truncated upload looks like a successful one.

      Files src/publish/verify.ts

    3. The night you need it is not the night to find out it was never written.

      Files src/publish/rollback.tstests/rollback.test.ts

    done when · tick each as it passes
    watch out
    • Syncing the build over the live site file by file leaves visitors during the upload with new HTML asking for stylesheets that are not there yet, and a failure halfway leaves the site permanently in that state with no way back. Upload a whole release, verify it, switch, and keep the previous one so rollback is one command.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more website builders.