3 · Storage, symlinks & SSL
Objective — make the public directory serveable and give the project a secure local domain: create the storage symlink, detect and wire the app’s addon system (Modules vs packages vs addons), then link the project to Herd over HTTPS and prove the installer route is reachable.
Background
Section titled “Background”Before the installer can run, the public directory needs its symlinks and the project needs a secure local domain. Addon systems differ — get the detection right and you avoid a whole class of “missing assets” bugs.
1. Create the standard storage symlink
Section titled “1. Create the standard storage symlink”php artisan storage:link errors with “The [public/storage] link already exists” on re-runs — ugly, not fatal. Make it idempotent.
-
Create the link only if it’s missing.
Terminal window if [ -L public/storage ]; thenecho "✅ public/storage symlink already exists"readlink public/storage # sanity-check the targetelsephp artisan storage:linkfi# Expected: either the existing target prints, or a fresh symlink is created- ✅
public/storageis a symlink and its target is verified.
- ✅
2. Detect your app’s addon system
Section titled “2. Detect your app’s addon system”CodeCanyon apps deliver addon assets in one of three ways. Detect which yours uses — the action differs.
-
Probe for the four addon directory shapes.
Terminal window [ -d Modules ] && echo "📦 Modules/ — nwidart/laravel-modules (copy-based, NO symlink)"[ -d packages ] && echo "📦 packages/ — in-tree packages (symlink required)"[ -d addons ] && echo "📦 addons/ — in-tree addons (symlink required)"[ -d uploads ] && echo "📦 uploads/ — root uploads dir (symlink required)"# Expected: a line per directory that exists — tells you which mechanism applies- ✅ The app’s addon mechanism is identified.
-
Match it to the correct action.
Mechanism Example apps How assets reach public/Action Symlink ( packages/,uploads/,addons/)MagicAI, WorkDo, older CodeCanyon apps public/packages → ../packageslets the server serve addon files directlyCreate the symlink (§3) Copy ( Modules/vianwidart/laravel-modules)ResiDoro, Worksuite, SocietyPro, Froiden apps php artisan module:publish-assetscopies intopublic/modules/<name>/No symlink — the installer (page 4) runs module:publish-assets. Re-run after manual module updates.Build output ( Modules/*/public/compiled)Newer modular apps Vite builds module assets into public/build/No symlink — handled by npm run build(page 1)- ✅ The right action for this app’s mechanism is identified.
3. Create addon symlinks (only if needed)
Section titled “3. Create addon symlinks (only if needed)”Run these only when §2 detected the directory and it’s a symlink-based system (not Modules/).
-
Create the symlinks for whichever directories exist.
Terminal window if [ -d packages ] && [ ! -L public/packages ]; thenln -s ../packages public/packages && echo "✅ public/packages → ../packages"fiif [ -d uploads ] && [ ! -L public/uploads ]; thenln -s ../uploads public/uploads && echo "✅ public/uploads → ../uploads"fiif [ -d addons ] && [ ! -L public/addons ]; thenln -s ../addons public/addons && echo "✅ public/addons → ../addons"fi# Expected: a ✅ line per symlink created (nothing for Modules/ apps)- ✅ Each required addon symlink exists (or nothing, correctly, for
Modules/).
- ✅ Each required addon symlink exists (or nothing, correctly, for
-
Verify what exists before moving on.
Terminal window ls -la public/ | grep "^l" || echo "(none — normal for Modules/ apps)"[ -d storage/app/public ] && echo "✅ storage/app/public" || echo "❌ MISSING"# Expected: the symlinks list + "✅ storage/app/public"- ✅ Symlinks and
storage/app/publicare confirmed present.
- ✅ Symlinks and
4. Link the project to Herd and secure it with TLS
Section titled “4. Link the project to Herd and secure it with TLS”Give the project a local .test domain and a trusted certificate, then prove both routes resolve.
-
Link the project to Herd.
Terminal window herd link [PROJECT_NAME]herd links | grep -i "[PROJECT_NAME]"# Expected: the project appears in the Herd links list- ✅
[PROJECT_NAME].testis registered with Herd.
- ✅
-
If the link is stale or wrong, remove and recreate it.
Terminal window rm ~/Library/Application\ Support/Herd/config/valet/Sites/[PROJECT_NAME]herd link [PROJECT_NAME]# Expected: a fresh, correct link entry- ✅ The link points at the right project root.
-
Secure it with TLS and restart Herd.
Terminal window herd secure [PROJECT_NAME]herd restart# Expected: a certificate is issued and Herd reloads- ✅ HTTPS is enabled for
[PROJECT_NAME].test.
- ✅ HTTPS is enabled for
-
Confirm the cert exists and both routes resolve.
Terminal window ls -la ~/Library/Application\ Support/Herd/config/valet/Certificates/ | grep -i "[PROJECT_NAME].test" \|| echo "❌ No cert — re-run herd secure"curl -sI -o /dev/null -w "root: HTTP %{http_code} | TLS %{ssl_verify_result}\n" https://[PROJECT_NAME].testcurl -sI -o /dev/null -w "install: HTTP %{http_code}\n" https://[PROJECT_NAME].test/install# Expected: a cert file listed; /install returns HTTP 200 (root 500 is acceptable here)- ✅ The cert file exists and
/installreturns HTTP 200.
- ✅ The cert file exists and
The expected route states depend on whether the installer has run yet:
| URL | Expected (empty DB, first run) | After installer (page 4) |
|---|---|---|
https://[PROJECT_NAME].test (root) | HTTP 500 acceptable — see below | 200 or 302 (redirect to login) |
https://[PROJECT_NAME].test/install | HTTP 200 — REQUIRED | 404 or 403 (blocked on page 6) |
5. Confirm the installer page in a browser
Section titled “5. Confirm the installer page in a browser”The curl check proves the route responds; only a human can confirm the certificate is trusted and the welcome page renders.
-
Open the installer page and confirm the welcome screen + padlock. A cert warning means the TLS install failed; re-run
herd secureand restart Herd.- ✅ The installer welcome page renders with a valid TLS padlock.
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🤖 Storage symlink present —
public/storagelinked and target verified. - 🤖 Addon system identified — Modules / packages / addons / uploads.
- 🤖 Addon symlinks created — or correctly skipped for
Modules/. - 🤖 Herd + TLS done —
herd link+herd secure; cert file present. - 🔀 Routes verified —
curlshows/install= HTTP 200; browser (👤) shows the welcome page with a valid padlock.