Before you start
The whole flow rests on two identities that an admin issues to you together. Everything else is fetched by script.
Identity issued to you
- A Gitea account on
git.kub3.424cloud.net, in the developer group. It clones the repo and unlocks the project credentials via OIDC. - A pfSense VPN account plus the shared
.ovpnprofile, from samuelcolacchia. The secret store and the snapshot bucket are LAN-only; without the tunnel bootstrap stops at its first step. - A read-only key to the snapshot bucket, minted by samuelcolacchia for your Gitea username and stored in OpenBao at
magento-dev/storage/garage/<you>. It is the one credential that is yours alone, so it is the one that can be revoked without touching anyone else. You never see or type it: bootstrap reads it from the store and writes it to.env.warden.local. If it has not been minted yet, bootstrap stops in its preflight, before anything slow, with the exact request to send:
Normally your admin does this when your Gitea account is created, so the first run just works.ask samuelcolacchia to mint a Garage key for <you> with read plus s3:ListBucket and store it at magento-dev/storage/garage/<you>.
Machine yours
- Linux or WSL2 only. macOS is not supported and the scripts fail fast saying so.
- Docker: Engine on Linux, or Docker Desktop with WSL integration on Windows.
- sudo, once, so Warden can trust its local CA.
git,curl,python3,unzippresent system-wide.- Ports 80 and 443 free; roughly 45 GB disk.
networkingMode=mirrored first. In NAT mode the Windows VPN tunnel is invisible to Linux. Add [wsl2] / networkingMode=mirrored to C:\Users\<you>\.wslconfig, run wsl --shutdown, reopen your terminal, and connect the VPN on the Windows side.The five steps
This is the canonical procedure from README.md. Four of the five are a line or two; step 4 does the work.
Install Warden once per machine
Warden is a git checkout on your PATH, not a package. The location matters: every script here falls back to ~/.local/warden/bin/warden when your shell has not sourced its rc file.
git clone -b main https://github.com/wardenenv/warden.git ~/.local/warden
echo 'export PATH="$HOME/.local/warden/bin:$PATH"' >> ~/.bashrc # or ~/.zshrc
export PATH="$HOME/.local/warden/bin:$PATH"
main, not a tag and not Homebrew: main pins Traefik 3.6 and carries the routing flag this project's labels need. The clone is all you do; warden install itself runs inside bootstrap.
Clone the repository
git clone https://git.kub3.424cloud.net/metalcloak/magento-dev.git && cd magento-dev
Add hosts entries the one manual step
No script can finish this for you: it needs privilege the script does not have, and on WSL2 it crosses into a Windows file that Linux mounts read-only. Do it before bootstrap.
127.0.0.1 app.magento-dev.test ars.magento-dev.test
127.0.0.1 jobber.magento-dev.test carbon.magento-dev.test rocksport.magento-dev.test
127.0.0.1 mailhog.magento-dev.test
Linux: /etc/hosts with sudo. WSL2: both C:\Windows\System32\drivers\etc\hosts (Notepad as Administrator; your browser uses this one) and /etc/hosts inside the distribution (Docker uses this one). Bootstrap offers to append the Linux half; the Windows half is yours.
Run bootstrap
./scripts/bootstrap.sh
It opens a browser once to sign you in with the Gitea account you just cloned with, asks for your sudo password once, and does everything else without asking. Expect 20 to 25 minutes on the office LAN; over half of it is the dataset import. Nothing is silent for more than 30 seconds, and it prints a per-step timing table at the end.
Verify
./scripts/verify-install.sh
This is the only thing that tells you the environment is good. Not bootstrap's output, and not a storefront that loads. Exit 0 means every assertion passed; anything else prints the claim, observed against expected, the cause, and a copy-pasteable fix.
What bootstrap actually does
Nine stages. The cheap, failure-prone checks all run before anything slow, so a missing grant or a closed tunnel costs you seconds rather than twenty minutes.
| # | Stage | What happens | Who |
|---|---|---|---|
| 1 | Platform & tooling | Fails fast on macOS. Checks Docker, git, python3, unzip. Runs warden install if incomplete. Downloads a version-pinned, sha256-verified bao CLI to ~/.local/bin. | automatic |
| 2 | Sign in | Opens the browser for Gitea OIDC. A one-hour, non-renewable OpenBao token lands in ~/.vault-token. | browser click |
| 3 | Fetch secrets | Reads five paths from OpenBao and writes auth.json (four Composer registries) and .env.warden.local (your Garage key) at mode 600. Echoes each credential by store version and rotation date, never by value. | automatic |
| 4 | Preflight | Docker responding, ports free, disk space, the sudo prompt for the Warden CA, snapshot bucket reachable and rejects writes with your key, all four Composer registries answer 200, newest snapshot named. Prints every failure, not the first. | sudo once |
| 5 | Hostnames | All six dev hostnames resolve. If not, it blocks and tells you which file to fix. | automatic |
| 6 | Containers | warden env up: PHP-FPM, nginx, MariaDB 11.4, OpenSearch 3.3, Valkey, RabbitMQ, Varnish, Mailhog. | automatic |
| 7 | Dependencies | composer install inside the container, against the four licensed registries. | automatic |
| 8 | Config & dataset | Writes app/etc/env.php with a freshly generated crypt key. Resolves the newest sanitized-YYYY-MM-DD.sql.gz in the bucket, downloads it (cached afterwards), imports it, and runs reconcile-db-state.sh to enforce the 44 environment values, theme assignments and reindex. | automatic |
| 9 | Themes & caches | Builds the vendor Hyvä CSP theme and all six Tailwind themes, flushes and enables caches. | automatic |
What a run costs
Measured on the fourth clean-room cycle, 2026-09-08, same machine, office LAN, Docker images and npm packages already cached. Treat it as an expectation, not a promise. A remote run over the VPN has not been measured.
| Step | Seconds | Note |
|---|---|---|
| OIDC login | 6 | human-gated |
| Secret fetch | 2 | |
| Preflight | 11 | all of it, before anything slow |
warden env up | 10 | warm-cache floor |
composer install | 119 | warm-cache floor |
| Dataset import | 1022 | 81% of the run; download plus MariaDB load plus reconcile |
| Vendor CSP theme build | 17 | npm ci |
| Six theme builds | 23 | npm ci each |
| Cache flush | 25 | |
| Total machine time | 1255 | wall clock 1326s; the 71s sudo wait is listed separately, not counted |
Where the credentials come from
You hold one identity. Everything else is derived from it, fetched by script, written to gitignored files at mode 600, and deleted by teardown.
bao.svc.424cloud.net. Default-deny policy: four shared Composer paths plus your own Garage path.auth.json, .env.warden.local. Plaintext, mode 600, gitignored.192.168.1.120:30188, read-only key. Newest dated sanitized snapshot.What is in the bundle
repo.magento.com(shared)hyva-themes.repo.packagist.com(shared; username is the literal stringtoken)composer.amasty.com(shared)repo.mageplaza.com(shared)- Garage S3 key pair (yours, read plus list only)
What is deliberately not
- The Magento crypt key: generated locally, per developer. The snapshot ships no ciphertext, so no key needs to match it.
- The
.ovpnprofile and pfSense credentials: handed over out of band. - Production or staging credentials of any kind.
- The Garage write key: the snapshot owner's only.
- Payment gateway credentials: configure your own in the admin if you need a gateway.
Fetching is event-driven, not scheduled. Bootstrap fetches once; afterwards start.sh, stop.sh, watch.sh, build-theme.sh and shell.sh never touch the store, so the VPN is not a dependency of everyday work. Only an actual credential rotation (a 401 from a registry) triggers a re-fetch.
The verifier is the pass signal
./scripts/verify-install.sh asserts what is true now, so it is runnable at any time: after bootstrap, after a snapshot import, after a week away, or to tell "broken" from "healthy" before choosing repair over rebuild. Forty assertions in four stages.
Stage 0 · Theme sources
Runs verify-hyva-themes.sh: every child theme's theme.xml, registration, Tailwind config and parent chain are what the repo says they are.
Stage 1 · Environment
Warden containers up, MAGE_MODE=developer, the 44 env.php system values in force, reCAPTCHA type_for rows absent, secret files gitignored.
Stage 2 · Data
Admin account present (prints the username, never the password), snapshot age (warns past 120 days), snapshot schema is an ancestor of HEAD, snapshot declares itself ciphertext-free.
Stage 3 · Delivery
Each of the five storefronts is proven by a theme-identity chain (page, then its linked stylesheet, then the expected theme id), the admin login form serves, a session survives a round trip on every website, and a real product goes into a real cart and /checkout/cart/ renders 200 on every store.
Exit codes
0every assertion passed1the environment is built and one or more assertions failed; the output is the remediation guide2usage error3the environment is not built; nothing meaningful was assertedhttps://app.magento-dev.test/backend/. The verifier prints the admin username (it belongs to the dataset, so it varies by snapshot). Every admin password is Admin123!; every customer account is Password123!.Daily workflow
| Command | What it does |
|---|---|
./scripts/start.sh | Resume stopped containers. Much faster than bootstrap; does not touch the store. |
./scripts/stop.sh | Pause containers, keeping all data. |
./scripts/watch.sh [theme] | Tailwind watcher plus BrowserSync live reload. Default theme mc-hyva; also carbon-hyva, kms-hyva, rocksport-hyva, jobber-hyva. |
./scripts/shell.sh | Shell into the PHP container (bin/magento, composer live there). |
./scripts/build-theme.sh | Production Tailwind build for all six themes. |
./scripts/build.sh | Full rebuild: setup:upgrade, di:compile, static deploy, cache flush. After a new module or schema migration. |
./scripts/db-import.sh | Re-import the newest sanitized snapshot. Reports which one it chose and how old the data is; cached locally so a repeat costs no WAN transfer. |
./scripts/reconcile-db-state.sh | Make the database match this checkout: theme assignments, environment values, reindex. Idempotent, prints every change. db-import.sh runs it for you. |
./scripts/verify-install.sh | Whenever you want to know whether the environment is healthy. |
Mail never leaves the environment: everything lands in Mailhog at https://mailhog.magento-dev.test/.
When it breaks
Re-running bootstrap is not the repair mechanism. The sanctioned repair is a soft teardown followed by a bootstrap. Both teardown tiers are dry runs unless you pass --execute, so run them once without it to read the plan.
--soft · the everyday repair
./scripts/teardown.sh --soft # dry run
./scripts/teardown.sh --soft --execute
cp ~/.magento-dev-teardown-backups/env.php.<ts> app/etc/env.php
./scripts/bootstrap.sh
Removes vendor/, generated/, var/, pub/static/, Tailwind outputs and app/etc/env.php (backed up first), stops containers. Keeps the database volume, so no re-import. Use for stale DI, vendor drift, a bad theme build.
--full · prove a cold first run
./scripts/teardown.sh --full
./scripts/teardown.sh --full --execute # type 'full'
Also removes ~/.local/warden, Warden's SSL and tunnel state, bao, the snapshot cache and the database volume. A machine-level act for verifying the clean room, not a bigger repair. After it, redo step 1 (the Warden clone and PATH line) before bootstrapping.
Neither tier touches pub/media/, ~/.npm, ~/.composer, or any container that is not this project's. Both refuse on a dirty git tree unless you pass --force; move or commit stray files rather than forcing.
A genuinely corrupt database is repaired by running ./scripts/db-import.sh deliberately, not by teardown. Bootstrap refuses to import over a populated database and points at --reimport.
Things that bite
curl but not from Chrome, or the reverse../scripts/trust-warden-ca.sh; it is idempotent.warden is not on PATH in non-interactive shells. Agent sessions, CI jobs and bash -c never source your rc file. The repo's scripts fall back to ~/.local/warden/bin/warden; your own one-liners need the export from step 1.warden env up.composer install dies inside the container, ./scripts/shell.sh then COMPOSER_MEMORY_LIMIT=-1 composer install.app/etc/config.php. It is tracked and ships to staging and production. Environment-specific values live in the system section of env.php, and reconcile-db-state.sh owns them. If you find yourself running bin/magento config:set for something environmental, the reconciler is where it belongs.Store map
Host-to-website routing is version controlled in app/etc/stores.php. Theme assignments are database state that the reconciler enforces.
| Store | Website code | Theme | Dev URL |
|---|---|---|---|
| MetalCloak | base | ArmoredWorks/mc-hyva | app.magento-dev.test |
| Carbon Offroad | CO | ArmoredWorks/carbon-hyva | carbon.magento-dev.test |
| ARS (Adventure Rack Systems) | KMS | ArmoredWorks/kms-hyva | ars.magento-dev.test |
| Rocksport | RS | ArmoredWorks/rocksport-hyva | rocksport.magento-dev.test |
| Jobber Portal | mcjob | ArmoredWorks/jobber-hyva | jobber.magento-dev.test |
| Admin | — | — | app.magento-dev.test/backend/ |
| Mailhog | — | — | mailhog.magento-dev.test |
The stack is Hyvä Commerce on Magento 2.4.9 CE: Tailwind and Alpine.js, no RequireJS, no Knockout, no jQuery. Check vendor/hyva-themes/magento2-default-theme when unsure how something should be done.
Who to ask, and what to read
samuelcolacchia
- Gitea account and group membership
- pfSense account and the
.ovpnprofile - Your Garage key in OpenBao (bootstrap prints the exact path to ask for)
- Snapshot owner: a fresh sanitized snapshot is produced quarterly
- OpenBao itself, if bootstrap reports the store sealed or down
In the repository
README.md: the canonical procedure. If any other document disagrees with it about what to run, README wins.docs/credentials-and-config.md: where every credential and config value lives and why.AGENTS.mdanddocs/agents/: git workflow (all work through PRs intomaindev), issue tracker, triage labels, browser QA.CONTEXT.md: the domain model. Read before working on any issue../scripts/teardown.sh --helpand./scripts/verify-install.sh --help