ArmoredWorks · magento-dev · developer onboarding

From a Gitea account to five working storefronts

One interactive command builds the whole local environment. Nothing is hand-filled and no project secret is handed to you by a person. This page walks the path a new developer takes, what happens at each step, and what to do when something breaks.

5storefronts + admin, HTTPS
1command, then 1 verifier
~21 minmachine time, on the LAN
0credentials typed by hand
40assertions prove it worked

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 .ovpn profile, 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:
    ask samuelcolacchia to mint a Garage key for <you> with read plus
    s3:ListBucket and store it at magento-dev/storage/garage/<you>.
    Normally your admin does this when your Gitea account is created, so the first run just works.

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, unzip present system-wide.
  • Ports 80 and 443 free; roughly 45 GB disk.
WSL2: set 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.

1

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.

2

Clone the repository

git clone https://git.kub3.424cloud.net/metalcloak/magento-dev.git && cd magento-dev
3

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.

4

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.

Bootstrap is a first-run tool, not a repair tool. Run against an environment that already exists it reports what it found and changes nothing. If something is broken, see When it breaks.
5

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.

#StageWhat happensWho
1Platform & toolingFails 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
2Sign inOpens the browser for Gitea OIDC. A one-hour, non-renewable OpenBao token lands in ~/.vault-token.browser click
3Fetch secretsReads 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
4PreflightDocker 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
5HostnamesAll six dev hostnames resolve. If not, it blocks and tells you which file to fix.automatic
6Containerswarden env up: PHP-FPM, nginx, MariaDB 11.4, OpenSearch 3.3, Valkey, RabbitMQ, Varnish, Mailhog.automatic
7Dependenciescomposer install inside the container, against the four licensed registries.automatic
8Config & datasetWrites 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
9Themes & cachesBuilds 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.

StepSecondsNote
OIDC login6human-gated
Secret fetch2
Preflight11all of it, before anything slow
warden env up10warm-cache floor
composer install119warm-cache floor
Dataset import102281% of the run; download plus MariaDB load plus reconcile
Vendor CSP theme build17npm ci
Six theme builds23npm ci each
Cache flush25
Total machine time1255wall 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.

pfSense VPNstep zero. Puts you on the LAN where the store and the bucket live.
Gitea OIDCyour account and group membership. The browser step.
OpenBaobao.svc.424cloud.net. Default-deny policy: four shared Composer paths plus your own Garage path.
Local filesauth.json, .env.warden.local. Plaintext, mode 600, gitignored.
Garage S3192.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 string token)
  • 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 .ovpn profile 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 passed
1the environment is built and one or more assertions failed; the output is the remediation guide
2usage error
3the environment is not built; nothing meaningful was asserted
Sign in afterwards. Admin is at https://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

CommandWhat it does
./scripts/start.shResume stopped containers. Much faster than bootstrap; does not touch the store.
./scripts/stop.shPause 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.shShell into the PHP container (bin/magento, composer live there).
./scripts/build-theme.shProduction Tailwind build for all six themes.
./scripts/build.shFull rebuild: setup:upgrade, di:compile, static deploy, cache flush. After a new module or schema migration.
./scripts/db-import.shRe-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.shMake the database match this checkout: theme assignments, environment values, reindex. Idempotent, prints every change. db-import.sh runs it for you.
./scripts/verify-install.shWhenever 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

WSL2 hosts entries go in two files. WSL2 is a separate VM with its own network stack. Your Windows browser reads the Windows file; Docker reads the Linux one. Missing one gives you a storefront that works from curl but not from Chrome, or the reverse.
The VPN is not only for the database. The secret store is LAN-only too, so bootstrap fails at the sign-in step without the tunnel. On WSL2, connect on the Windows side with mirrored networking.
Certificate warnings after a rebuild. Bootstrap trusts the Warden CA in preflight. If the CA drifted since (a Warden CA regenerated outside a bootstrap), run ./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.
Ports 80 and 443 must be free. Traefik takes both. A local nginx, another Docker project or IIS on Windows will block warden env up.
Composer memory. If composer install dies inside the container, ./scripts/shell.sh then COMPOSER_MEMORY_LIMIT=-1 composer install.
Never put dev settings in 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.

StoreWebsite codeThemeDev URL
MetalCloakbaseArmoredWorks/mc-hyvaapp.magento-dev.test
Carbon OffroadCOArmoredWorks/carbon-hyvacarbon.magento-dev.test
ARS (Adventure Rack Systems)KMSArmoredWorks/kms-hyvaars.magento-dev.test
RocksportRSArmoredWorks/rocksport-hyvarocksport.magento-dev.test
Jobber PortalmcjobArmoredWorks/jobber-hyvajobber.magento-dev.test
Adminapp.magento-dev.test/backend/
Mailhogmailhog.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 .ovpn profile
  • 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.md and docs/agents/: git workflow (all work through PRs into maindev), issue tracker, triage labels, browser QA.
  • CONTEXT.md: the domain model. Read before working on any issue.
  • ./scripts/teardown.sh --help and ./scripts/verify-install.sh --help