AIDA Labs · Library
How AIDA Labs builds safely: guardrails in practice
AIDA Labs · Library · Method document · First written 3 October 2026 · Updated for canonical v2 (4 October 2026)
1. The roles
| Who | Does | Never does |
|---|---|---|
| AI (Claude) | Reads the files it has been given, drafts pages and configs, writes deploy scripts, tests in its own sandbox, renders previews, explains each step before it runs | Runs anything against production servers, databases, containers or schedulers; deletes files; opens folders, drives or accounts it wasn't given |
| Mike | Grants access folder by folder, reviews previews, reads and runs every script, approves each change before it goes live | — |
| The scripts | Back up first, test before switching anything on, undo themselves on failure, check the result | Touch anything outside their stated target |
2. The rules
- A person runs anything that touches production. AI writes a saved zsh script and explains it. Mike reads it and runs it. AI has no path to the server.
- Scripts, not loose commands. Every step is a numbered, saved file in a folder on the Mac, with a header saying what it does, what it touches and what it doesn't. The same file can be re-read, re-run or audited later.
- Explain first, command last. The explanation arrives in its own message. The final message is only the command, so it can be pasted safely.
- Read-only first. Before changing anything, take an inventory (a dump of the nginx config, a copy of the site folder) and work from facts, not guesses.
- Back up before every change. Server folders go to
.safe_keep/<name>_<timestamp>and nginx to a server file. - Test before switching on. nginx changes are syntax-tested in the AI's sandbox first, then again with
nginx -ton the server before any reload. If the test fails, the script puts the old file back, doesn't reload, and stops before uploading anything. - Separate everything. Each site has its own folder and its own nginx block. A deploy for one site can't overwrite another. aidalabs.ca and aida-job-search.ca are kept apart.
- Preview before publish. Every visual change is rendered and approved before it goes live: desktop, iPad mini (744 px) and iPhone 12 mini (375 px).
- Least access, granted case by case. AI only reads folders Mike has explicitly connected, one at a time, for one session. No drives or accounts without permission. Deleting is off unless Mike approves it.
- Every change can be undone. Backups, kept old files (
sites-retired/,.bak_*copies) and rebuild scripts mean any step can be reversed. - Stop rather than guess. Build scripts check that the page still has the layout they expect, and stop if it doesn't.
3. Worked example: building aidalabs.ca on 3 October 2026
Step 1: Inventory before changing anything
- Need: split nginx into separate files so one site can't overwrite another.
- Guardrail: read-only first. AI didn't guess the server setup. It wrote one read-only command (
nginx -Tplus folder listings) that saved a snapshot to a file. Mike ran it. - What it found:
- The job-search site was the server-wide catch-all, so any new aidalabs.ca subdomain without its own block showed the job-search site.
- Lesson:
nginx -Tprints included secret files. A Cloudflare Access token appeared in the snapshot. AI redacted it from the file at once and recommended rotating it. New rule: treat any config dump as sensitive and redact secrets before it's stored.
Step 2: An access boundary was crossed, then made a rule
- What happened: while looking for setup notes, AI searched folders and Google Drive belonging to another project without being asked. Mike stopped it.
- Fix: the boundary was written into the project instructions and into memory: "No accessing any external files or drives or Google Drive without permission." AIDA Labs work now lives only in a folder on the Mac, a folder created for it and granted on its own.
- Lesson: the guardrail that matters most is the one a person can enforce. The incident is recorded here on purpose. Guardrails get better when misses are written down, not hidden.
Step 3: Downloads only, through scripts
- Problem: the AI sandbox can't reach the internet sites.
- Fix: AI wrote read-only download scripts that Mike ran:
01_download_sources,02_download_components,05_fetch_consulting_site,07_fetch_recruiter_look. - Result: AI worked only from those local copies.
- Licensing: the five toolbox components were MIT-licensed. Credit to each author is kept in
toolbox/CREDITS.md. Where CodePen blocked a download, the component was rebuilt from scratch rather than copied from somewhere else.
Step 4: Build and test in the sandbox
Every page was rendered headless at 1440, 744 and 375 px and checked automatically for:
- text smaller than 14 px
- sideways scrolling
- script errors
Problems found and fixed before Mike saw anything:
- clipped accordion captions on phones
- a three-row phone menu
- cards leaving empty space on tablets
Step 5: Preview, then approval
- Previews: screenshots, plus single-file preview pages that Mike could open himself. When a raw page opened unstyled on his Mac, AI explained why and produced self-contained previews instead of changing the site.
- Later: previews were published to toolbox.aidalabs.ca so they could be checked on a real phone and iPad.
Step 6: Deploy with backup, test and automatic undo
04_deploy_toolbox.zsh and 06_deploy_from_consulting.zsh:
- Rebuild locally.
- Back up nginx and the live folder.
- Install one nginx file, then
nginx -t. On failure, restore and stop. - Upload only the site's own folders and fix permissions.
- Check every URL for 200.
They don't touch aida-job-search.ca, the Sample Bank sites or any other file.
Step 7: Exact copies stay exact
- Copied unchanged: hub.aidalabs.ca and blueprint.aidalabs.ca are exact copies of the consulting site's pages. The only changes are the name (AIDA Consulting → AIDA Labs) and the links between sites.
- Known quirks carried over, recorded rather than silently "fixed":
- some small text on the hub page
- live API calls on the blueprint board that don't work on the original either
Step 8: The live site didn't match the preview, so a check was added
- What happened: after a deploy, the new "How we build" pages looked broken on the live site even though the preview was correct. The cause was caching: Cloudflare and the browser were still serving an older copy of the shared stylesheet,
/assets/aidalabs-blue.css. - Fix, part 1: every asset link now carries a version stamp taken from the file's contents (
aidalabs-blue.css?v=<hash>). A changed file always gets a new address, so an old cached copy can never be served in its place. - Fix, part 2: the deploy script now downloads each shared file from the live site and compares it with the copy on the Mac. A mismatch is printed in capitals.
- Lesson: a preview proves the files are right, not that the live site is serving them. New rule: after every deploy, check the live files themselves, not just that the address returns 200.
Step 9: Approve, lock, document
- Lock: when Mike approved the result ("it's perfect, lock it all down"), version 1 was locked. A read-only copy of every source, built site, script and document went into
canonical/2026-10-03_v1/with a SHA-256 checksum for each file, and the live server folders were snapshotted at the same point. - Documents: the documentation was published to library.aidalabs.ca behind a password, because the internal documents include server details. The public explanation stays on aidalabs.ca/how-we-build/.
- Lesson: "done" means approved, locked, documented, and reversible to exactly that point.
Step 10: The Sample Bank sites, with the smallest possible changes
- A copy, not a link: Mike chose to keep the Sample Bank hub and Sample Bank blueprint as separate copies. The originals on aida-job-search.ca stay untouched as emergency backups.
- One script for twins: both project blueprints are built and deployed together by
14_update_blueprints.zsh, so their look can never drift apart. - Lesson: the smallest change is the safest. Inventory, then change one named thing, with a stop if reality doesn't match the plan.
Step 11: Live job data without exposing anyone (4 October 2026)
- The structure of the job database was read first (script 22: columns only, no rows).
- The reports were written as two read-only functions with column-level grants, tested on a copy with fake data, and every forbidden read (tables, client data, descriptions, other functions) was confirmed refused before anything touched the live database.
- Mike ran one script (27) to install them; the Cloudflare steps were his, done once.
Step 12: When a site went "offline", trace before fixing
- Script 28 checked each hop read-only; script 29 fixed the one real fault (two services sharing the name "api"); script 30 traced from the server itself. Nothing was changed on a guess.
Step 13: Making live data rock solid (4 October 2026, evening)
- Symptom: Sample Bank dashboards said "Database not reachable" on and off, then constantly after a Docker restart.
- Permanent fix: unique service names (33); restart-always on every data container plus a one-minute watchdog on Toronto (34, 35); a safety net on Quebec that serves the last good answer if Toronto fails (35).
- Lesson: the command was pasted twice and the second run of 35 damaged the saved nginx files (the running server was unaffected). Script 36 repaired them and was itself run twice with identical results. Every server-changing script must be safe to run twice, and its undo path must never delete what an earlier run installed.
- Verified: nginx test passes (reboot-safe), 13 sites answer, every live-data path 20 of 20, twice.
4. The checklist, for any new AIDA Labs site
- Is the work inside a folder that was granted for it?
- Was a read-only inventory taken before any change?
- Is every change a saved, commented zsh script?
- Does the script back up first, test before switching on, and undo on failure?
- Does it touch only its own site's folder and nginx block?
- Did the change pass checks at 1440, 744 and 375 px, with no text under 14 px and no sideways scrolling?
- Was a preview approved before publishing?
- Are third-party code and content licensed and credited?
- Were secrets kept out of files, chats and screenshots?
- After deploying, do the live shared files match the local ones (version stamps, live comparison)?
- Once approved, is the version locked (read-only copy with checksums) and documented in the library?
- Does every config edit change only one named block, and stop if that block isn't as expected?
- Is the original kept untouched as a backup when a site is copied?
- Is anything that went wrong written down here?