Skip to content

Artisan Commands

The starter includes several custom Artisan commands for managing your application’s database, configuration, and Northwestern integrations.

Completely rebuild the database by dropping all tables, running migrations, and executing all auto-discovered seeders.

Terminal window
php artisan db:rebuild

What it does:

  1. Clears application cache, queue, and schedule cache
  2. Runs migrate:fresh (drops all tables and re-runs migrations)
  3. Executes all seeders via db:seed
  4. Generates the OAuth signing keys with passport:keys if storage/oauth-private.key doesn’t exist
  5. Seeds demo data via DemoSeeder
  6. Regenerates IDE model helper annotations with ide-helper:models -N

Afterwards it warns if jobs are waiting in the queue, and, locally with the API on, prints the demo API user’s service client ID and secret to try the API with.

When to use:

  • Initial project setup
  • After pulling schema changes from git
  • When switching between feature branches with different schemas
  • Resetting test data during development

Related: db:wipe, migrate:fresh, db:seed


Display all auto-discovered seeders with their dependencies and execution order.

Terminal window
php artisan db:seed:list
Options
#--show-dependenciesflag

Show full dependency tree

#--mermaidflag

Output as Mermaid diagram

#--jsonflag

Output as JSON

Output example:

┌───┬──────────────────┬──────────────────────────────────┐
│ # │ Seeder │ Dependencies │
├───┼──────────────────┼──────────────────────────────────┤
│ 1 │ PermissionSeeder │ none │
│ 2 │ RoleTypeSeeder │ none │
│ 3 │ RoleSeeder │ RoleTypeSeeder, PermissionSeeder │
└───┴──────────────────┴──────────────────────────────────┘

When to use:

  • Verify seeder discovery and dependencies
  • Troubleshoot seeding order issues
  • Document available seeders for your team

See also: Idempotent Seeding documentation


Create a snapshot of the current database state.

Terminal window
php artisan db:snapshot:create my-snapshot-name
Options
#filenameargument

Descriptive name for the snapshot. Defaults to database-dump if omitted.

#--skip-schema-validationflag

Skip schema validation checks

What it does:

  1. Validates database schema matches current migrations (unless skipped)
  2. Dumps database to database/snapshots/{name}.sql
  3. Stores metadata about the snapshot (checksum, timestamp, migration/seeder counts)

When to use:

  • Before making risky database changes
  • Saving a known-good state for testing
  • Creating test data fixtures
  • Preserving demo data

See also: Database Snapshots documentation


Restore a previously created database snapshot.

Terminal window
php artisan db:snapshot:restore my-snapshot-name
Options
#filenameargument

Name of the snapshot to restore. Defaults to database-dump if omitted.

#--skip-schema-validationflag

Skip schema validation checks

#--backupflag

Create a backup of the current database before restoring

#--forceflag

Skip confirmation prompt

What it does:

  1. Validates snapshot exists
  2. Checks schema compatibility (unless skipped)
  3. Creates a backup if requested
  4. Drops all current database tables
  5. Restores data from snapshot SQL file

When to use:

  • Resetting to a known state during testing
  • Recovering from bad data changes during development
  • Switching between test scenarios

List all available database snapshots, then offer to restore one. Restoring from here skips the schema check that db:snapshot:restore runs.

Terminal window
php artisan db:snapshot:list

Display detailed information about a specific snapshot.

Terminal window
php artisan db:snapshot:info my-snapshot-name
Options
#filenameargument

Name of the snapshot to inspect. If omitted, an interactive selector is shown.

What it does:

  1. Displays file information (path, size, modification date)
  2. Shows schema metadata (checksum, migration/seeder counts)
  3. Compares snapshot schema with current codebase

When to use:

  • Verifying a snapshot’s contents before restoring
  • Checking if a snapshot is compatible with current code
  • Reviewing snapshot metadata

Delete a database snapshot and its associated metadata.

Terminal window
php artisan db:snapshot:delete my-snapshot-name
Options
#filenameargument

Name of the snapshot to delete. If omitted, an interactive selector is shown.

#--allflag

Delete all snapshots

What it does:

  1. Removes the SQL snapshot file from database/snapshots/
  2. Cleans up associated metadata from the checksum map

When to use:

  • Removing old or unused snapshots
  • Freeing up disk space
  • Cleaning up after testing

Wake up a potentially-inactive serverless RDS database by establishing a connection.

Terminal window
php artisan db:wake
Options
#--max-attemptsoption5

Maximum number of connection attempts (1-20)

#--delayoption5

Seconds to wait between attempts (1-30)

What it does:

Attempts to connect to the database and execute a simple query, causing serverless databases (like AWS Aurora Serverless) to wake from sleep mode.

When to use:

  • Before running migrations
  • As part of deployment scripts
  • When you know the database has been idle

Validate application configuration and environment variables.

Terminal window
php artisan config:validate

What it does:

Runs all auto-discovered config validators and reports their status. Built-in validators check:

  • Application key
  • SSO credentials (Entra ID or Online Passport)
  • Directory Search API key
  • Database connectivity
  • Queue connection
  • S3 storage access
  • EventHub credentials (when not mocked)

Validators that aren’t relevant to the current configuration (e.g., EventHub when mocked) are automatically skipped and shown separately in the output.

When to use:

  • After initial installation
  • After environment variable changes
  • Before deployment
  • Troubleshooting integration issues

Validators are discovered automatically using chassis’s #[ValidatesConfig] attribute, following the same pattern as #[AutoSeed] for seeders.

1. Create a validator class implementing ConfigValidator in a Services/ConfigValidation directory within your domain:

<?php
namespace App\Domains\Billing\Services\ConfigValidation;
use Northwestern\SysDev\Chassis\Attributes\ValidatesConfig;
use Northwestern\SysDev\Chassis\Contracts\ConfigValidator;
#[ValidatesConfig(description: 'Stripe')]
class StripeValidator implements ConfigValidator
{
public function shouldRun(): bool
{
// Return false to skip when the integration isn't configured
return filled(config('services.stripe.secret'));
}
public function validate(): bool
{
return filled(config('services.stripe.secret'))
&& filled(config('services.stripe.key'));
}
public function successMessage(): string
{
return 'Stripe API keys are configured';
}
public function errorMessage(): string
{
return 'Stripe configuration is incomplete';
}
public function hints(): array
{
return [
'Set <comment>STRIPE_KEY</comment> and <comment>STRIPE_SECRET</comment> in your .env file',
];
}
}

2. That’s it. The validator is automatically discovered from any app/Domains/*/Services/ConfigValidation directory. No registration or configuration needed.


Remove a role from a user in an emergency, bypassing the role’s assignment lock.

Terminal window
php artisan role:force-detach jdoe "Super Administrator" --reason="Offboarding"
Options
#userargument

Username or ID of the user. If omitted, a search prompt is shown.

#roleargument

Name of the role to detach. If omitted, a search prompt is shown.

#--reasonoption

Reason for the detachment, recorded in the audit log. Prompted for if omitted; required with --force.

#--forceflag

Skip the confirmation prompt

What it does:

  1. Finds the user and role, and fails if the user doesn’t have the role
  2. Shows the user, role, assignment lock and reason, and asks for confirmation
  3. Detaches the role with an audit record (origin System) that includes the reason

Send reminder emails for service client secrets approaching their expiration date.

Terminal window
php artisan oauth-clients:notify-secret-expiration

What it does:

  1. Queries for active service clients whose secrets expire soon
  2. Checks which are due for notifications (30, 14, 7, 3, 1 days before)
  3. Sends reminder emails to the API user’s contact address

See also: API Documentation


Email people about their personal access tokens that are about to expire.

Terminal window
php artisan personal-access-tokens:notify-expiration

It emails the owner of each active token expiring in 30, 14, 7, 3 or 1 days (api.personal_access_tokens.expiration_notifications.intervals), unless they turned the reminders off on their Preferences page, and at most once a day per token. It does nothing while API_PERSONAL_ACCESS_TOKEN_EXPIRATION_NOTIFICATIONS_ENABLED is false.

See also: Personal Access Tokens


Revoke the API credentials their holders can no longer use.

Terminal window
php artisan oauth:revoke-ineligible

It revokes each live personal access token, connection and service client that CredentialAccess refuses for a lasting reason: a deactivated or deleted account, or a person who lost CreatePersonalAccessTokens or UseMcp. A feature that is turned off isn’t one. The API already refuses these on every request; this makes the records say so, so the Account and Administration pages show them as revoked. Each goes through its action, so each is audited.


Clean up the clients MCP clients register for themselves.

Terminal window
php artisan mcp:prune-clients

It deletes a self-registered client nobody connected within mcp.cleanup.unconnected_hours (24) of registering, and revokes one nobody has used for mcp.cleanup.unused_days (90). Administrator-registered applications and service clients are never touched.

See also: MCP


Notify the audiences of announcements that have started, when their authors asked for it.

Terminal window
php artisan announcements:notify

Publishing notifies at once for an announcement that starts straight away; this catches the ones scheduled to start later. Outside production it’s scheduled only at noon on weekdays, so run it by hand to send one sooner.

See also: Announcements


The starter schedules or calls these Laravel Passport commands.

Generate the RSA key pair Passport signs access tokens with.

Terminal window
php artisan passport:keys

It writes storage/oauth-private.key and storage/oauth-public.key. db:rebuild runs it locally; deployed environments put the contents in PASSPORT_PRIVATE_KEY and PASSPORT_PUBLIC_KEY instead. Replacing the keys invalidates every outstanding access token.

Options
#--forceflag

Overwrite keys that already exist

#--lengthoption

Length of the private key (4096)

See also: API Operations


Delete OAuth tokens and authorization codes that are no longer needed.

Terminal window
php artisan passport:purge --expired --hours=744

The starter runs it daily with --expired --hours=744, so tokens and codes go once they expired more than 31 days ago, revoked or not. Without --expired, Passport also deletes everything revoked at once. Don’t run it that way: the starter finds a refresh token through its access token when access is revoked, so an access token has to outlive its refresh token’s 30-day lifetime.


Run health checks for all external integrations and services.

Terminal window
php artisan health:check

What it does:

Validates connectivity and configuration for:

  • Database - Connection and query execution (production only)
  • Queue - Fails when the heartbeat job hasn’t been processed within 15 minutes (production only)
  • Cache - Cache store read/write
  • Schedule - Scheduler heartbeat
  • Debug Mode - Ensures debug mode is off (non-local)
  • Optimized App - Ensures app is optimized (non-local)
  • Security Advisories - Checks for known package vulnerabilities
  • Redis - Redis connectivity (when Redis driver is in use)
  • Directory Search - API connectivity and test query (skipped locally when DIRECTORY_SEARCH_API_KEY is blank)

When to use:

  • After deployment
  • Troubleshooting integration issues
  • Monitoring service connectivity
  • As part of CI/CD pipelines

Integration:

The /api/health endpoint also runs these checks on every request for automated monitoring. It refuses every request with 403 until HEALTH_SECRET_TOKEN is set:

Terminal window
curl -H "X-Secret-Token: your-secret-token" https://your-app.northwestern.edu/api/health

Scaffold a new changelog Markdown file in resources/changelogs/.

Terminal window
php artisan make:changelog

In interactive mode, the command prompts for a slug, date, and title with sensible defaults. Arguments and options can also be passed directly:

Terminal window
php artisan make:changelog 2026-03-01 --date=2026-03-01 --title="Search Improvements"
Options
#slugargument

URL-safe identifier. Defaults to today’s date.

#--dateoption

Authored date in YYYY-MM-DD format. Defaults to today.

#--titleoption

Human-readable title. Defaults to “Month Year Release” when the slug is a date.

If a file with the same slug already exists, a numeric suffix is appended automatically.

See also: Changelog documentation


Simulate a message from the EventHub NetID Updates topic to test the NetID update webhook.

By default it posts the message straight to the webhook, so the netid-update route in routes/api.php, which ships commented out, must be enabled first. With --via-queue it sends the message through the real EventHub API instead, so it needs EventHub’s credentials.

Terminal window
php artisan netid:update:test jdoe deactivate
Options
#netIdargument

NetID to include in the message. Prompted for if omitted.

#actionargument

Update action: deactivate, deprovision or sechold. A selector is shown if omitted.

#--via-queueflag

Send through the EventHub queue instead of directly to the HTTP webhook


The starter uses laravel-ide-helper to generate IDE autocomplete files. All three run from Composer’s post-update-cmd script and from composer ide-helper:generate. db:rebuild runs only ide-helper:models -N.

Generate IDE helper file for Laravel facades.

Terminal window
php artisan ide-helper:generate

Generate PHPDoc annotations for Eloquent models.

Terminal window
php artisan ide-helper:models -N

-N (--nowrite) writes the annotations to _ide_helper_models.php instead of the model files.

Generate PhpStorm meta file for container bindings.

Terminal window
php artisan ide-helper:meta

routes/console.php schedules these commands. Times are Chicago time (schedule_timezone in config/app.php). The scheduler must be running for any of them to run: deployments run php artisan schedule:run every minute, and locally composer dev doesn’t start it, so run php artisan schedule:work, or run a command by hand.

CommandFrequencyPurpose
telescope:pruneDailyDeletes Telescope entries older than 24 hours
livewire:configure-s3-upload-cleanupDailySets the S3 lifecycle rule that expires Livewire’s temporary uploads after 24 hours (only when uploads use an S3 disk)
model:prune --path=app/Domains/*/ModelsDailyPrunes models in app/Domains/*/Models that use Prunable or MassPrunable, including those with PrunesAfterRetentionPeriod
model:prune --model=HealthCheckResultHistoryItemDailyDeletes health check results older than keep_history_for_days (5)
oauth-clients:notify-secret-expirationDaily at 09:00Sends client secret expiration reminders, when api.client_secret_expiration_notifications.enabled is true
personal-access-tokens:notify-expirationDaily at 09:00Emails people before their personal access tokens expire, unless they turned it off in their preferences, when api.personal_access_tokens.expiration_notifications.enabled is true
announcements:notifyEvery five minutes in production, weekdays at noon elsewhereNotifies the audiences of announcements that have started, when their authors asked; publishing notifies at once for one that starts straight away
oauth:revoke-ineligibleHourly in production, weekdays at noon elsewhereRevokes the credentials their holders can no longer use: deleted and deactivated accounts, and people who lost CreatePersonalAccessTokens or UseMcp
mcp:prune-clientsDailyDeletes self-registered MCP clients nobody connected within a day, and revokes those nobody has used for 90 days
passport:purge --expired --hours=744DailyDeletes OAuth tokens and codes, revoked or not, once they expired more than 31 days ago: longer than the 30-day refresh token lifetime
health:schedule-check-heartbeatEvery minuteRecords the heartbeat the Schedule health check reads, in every environment
health:queue-check-heartbeatEvery five minutes, production onlyDispatches the job the Queue health check waits for
health:checkEvery minute, production onlyRuns and stores the health checks