Getting started
Installation
Faster path
Copy-paste Herd .env, seed summary, and minimal commands: Quick Start. This page is the fuller install reference.
This guide gets externa-core running on your machine: PHP dependencies, SQLite (default), frontend build, seeded roles and super admin, and the concurrent dev processes (HTTP, queue, logs, Vite).
Requirements
| Requirement | Notes |
|---|---|
| PHP 8.4+ | Matches composer.json (^8.4) and the repo .php-version pin. |
| Composer 2 | Used for composer setup and composer run dev. |
| Node.js + npm | Frontend via Vite; Node 24 is pinned in .nvmrc when present. |
| SQLite (default) | Or configure MySQL/Postgres via DB_* after copying .env. |
| Laravel Herd (recommended on macOS) | Provides PHP, nginx, and convenient .test hosts; Sail is also available as a Composer dev dependency if you prefer containers. |
Optional for AI features:
- An OpenAI-compatible local gateway (e.g. LM Studio) listening where
LOCAL_AI_URLpoints (defaulthttp://127.0.0.1:1234/v1). - A model that supports tool calling reliably — tiny models often hallucinate successful tool results.
Note
You need a queue worker for zip downloads, larger/bulk file duplicates, and collection imports. The composer run dev script starts php artisan queue:listen for you. If you only run php artisan serve, start a worker in a second terminal.
Recommended: Laravel Herd
- Install Laravel Herd and ensure the CLI PHP version is 8.4+.
- Clone or place externa-core in a directory Herd serves (or use
herd link/ park as you usually do). - Open a terminal in the project root for the steps below.
Herd is optional — any local PHP 8.4+ + Composer + Node toolchain works the same for composer setup.
Step-by-step install
1. Get the code
cd /path/to/externa-core
2. Run Composer setup
The setup script in composer.json performs the full bootstrap:
composer setup
That runs, in order:
composer install- Copy
.env.example→.envif.envis missing php artisan key:generatephp artisan migrate --forcenpm installnpm run build
Note
If you use SQLite and database/database.sqlite does not exist yet, create it before migrate (Laravel’s create-project flow does this automatically; a fresh clone may need touch database/database.sqlite).
# Only if the SQLite file is missing
touch database/database.sqlite
3. Review environment defaults
Open .env and confirm at least:
APP_URL=http://localhost
DB_CONNECTION=sqlite
SESSION_DRIVER=database
QUEUE_CONNECTION=database
CACHE_STORE=database
For Herd with a custom host, set APP_URL to that URL (including scheme). HTTP is fine for the rest of Externa (password login, TOTP, CMS, and password confirm on /user/confirm-password). Passkeys (sign-in, register, Confirm with passkey) need a secure context — http://externa-core.test hides those CTAs (browser rule). Local passkey testing: herd secure …, APP_URL=https://…, and Vite detectTls for the site host — Passkeys — Local HTTPS; confirm-password details: Confirm password with a passkey. File and AI tunables: Environment variables.
4. Link public storage
Required for publicly served uploads and related assets:
php artisan storage:link
5. Seed roles, permissions, and super admin
php artisan db:seed
DatabaseSeeder calls:
PermissionSeeder— permissions fromApp\Enums\PermissionEnumviapermissions:syncRoleSeeder— rolessuper-admin,admin,reader, and system rolepublicCreateSuperAdminSeeder— first login user fromconfig/super_admin.php
Override credentials before seeding if needed:
INITIAL_SUPER_ADMIN_FIRST_NAME=Super
INITIAL_SUPER_ADMIN_LAST_NAME=Admin
INITIAL_SUPER_ADMIN_EMAIL=superadmin@example.com
INITIAL_SUPER_ADMIN_PASSWORD=password
Defaults when unset: email superadmin@example.com, password password.
Warning
Seeding is meant for local (or carefully controlled) environments. Re-running seeders may conflict with existing users or permission rows depending on seeder idempotency. Prefer fresh databases for demos.
6. Start the development stack
composer run dev
This uses concurrently to run:
| Process | Command |
|---|---|
| server | php artisan serve |
| queue | php artisan queue:listen --tries=1 --timeout=0 |
| logs | php artisan pail --timeout=0 |
| vite | npm run dev |
Open the URL shown by artisan serve (or your Herd host). Unauthenticated visits to / redirect to login; authenticated users go to the dashboard.
Full stack (Herd Redis + Reverb + Pulse)
When Redis and Herd Pro Reverb are enabled (see Minimal vs full stack):
- Copy
BROADCAST_*/REVERB_*/VITE_REVERB_*/ Pulse keys from.env.example(Herd defaults on:8080). - Restart Vite after any
VITE_REVERB_*change. - Run the two long-lived workers (Herd Reverb is already up — do not also
reverb:start):
php84 artisan horizon
php84 artisan pulse:work
Dashboard Health then shows native Pulse/Horizon cards. Echo powers notifications, presence, and the avatar connection dot (Reverb & Echo).
Alternative: SSR-oriented dev
If you need the Inertia SSR path:
composer run dev:ssr
That builds SSR assets and runs serve, queue, pail, and inertia:start-ssr instead of the Vite HMR process used by composer run dev.
First login
- Go to the login page (Fortify).
- Sign in with the seeded super admin (default
superadmin@example.com/password). - Confirm you can open Dashboard, Users, Collections, and Files according to
super-adminpermissions.
Built-in roles after seed:
| Role | Access summary |
|---|---|
super-admin | Every permission |
admin | Every permission |
reader | Read/show permissions only (can-show-*) |
public | System / not assignable; no Spatie admin permissions — Public CMS API actor (collection/file matrices in role UI) |
Create additional users from the admin UI or tinker; assign roles and/or groups so effective permissions resolve as expected.
Queue worker (required for background work)
With QUEUE_CONNECTION=database, jobs are stored in the jobs table. Keep a worker running whenever you use:
- Multi-file / folder zip preparation (
PrepareFilesZipJob) - Duplicate of folders, bulk selections, or files above
FILES_DUPLICATE_SYNC_MAX_BYTES(DuplicateFilesJob) - Collection imports (
ImportCollectionJob)
composer run dev already includes queue:listen. Standalone:
php artisan queue:listen --tries=1 --timeout=0
Warning
If zips stay “preparing”, imports never finish, or large duplicates hang, the usual cause is no worker (or a worker pointed at a different .env / database).
Optional: local AI gateway
AI chat works without a remote cloud key when the local provider is configured (default).
- Start LM Studio, vLLM, or another OpenAI-compatible server.
- Align
.envwith the gateway:
AI_DEFAULT_PROVIDER=local
LOCAL_AI_URL=http://127.0.0.1:1234/v1
LOCAL_AI_API_KEY=
LOCAL_AI_MODEL=openai/gpt-oss-20b
- Prefer a model with reliable tool calling. The
.env.examplecomment warns that tiny models often hallucinate success. - Optional hardening / features:
AI_REMOTE_IMPORT_HOSTS=
AI_WEBHOOK_TOKEN=
AI_DAILY_PROMPT_LIMIT=0
AI_EMBEDDINGS_ENABLED=false
AI_MCP_ENABLED=false
AI_DAILY_PROMPT_LIMIT=0 means no daily cap (see config/ai.php). Remote JSON import hosts must be allowlisted via AI_REMOTE_IMPORT_HOSTS when you use those tools.
Open the AI section in the admin UI once the app and gateway are up. Deeper behavior is covered in AI assistant model and AI configuration.
Useful Artisan follow-ups
# Re-sync permission definitions from the enum (ops / upgrades)
php artisan permissions:sync
# Optional: drop DB permissions no longer present on PermissionEnum
php artisan permissions:sync --prune
# Cleanup helpers (zips, uploads, AI)
php artisan files:cleanup-zips
php artisan files:cleanup-uploads
php artisan list | grep -E 'files:|ai:|permissions:'
Other operational commands (AI sync sources, AI attachment cleanup, chat verification) live under app/Console/Commands — run php artisan list for the full set.
Verify the install
Smoke checklist:
- Login with the seeded super admin.
- Create a collection and a field; create an item.
- Upload a small file in the file manager; confirm it appears after
storage:link. - Trigger a zip of multiple files and confirm the queue worker processes
PrepareFilesZipJob. - (Optional) Send an AI prompt that lists collections — confirms gateway + tools.
Automated tests:
php artisan test
# or
composer test
Troubleshooting
APP_KEY / cipher errors
.env is missing or empty APP_KEY. Run:
php artisan key:generate
Migrate fails on SQLite
Ensure database/database.sqlite exists and DB_CONNECTION=sqlite. Remove stale DB_DATABASE host settings left over from MySQL examples if they confuse your tooling.
Frontend is blank or outdated
Run Vite (composer run dev or npm run dev), or rebuild:
npm run build
If the site is on Herd HTTPS (herd secure) and the admin is black/blank, check that Vite detectTls uses the site host (so public/hot is https://your-site.test:5173, not 127.0.0.1) — Passkeys — Local HTTPS.
404 on uploaded file URLs
Run php artisan storage:link and confirm FILESYSTEM_DISK / public disk configuration.
Jobs never run
Confirm QUEUE_CONNECTION=database, that migrations created queue tables, and that queue:listen or queue:work is running against the same database as the web process.
AI tools “succeed” but nothing changes
Usually a weak local model inventing tool results. Switch LOCAL_AI_MODEL to a tool-capable model and watch pail / Laravel logs for real tool exceptions.
Permission cache after seeding or role edits
Spatie caches permissions. Seeders call forgetCachedPermissions; if you edited the DB by hand:
php artisan permission:cache-reset
(or the equivalent provided by your installed spatie/laravel-permission version).
Port already in use
Change the serve port (php artisan serve --port=8001) or stop the other process. Update APP_URL to match.
Next steps
- Prefer the short path next time: Quick Start
- Map the codebase: Project layout
- Tune
.env: Environment variables - Understand access control: Effective permissions
- Hit the Public API / GraphQL: externa-bruno (open in Bruno, env Local) · Public CMS API
- Run background work in production: Queues & scheduler