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

RequirementNotes
PHP 8.4+Matches composer.json (^8.4) and the repo .php-version pin.
Composer 2Used for composer setup and composer run dev.
Node.js + npmFrontend 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_URL points (default http://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.

  1. Install Laravel Herd and ensure the CLI PHP version is 8.4+.
  2. Clone or place externa-core in a directory Herd serves (or use herd link / park as you usually do).
  3. 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:

  1. composer install
  2. Copy .env.example.env if .env is missing
  3. php artisan key:generate
  4. php artisan migrate --force
  5. npm install
  6. npm 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.

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:

  1. PermissionSeeder — permissions from App\Enums\PermissionEnum via permissions:sync
  2. RoleSeeder — roles super-admin, admin, reader, and system role public
  3. CreateSuperAdminSeeder — first login user from config/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:

ProcessCommand
serverphp artisan serve
queuephp artisan queue:listen --tries=1 --timeout=0
logsphp artisan pail --timeout=0
vitenpm 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):

  1. Copy BROADCAST_* / REVERB_* / VITE_REVERB_* / Pulse keys from .env.example (Herd defaults on :8080).
  2. Restart Vite after any VITE_REVERB_* change.
  3. 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

  1. Go to the login page (Fortify).
  2. Sign in with the seeded super admin (default superadmin@example.com / password).
  3. Confirm you can open Dashboard, Users, Collections, and Files according to super-admin permissions.

Built-in roles after seed:

RoleAccess summary
super-adminEvery permission
adminEvery permission
readerRead/show permissions only (can-show-*)
publicSystem / 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).

  1. Start LM Studio, vLLM, or another OpenAI-compatible server.
  2. Align .env with 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
  1. Prefer a model with reliable tool calling. The .env.example comment warns that tiny models often hallucinate success.
  2. 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:

  1. Login with the seeded super admin.
  2. Create a collection and a field; create an item.
  3. Upload a small file in the file manager; confirm it appears after storage:link.
  4. Trigger a zip of multiple files and confirm the queue worker processes PrepareFilesZipJob.
  5. (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

Previous
Quick Start