Artisan Commands
The starter includes several custom Artisan commands for managing your application’s database, configuration, and Northwestern integrations.
Database Commands
Section titled “Database Commands”db:rebuild
Section titled “db:rebuild”Completely rebuild the database by dropping all tables, running migrations, and executing all auto-discovered seeders.
php artisan db:rebuildWhat it does:
- Clears application cache, queue, and schedule cache
- Runs
migrate:fresh(drops all tables and re-runs migrations) - Executes all seeders via
db:seed - Generates the OAuth signing keys with
passport:keysifstorage/oauth-private.keydoesn’t exist - Seeds demo data via
DemoSeeder - 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
db:seed:list
Section titled “db:seed:list”Display all auto-discovered seeders with their dependencies and execution order.
php artisan db:seed:listOutput 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
db:snapshot:create {filename?}
Section titled “db:snapshot:create {filename?}”Create a snapshot of the current database state.
php artisan db:snapshot:create my-snapshot-nameWhat it does:
- Validates database schema matches current migrations (unless skipped)
- Dumps database to
database/snapshots/{name}.sql - 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
db:snapshot:restore {filename?}
Section titled “db:snapshot:restore {filename?}”Restore a previously created database snapshot.
php artisan db:snapshot:restore my-snapshot-nameWhat it does:
- Validates snapshot exists
- Checks schema compatibility (unless skipped)
- Creates a backup if requested
- Drops all current database tables
- 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
db:snapshot:list
Section titled “db:snapshot:list”List all available database snapshots, then offer to restore one. Restoring from here skips the schema check that db:snapshot:restore runs.
php artisan db:snapshot:listdb:snapshot:info {filename?}
Section titled “db:snapshot:info {filename?}”Display detailed information about a specific snapshot.
php artisan db:snapshot:info my-snapshot-namefilenameargumentName of the snapshot to inspect. If omitted, an interactive selector is shown.
What it does:
- Displays file information (path, size, modification date)
- Shows schema metadata (checksum, migration/seeder counts)
- 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
db:snapshot:delete {filename?}
Section titled “db:snapshot:delete {filename?}”Delete a database snapshot and its associated metadata.
php artisan db:snapshot:delete my-snapshot-nameWhat it does:
- Removes the SQL snapshot file from
database/snapshots/ - Cleans up associated metadata from the checksum map
When to use:
- Removing old or unused snapshots
- Freeing up disk space
- Cleaning up after testing
db:wake
Section titled “db:wake”Wake up a potentially-inactive serverless RDS database by establishing a connection.
php artisan db:wakeWhat 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
Configuration Commands
Section titled “Configuration Commands”config:validate
Section titled “config:validate”Validate application configuration and environment variables.
php artisan config:validateWhat 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
Adding Custom Validators
Section titled “Adding Custom Validators”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.
User and Role Commands
Section titled “User and Role Commands”role:force-detach {user} {role}
Section titled “role:force-detach {user} {role}”Remove a role from a user in an emergency, bypassing the role’s assignment lock.
php artisan role:force-detach jdoe "Super Administrator" --reason="Offboarding"userargumentUsername or ID of the user. If omitted, a search prompt is shown.
roleargumentName of the role to detach. If omitted, a search prompt is shown.
--reasonoptionReason for the detachment, recorded in the audit log. Prompted for if
omitted; required with --force.
--forceflagSkip the confirmation prompt
What it does:
- Finds the user and role, and fails if the user doesn’t have the role
- Shows the user, role, assignment lock and reason, and asks for confirmation
- Detaches the role with an audit record (origin
System) that includes the reason
Maintenance Commands
Section titled “Maintenance Commands”oauth-clients:notify-secret-expiration
Section titled “oauth-clients:notify-secret-expiration”Send reminder emails for service client secrets approaching their expiration date.
php artisan oauth-clients:notify-secret-expirationWhat it does:
- Queries for active service clients whose secrets expire soon
- Checks which are due for notifications (30, 14, 7, 3, 1 days before)
- Sends reminder emails to the API user’s contact address
See also: API Documentation
personal-access-tokens:notify-expiration
Section titled “personal-access-tokens:notify-expiration”Email people about their personal access tokens that are about to expire.
php artisan personal-access-tokens:notify-expirationIt 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
oauth:revoke-ineligible
Section titled “oauth:revoke-ineligible”Revoke the API credentials their holders can no longer use.
php artisan oauth:revoke-ineligibleIt 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.
mcp:prune-clients
Section titled “mcp:prune-clients”Clean up the clients MCP clients register for themselves.
php artisan mcp:prune-clientsIt 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
announcements:notify
Section titled “announcements:notify”Notify the audiences of announcements that have started, when their authors asked for it.
php artisan announcements:notifyPublishing 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
Passport Commands
Section titled “Passport Commands”The starter schedules or calls these Laravel Passport commands.
passport:keys
Section titled “passport:keys”Generate the RSA key pair Passport signs access tokens with.
php artisan passport:keysIt 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.
See also: API Operations
passport:purge
Section titled “passport:purge”Delete OAuth tokens and authorization codes that are no longer needed.
php artisan passport:purge --expired --hours=744The 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.
Health Check Commands
Section titled “Health Check Commands”health:check
Section titled “health:check”Run health checks for all external integrations and services.
php artisan health:checkWhat 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_KEYis 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:
curl -H "X-Secret-Token: your-secret-token" https://your-app.northwestern.edu/api/healthChangelog Commands
Section titled “Changelog Commands”make:changelog
Section titled “make:changelog”Scaffold a new changelog Markdown file in resources/changelogs/.
php artisan make:changelogIn interactive mode, the command prompts for a slug, date, and title with sensible defaults. Arguments and options can also be passed directly:
php artisan make:changelog 2026-03-01 --date=2026-03-01 --title="Search Improvements"If a file with the same slug already exists, a numeric suffix is appended automatically.
See also: Changelog documentation
Development Commands
Section titled “Development Commands”netid:update:test {netId} {action}
Section titled “netid:update:test {netId} {action}”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.
php artisan netid:update:test jdoe deactivateIDE Helper Commands
Section titled “IDE Helper Commands”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.
ide-helper:generate
Section titled “ide-helper:generate”Generate IDE helper file for Laravel facades.
php artisan ide-helper:generateide-helper:models
Section titled “ide-helper:models”Generate PHPDoc annotations for Eloquent models.
php artisan ide-helper:models -N-N (--nowrite) writes the annotations to _ide_helper_models.php instead of the model files.
ide-helper:meta
Section titled “ide-helper:meta”Generate PhpStorm meta file for container bindings.
php artisan ide-helper:metaScheduled Tasks
Section titled “Scheduled Tasks”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.
| Command | Frequency | Purpose |
|---|---|---|
telescope:prune | Daily | Deletes Telescope entries older than 24 hours |
livewire:configure-s3-upload-cleanup | Daily | Sets 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/*/Models | Daily | Prunes models in app/Domains/*/Models that use Prunable or MassPrunable, including those with PrunesAfterRetentionPeriod |
model:prune --model=HealthCheckResultHistoryItem | Daily | Deletes health check results older than keep_history_for_days (5) |
oauth-clients:notify-secret-expiration | Daily at 09:00 | Sends client secret expiration reminders, when api.client_secret_expiration_notifications.enabled is true |
personal-access-tokens:notify-expiration | Daily at 09:00 | Emails 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:notify | Every five minutes in production, weekdays at noon elsewhere | Notifies the audiences of announcements that have started, when their authors asked; publishing notifies at once for one that starts straight away |
oauth:revoke-ineligible | Hourly in production, weekdays at noon elsewhere | Revokes the credentials their holders can no longer use: deleted and deactivated accounts, and people who lost CreatePersonalAccessTokens or UseMcp |
mcp:prune-clients | Daily | Deletes self-registered MCP clients nobody connected within a day, and revokes those nobody has used for 90 days |
passport:purge --expired --hours=744 | Daily | Deletes 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-heartbeat | Every minute | Records the heartbeat the Schedule health check reads, in every environment |
health:queue-check-heartbeat | Every five minutes, production only | Dispatches the job the Queue health check waits for |
health:check | Every minute, production only | Runs and stores the health checks |