# Quick Start

Source: https://docs.qiick.io/externa/quick-start

Get **externa-core** running on Laravel Herd in a few minutes. For troubleshooting, AI gateway setup, and SSR, use the full [Installation](https://docs.qiick.io/externa/installation) guide.

## Prerequisites

| Requirement                      | Notes                                                                                                                                                                |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **PHP 8.4.27+** (or **8.5.12+**) | Pinned major in `.php-version` / `composer.json` (`^8.4`). Use a patched Herd PHP CLI — [security notice](https://github.com/qiick-io/externa-core/discussions/247). |
| **Composer 2**                   | PHP dependencies and `composer setup` / `composer run dev`.                                                                                                          |
| **Node.js 24**                   | Pinned in `.nvmrc` and `package.json` `engines`. Run `nvm use` (or equivalent).                                                                                      |
| **Laravel Herd**                 | Serves the site at a `.test` host (e.g. `http://externa-core.test`).                                                                                                 |
| **Postgres** (this example)      | Or switch to SQLite — see the commented block in the `.env` below.                                                                                                   |
| **Redis** (optional)             | Needed for Horizon, Reverb-friendly realtime, and Pulse redis ingest. Enable in Herd.                                                                                |

Optional: [Herd Pro](https://herd.laravel.com) Reverb on `:8080` for live admin notifications.

## 1. Get the code and enter

**Operators (preferred):**

```bash
composer create-project qiick/externa-core externa-core
cd externa-core
nvm use   # Node 24
php artisan externa:install
```

`create-project` does **not** migrate or assume SQLite — next step is always the installer. Details: [Packagist & create-project](https://docs.qiick.io/externa/packagist).

**Not an update channel:** after you push this tree to your own git remote, stay current via git tags / `upstream` merge or `externa:upgrade --sync-upstream=…` (or Docker image pull), then `php artisan externa:upgrade` — [Upgrade](https://docs.qiick.io/externa/upgrade).

Then skip to [Start](#4-start-the-stack) if the installer already seeded and built. Otherwise continue with a hand-edited `.env` below (Herd Postgres example).

**Contributors:**

```bash
git clone <your-fork-or-remote> externa-core
cd externa-core
nvm use   # Node 24
```

Park or link the folder in Herd so `http://externa-core.test` resolves (or set `APP_URL` to your Herd host). HTTP is enough for password / TOTP / CMS; passkeys on a `.test` host need `herd secure` + `https://…` — [Passkeys](https://docs.qiick.io/externa/passkeys#requirements).

## 1b. Interactive installer only (clone / existing tree)

```bash
php artisan externa:install
```

Same wizard as after create-project: minimal profile needs no Redis; keeps a valid existing `APP_KEY` or generates one; re-run with `--force` when intentional ([#249](https://github.com/qiick-io/externa-core/pull/249)). Skip to [Start](#4-start-the-stack) when done.

## 2. Environment

Either copy the example and edit:

```bash
cp .env.example .env
```

Or paste the Herd-oriented block below into `.env` (safe local defaults — no production secrets).

Create the Postgres database `externa_core` in Herd (or your local Postgres) before migrate. For zero-config SQLite instead, use the commented `DB_*` lines and `touch database/database.sqlite`. Matrix and MySQL/MariaDB notes: [Supported databases](https://docs.qiick.io/externa/supported-databases).

```env
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
# HTTP OK for password/TOTP/CMS. Passkeys need HTTPS (or localhost) — see /externa/passkeys
APP_URL=http://externa-core.test

APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US

APP_MAINTENANCE_DRIVER=file

BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

# Postgres (typical Herd local DB). For SQLite instead:
# DB_CONNECTION=sqlite
# (and: touch database/database.sqlite)
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=externa_core
DB_USERNAME=root
DB_PASSWORD=

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

FILESYSTEM_DISK=local
CACHE_STORE=database

MEMCACHED_HOST=127.0.0.1

# Redis — enable in Herd. Without Redis: QUEUE_CONNECTION=database, BROADCAST_CONNECTION=log
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

QUEUE_CONNECTION=redis
# QUEUE_CONNECTION=database

BROADCAST_CONNECTION=reverb
# BROADCAST_CONNECTION=log

# Herd Pro Reverb defaults (Services → Reverb). Host must be 127.0.0.1 / localhost for Echo.
REVERB_APP_ID=1001
REVERB_APP_KEY=laravel-herd
REVERB_APP_SECRET=secret
REVERB_HOST=127.0.0.1
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

PULSE_ENABLED=true
PULSE_INGEST_DRIVER=redis
PULSE_REDIS_CONNECTION=pulse
PULSE_STORAGE_KEEP="2 days"
PULSE_INGEST_KEEP="2 days"

MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"

FILES_DUPLICATE_SYNC_MAX_BYTES=52428800
FILES_ZIP_TTL_MINUTES=60
FILES_ZIP_MAX_BYTES=104857600

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
AI_REMOTE_IMPORT_HOSTS=
AI_WEBHOOK_TOKEN=
AI_DAILY_PROMPT_LIMIT=0
AI_EMBEDDINGS_ENABLED=false
AI_MCP_ENABLED=false
```

> **MySQL / MariaDB instead**
> Create an empty database first, then swap the `DB_*` block:
> 
> ```env
> DB_CONNECTION=mysql
> DB_HOST=127.0.0.1
> DB_PORT=3306
> DB_DATABASE=externa_core
> DB_USERNAME=root
> DB_PASSWORD=
> DB_CHARSET=utf8mb4
> DB_COLLATION=utf8mb4_unicode_ci
> ```
> 
> For MariaDB use `DB_CONNECTION=mariadb` (same host/port pattern; Herd MariaDB often listens on `3307`). Full matrix: [Supported databases](https://docs.qiick.io/externa/supported-databases).

Full key reference: [Environment variables](https://docs.qiick.io/externa/environment-variables). Minimal vs Redis/Reverb/Pulse: [Minimal vs full stack](https://docs.qiick.io/externa/minimal-vs-full-stack).

## 3. Install, migrate, seed

```bash
composer install
php artisan key:generate
php artisan migrate --seed
php artisan storage:link
php artisan wayfinder:generate --with-form --no-interaction

npm ci   # or: npm install
npm run build   # or skip and use `npm run dev` / Vite HMR below
```

**CI / non-interactive shortcut:** `composer setup` runs install → copy `.env` if missing → `key:generate` → migrate → `npm install` → `npm run build`. It does **not** seed, link storage, or generate Wayfinder — still run those (or let Vite generate routes on first `npm run dev`). Operators after create-project should use `externa:install`, not `composer setup`.

## What seeding creates

`php artisan migrate --seed` (or `db:seed`) runs `DatabaseSeeder`:

1. **`PermissionSeeder`** — syncs Spatie permissions from `PermissionEnum` via `php artisan permissions:sync`
2. **`RoleSeeder`** — roles:
   | Role          | Notes                                                                                                                                                       |
   | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | `super-admin` | Every Spatie permission                                                                                                                                     |
   | `admin`       | Every Spatie permission                                                                                                                                     |
   | `reader`      | Only `can-show-*` permissions                                                                                                                               |
   | `public`      | System role (`is_system`, not assignable); **no** Spatie admin permissions — used for anonymous Public CMS API; grant collection/file access in the role UI |
3. **`CreateSuperAdminSeeder`** — first user from `config/super_admin.php`

| Field    | Default (local only)     |
| -------- | ------------------------ |
| Email    | `superadmin@example.com` |
| Password | `password`               |

Override **before** seeding:

```env
INITIAL_SUPER_ADMIN_FIRST_NAME=Super
INITIAL_SUPER_ADMIN_LAST_NAME=Admin
INITIAL_SUPER_ADMIN_EMAIL=superadmin@example.com
INITIAL_SUPER_ADMIN_PASSWORD=password
```

> Change or remove default credentials before any shared or production deploy. Do not commit real secrets.

After upgrades, re-sync permission definitions:

```bash
php artisan permissions:sync
# optional: php artisan permissions:sync --prune
```

More on the `public` role matrix: [Roles & permissions](https://docs.qiick.io/externa/roles-and-permissions) · [Public CMS API](https://docs.qiick.io/externa/public-cms-api).

## 4. Run

Herd already serves HTTP at `APP_URL`. You still need Vite and a queue path.

**Minimal** (no Redis — set `QUEUE_CONNECTION=database` and `BROADCAST_CONNECTION=log`):

```bash
composer run dev
# serve + queue:listen + pail + vite
```

With Herd serving the site you can skip `artisan serve` and run Vite + a worker yourself (`npm run dev` and `php artisan queue:listen --tries=1 --timeout=0`).

**Full stack** (matches the `.env` above — Redis + Herd Reverb + Pulse):

```bash
php artisan horizon
php artisan pulse:work
npm run dev
```

Do **not** also run `php artisan reverb:start` if Herd Pro already binds `:8080`.

Standalone all-in-one (starts its own Reverb — conflicts with Herd Reverb on the same port):

```bash
composer run dev:full
```

Open `http://externa-core.test` (or your `APP_URL`). Unauthenticated `/` redirects to login — use the seeded super admin.

## Optional services

| Need               | One-liner                                          | Docs                                        |
| ------------------ | -------------------------------------------------- | ------------------------------------------- |
| Redis              | Enable in Herd; keep `REDIS_*` as above            | [Redis](https://docs.qiick.io/externa/redis-prerequisite)        |
| Horizon            | `php artisan horizon`                              | [Horizon](https://docs.qiick.io/externa/horizon)                 |
| Reverb + Echo      | Herd Pro on `:8080` + `REVERB_*` / `VITE_REVERB_*` | [Reverb & Echo](https://docs.qiick.io/externa/reverb-and-echo)   |
| Pulse              | `php artisan pulse:work` (redis ingest)            | [Pulse & Health](https://docs.qiick.io/externa/pulse-and-health) |
| Queues / scheduler | Ops overview                                       | [Operations](https://docs.qiick.io/externa/operations)           |

## Related packages

| Repo                                                       | Role                                                                                |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| [externa-core](https://github.com/qiick-io/externa-core)   | Laravel + Inertia app (this Quick Start)                                            |
| [externa-bruno](https://github.com/qiick-io/externa-bruno) | Runnable Bruno collection for Public CMS API (`/api/v1`) + GraphQL (`/api/graphql`) |

Clone **[externa-bruno](https://github.com/qiick-io/externa-bruno)**, open that folder in [Bruno](https://www.usebruno.com/), select env **Local**, copy `.env.sample` → `.env` and set `EXTERNA_API_KEY` (and `base_url` if not on `http://externa-core.test`). For staging, duplicate **Local** and change `base_url` + key. Endpoint examples live in the docs ([Public CMS API](https://docs.qiick.io/externa/public-cms-api), [GraphQL](https://docs.qiick.io/externa/graphql)); Bruno executes them.

## Next steps

- Deeper local setup & troubleshooting: [Installation](https://docs.qiick.io/externa/installation)
- Tune every key: [Environment variables](https://docs.qiick.io/externa/environment-variables)
- Minimal vs Redis stack: [Minimal vs full stack](https://docs.qiick.io/externa/minimal-vs-full-stack)
- Code map: [Project layout](https://docs.qiick.io/externa/project-layout)
- Access control: [Effective permissions](https://docs.qiick.io/externa/permissions)
- Headless API: [Public CMS API](https://docs.qiick.io/externa/public-cms-api) · [GraphQL](https://docs.qiick.io/externa/graphql)
- Try requests: [externa-bruno](https://github.com/qiick-io/externa-bruno)
