---
title: "Installation — Sentinel for Laravel | Roundly"
description: "Install via Composer, run the guided sentinel:install, settle the morph key types, migrate, create the first key and baseline existing rows."
url: https://roundly-consulting.com/open-source/docs/sentinel-for-laravel/installation
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Sentinel for Laravel](https://roundly-consulting.com/open-source/docs/sentinel-for-laravel.md)

# Installation

Require the package and run the guided installer. The service provider is auto-discovered; the package registers no global facade alias, so always import RoundlyConsulting\\Sentinel\\Facades\\Sentinel:

```bash
composer require roundly-consulting/sentinel-for-laravel
php artisan sentinel:install
```

sentinel:install publishes the configuration and the migrations, shows the morph key types to settle before migrating, generates the default ring’s key when it has none — with the ring’s configured algorithm, printed as environment lines (.env is never written) — and prints the next steps. It never edits host files beyond the publish. It exits 1 when generating the key fails (the next steps are still printed); running it again is harmless, and --force overwrites published files.

## The same steps by hand

Publish and run the migrations — six tables, all prefixed sentinel\_. They are publish-only and forward-only, never auto-loaded:

```bash
php artisan vendor:publish --tag="sentinel-migrations"
php artisan migrate
```

Optionally publish the configuration file and the translations (English and Slovak):

```bash
php artisan vendor:publish --tag="sentinel-config"
php artisan vendor:publish --tag="sentinel-translations"
```

| Tag | Publishes |
| --- | --- |
| `sentinel-migrations` | Six publish-only, forward-only migrations (no down()), timestamp-injected. |
| `sentinel-config` | config/sentinel.php |
| `sentinel-translations` | lang/vendor/sentinel/{en,sk} |

## Choose the key types, then migrate

The morph columns follow sentinel.key\_type (sealed models) and sentinel.actor\_key\_type (actors, key owners, nonce subjects) — bigint by default, or uuid / ulid. Set them before migrating. A fleet mixing integer and UUID keys on MySQL or SQLite picks uuid: the column is a char(36) there, which holds integer keys as strings too. On PostgreSQL the uuid column is native and refuses integer keys, so a mixed fleet publishes the migrations and changes those id columns to string(36) first. Both settings are read strictly: anything but bigint, uuid or ulid (any case) makes the migration throw — a typo never silently builds bigint columns.

sentinel\_keys, sentinel\_idempotency\_keys and sentinel\_nonces use sentinel.database.connection; sentinel\_checkpoints, sentinel\_ledger and sentinel\_seals live on each sealable model’s connection and share its transaction. With sealables on several connections, run migrations 0002–0004 on each and list the connections in sentinel.ledger.connections.

## Create the first key

The default ring uses the config driver: the command prints three environment lines and never writes .env itself. Add them to your secret store and treat them as secrets:

```bash
php artisan sentinel:key:generate
# SENTINEL_KEY_ID=default-20261002-k3f9qa
# SENTINEL_ALGORITHM=hmac-sha256
# SENTINEL_KEY="base64:…"
```

Use --algorithm=ed25519 (or ecdsa-p256-sha256, …) for asymmetric seals; the public half is printed as SENTINEL\_PUBLIC\_KEY for verify-only nodes. Keys for HTTP partners live in the http ring (database driver) — generate one, or import the partner’s own:

```bash
php artisan sentinel:key:generate --ring=http --database
php artisan sentinel:key:import acme-2026-10 --ring=http --algorithm=ed25519 --file=acme.pem --owner-type=partner --owner-id=7
```

## Seal a model and adopt existing rows

```php
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Sentinel\Concerns\HasSeals;
use RoundlyConsulting\Sentinel\Contracts\Sealable;
use RoundlyConsulting\Sentinel\Definition\SealBuilder;

final class Invoice extends Model implements Sealable
{
    use HasSeals;

    public static function defineSeals(SealBuilder $seals): void
    {
        $seals->seal('financial')->attributes('customer_id', 'currency', 'amount', 'status');
    }
}
```

Every Eloquent write now seals the row. Seals are strict by default — a row without a seal is a finding — so baseline the rows that exist today, once, with a reason that ends up in the ledger. Rows that already have ledger history but lost their seal are reported, never baselined: a deleted seal is evidence.

```bash
php artisan sentinel:seal-missing "App\Models\Invoice" --reason="Initial baseline"
```

## Quick start

Verify wherever it matters — in code, as route middleware or in the scheduled scan:

```php
use RoundlyConsulting\Sentinel\Facades\Sentinel;

$invoice->isIntact();                          // true when every seal verifies
Sentinel::for($invoice)->verifyOrFail();       // throws TamperedModelException otherwise

Route::get('/invoices/{invoice}', ShowInvoice::class)->middleware('sentinel.verified');
```

When someone changes a sealed value outside the application, verification names the changed column and the model is refused for further writes until someone acknowledges the change with a reason:

```php
// Someone runs UPDATE invoices SET amount = 0 WHERE id = 42 in a SQL console:
Sentinel::for($invoice)->verify();
// VerificationResult { status: Tampered, reason: 'mac', changedAttributes: ['a:amount'], … }

$invoice->update(['note' => 'x']);             // TamperedModelException: refused until acknowledged

Sentinel::for($invoice)->by($admin)->because('INC-88: refund fixed by the DBA')->acknowledge();
```

## Check the installation

There is nothing to schedule: Sentinel registers its upkeep on the Laravel scheduler itself (run php artisan schedule:run every minute, as for any scheduled task). Until the migrations have run, the tasks skip instead of failing — and sentinel:check fails its schedule check, so it does not go unnoticed. Then run the health check — eleven checks, each ok, warning or failure:

```bash
php artisan sentinel:check            # exit 1 on a failure
php artisan sentinel:check --strict   # warnings fail too (CI)
```

## Recommended production settings

- Keep the default ring on the config driver — keys stay out of the database.
- Set SENTINEL\_CONTEXT to a value unique to the application when several apps share keys.
- Configure an anchor outside the application database: SENTINEL\_ANCHORS=cache with SENTINEL\_ANCHOR\_CACHE\_STORE pointing at a separate Redis, or filesystem on object storage with object lock.
- Verify-only servers: SENTINEL\_AUTO\_SEAL=false and only public key material.
- SENTINEL\_REQUIRE\_BOUND\_LABEL=true — right away on a fresh install, after sentinel:key:reseal on one upgraded from 1.1 (see Key management).

## Naming

laravel/sentinel (installed with Horizon, Pulse and Telescope) ships Laravel\\Sentinel\\Sentinel and Laravel\\Sentinel\\SentinelManager, and cartalyst/sentinel registers a global Sentinel alias. That is why this package registers no alias: import RoundlyConsulting\\Sentinel\\Facades\\Sentinel and RoundlyConsulting\\Sentinel\\SentinelManager explicitly — an IDE may auto-import the wrong one. php artisan about shows the manager’s full class name.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/sentinel-for-laravel.md)
