Configuration#
Signal's configuration file provides complete control over how your application consumes AT Protocol events.
Configuration File#
After installation, configuration lives in config/signal.php.
Publishing Configuration#
Publish the config file manually if needed:
php artisan vendor:publish --tag=signal-config
This creates config/signal.php with all available options.
Environment Variables#
Most configuration can be set via .env for environment-specific values.
Basic Configuration#
# Consumer Mode (jetstream or firehose)
SIGNAL_MODE=jetstream
# Jetstream Configuration
SIGNAL_JETSTREAM_URL=wss://jetstream2.us-east.bsky.network
# Firehose Configuration
SIGNAL_FIREHOSE_HOST=bsky.network
# Cursor Storage (database, redis, or file)
SIGNAL_CURSOR_STORAGE=database
# Queue Configuration
SIGNAL_QUEUE_CONNECTION=redis
SIGNAL_QUEUE=signal
Consumer Mode#
Choose between Jetstream and Firehose mode.
Configuration#
'mode' => env('SIGNAL_MODE', 'jetstream'),
Options:
jetstream- JSON events, server-side filtering (default)firehose- CBOR/CAR events, client-side filtering
Environment Variable:
SIGNAL_MODE=jetstream
When to use each:
- Jetstream: Standard Bluesky collections, production efficiency
- Firehose: Custom collections, AppViews, comprehensive indexing
Jetstream Configuration#
Configuration specific to Jetstream mode.
WebSocket URL#
'jetstream' => [
'websocket_url' => env(
'SIGNAL_JETSTREAM_URL',
'wss://jetstream2.us-east.bsky.network'
),
],
Available Endpoints:
US East (Default):
SIGNAL_JETSTREAM_URL=wss://jetstream2.us-east.bsky.network
US West:
SIGNAL_JETSTREAM_URL=wss://jetstream1.us-west.bsky.network
Choose the endpoint closest to your server for best latency.
Firehose Configuration#
Configuration specific to Firehose mode.
Host#
'firehose' => [
'host' => env('SIGNAL_FIREHOSE_HOST', 'bsky.network'),
],
The WebSocket URL is constructed as:
wss://{host}/xrpc/com.atproto.sync.subscribeRepos
Environment Variable:
SIGNAL_FIREHOSE_HOST=bsky.network
Default Host: bsky.network
Custom Hosts: If you're running your own AT Protocol PDS, specify it here:
SIGNAL_FIREHOSE_HOST=my-pds.example.com
Cursor Storage#
Configure how Signal stores cursor positions for resuming after disconnections.
Storage Driver#
'cursor_storage' => env('SIGNAL_CURSOR_STORAGE', 'database'),
Available Drivers:
database- Store in database (recommended for production)redis- Store in Redis (high performance)file- Store in filesystem (development only)
Environment Variable:
SIGNAL_CURSOR_STORAGE=database
Database Driver#
Uses Laravel's default database connection.
Configuration:
'cursor_storage' => 'database',
Requires:
- Migration published and run
- Database connection configured
Table: signal_cursors
Redis Driver#
Stores cursors in Redis for high performance.
Configuration:
'cursor_storage' => 'redis',
'redis' => [
'connection' => env('SIGNAL_REDIS_CONNECTION', 'default'),
'key_prefix' => env('SIGNAL_REDIS_PREFIX', 'signal:cursor:'),
],
Environment Variables:
SIGNAL_CURSOR_STORAGE=redis
SIGNAL_REDIS_CONNECTION=default
SIGNAL_REDIS_PREFIX=signal:cursor:
Requires:
- Redis connection configured in
config/database.php - Redis server running
File Driver#
Stores cursors in the filesystem (development only).
Configuration:
'cursor_storage' => 'file',
'file' => [
'path' => env('SIGNAL_FILE_PATH', storage_path('app/signal')),
],
Environment Variables:
SIGNAL_CURSOR_STORAGE=file
SIGNAL_FILE_PATH=/path/to/storage/signal
Not recommended for production:
- Single server only
- No clustering support
- Filesystem I/O overhead
Queue Configuration#
Configure how Signal dispatches queued jobs.
Queue Connection#
'queue' => [
'connection' => env('SIGNAL_QUEUE_CONNECTION', null),
'queue' => env('SIGNAL_QUEUE', 'default'),
],
Environment Variables:
# Queue connection (redis, database, sqs, etc.)
SIGNAL_QUEUE_CONNECTION=redis
# Queue name
SIGNAL_QUEUE=signal
Defaults:
connection: Uses Laravel's default queue connectionqueue: Uses Laravel's default queue name
Per-Signal Configuration#
Signals can override queue configuration:
public function shouldQueue(): bool
{
return true;
}
public function queueConnection(): string
{
return 'redis'; // Override connection
}
public function queue(): string
{
return 'high-priority'; // Override queue name
}
Learn more about queue integration →
Auto-Discovery#
Configure automatic Signal discovery.
Enable/Disable#
'auto_discovery' => [
'enabled' => true,
'path' => app_path('Signals'),
'namespace' => 'App\\Signals',
],
Options:
enabled: Enable/disable auto-discovery (default:true)path: Directory to scan for Signals (default:app/Signals)namespace: Namespace for discovered Signals (default:App\Signals)
Disable Auto-Discovery#
Manually register Signals instead:
'auto_discovery' => [
'enabled' => false,
],
'signals' => [
\App\Signals\NewPostSignal::class,
\App\Signals\NewFollowSignal::class,
],
Custom Discovery Path#
Organize Signals in a custom directory:
'auto_discovery' => [
'enabled' => true,
'path' => app_path('Domain/Signals'),
'namespace' => 'App\\Domain\\Signals',
],
Manual Signal Registration#
Register Signals explicitly.
Configuration#
'signals' => [
\App\Signals\NewPostSignal::class,
\App\Signals\NewFollowSignal::class,
\App\Signals\ProfileUpdateSignal::class,
],
When to use:
- Auto-discovery disabled
- Signals outside standard directory
- Fine-grained control over which Signals run
Logging#
Signal uses Laravel's logging system.
Configure Logging#
Standard Laravel log configuration applies:
// config/logging.php
'channels' => [
'signal' => [
'driver' => 'daily',
'path' => storage_path('logs/signal.log'),
'level' => env('SIGNAL_LOG_LEVEL', 'debug'),
'days' => 14,
],
],
Use in Signals:
use Illuminate\Support\Facades\Log;
public function handle(SignalEvent $event): void
{
Log::channel('signal')->info('Processing event', [
'did' => $event->did,
]);
}
Complete Configuration Reference#
Here's the full config/signal.php with all options:
<?php
return [
/*
|--------------------------------------------------------------------------
| Consumer Mode
|--------------------------------------------------------------------------
|
| Choose between 'jetstream' (JSON events) or 'firehose' (CBOR/CAR events).
| Jetstream is more efficient for standard Bluesky collections.
| Firehose is required for custom collections.
|
| Options: 'jetstream', 'firehose'
|
*/
'mode' => env('SIGNAL_MODE', 'jetstream'),
/*
|--------------------------------------------------------------------------
| Jetstream Configuration
|--------------------------------------------------------------------------
|
| Configuration for Jetstream mode (JSON events).
|
*/
'jetstream' => [
'websocket_url' => env(
'SIGNAL_JETSTREAM_URL',
'wss://jetstream2.us-east.bsky.network'
),
],
/*
|--------------------------------------------------------------------------
| Firehose Configuration
|--------------------------------------------------------------------------
|
| Configuration for Firehose mode (CBOR/CAR events).
|
*/
'firehose' => [
'host' => env('SIGNAL_FIREHOSE_HOST', 'bsky.network'),
],
/*
|--------------------------------------------------------------------------
| Cursor Storage
|--------------------------------------------------------------------------
|
| Configure how Signal stores cursor positions for resuming after
| disconnections. Options: 'database', 'redis', 'file'
|
*/
'cursor_storage' => env('SIGNAL_CURSOR_STORAGE', 'database'),
/*
|--------------------------------------------------------------------------
| Redis Configuration
|--------------------------------------------------------------------------
|
| Configuration for Redis cursor storage.
|
*/
'redis' => [
'connection' => env('SIGNAL_REDIS_CONNECTION', 'default'),
'key_prefix' => env('SIGNAL_REDIS_PREFIX', 'signal:cursor:'),
],
/*
|--------------------------------------------------------------------------
| File Configuration
|--------------------------------------------------------------------------
|
| Configuration for file-based cursor storage.
|
*/
'file' => [
'path' => env('SIGNAL_FILE_PATH', storage_path('app/signal')),
],
/*
|--------------------------------------------------------------------------
| Queue Configuration
|--------------------------------------------------------------------------
|
| Configure queue connection and name for processing events asynchronously.
|
*/
'queue' => [
'connection' => env('SIGNAL_QUEUE_CONNECTION', null),
'queue' => env('SIGNAL_QUEUE', 'default'),
],
/*
|--------------------------------------------------------------------------
| Auto-Discovery
|--------------------------------------------------------------------------
|
| Automatically discover and register Signals from the specified directory.
|
*/
'auto_discovery' => [
'enabled' => true,
'path' => app_path('Signals'),
'namespace' => 'App\\Signals',
],
/*
|--------------------------------------------------------------------------
| Manual Signal Registration
|--------------------------------------------------------------------------
|
| Manually register Signals if auto-discovery is disabled.
|
*/
'signals' => [
// \App\Signals\NewPostSignal::class,
],
];
Environment-Specific Configuration#
Development#
SIGNAL_MODE=firehose
SIGNAL_CURSOR_STORAGE=file
SIGNAL_QUEUE_CONNECTION=sync
Why:
- Firehose mode sees all events (comprehensive testing)
- File storage is simple and adequate
- Sync queue processes immediately (easier debugging)
Staging#
SIGNAL_MODE=jetstream
SIGNAL_CURSOR_STORAGE=redis
SIGNAL_QUEUE_CONNECTION=redis
SIGNAL_QUEUE=signal-staging
Why:
- Jetstream mode matches production
- Redis for performance testing
- Separate queue for staging isolation
Production#
SIGNAL_MODE=jetstream
SIGNAL_CURSOR_STORAGE=database
SIGNAL_QUEUE_CONNECTION=redis
SIGNAL_QUEUE=signal
Why:
- Jetstream mode for efficiency
- Database storage for reliability
- Redis queues for performance
Runtime Configuration#
Change configuration at runtime:
use SocialDept\AtpSignals\Facades\Signal;
// Override mode
config(['signal.mode' => 'firehose']);
// Override cursor storage
config(['signal.cursor_storage' => 'redis']);
// Start consumer with new config
Signal::start();
Validation#
Signal validates configuration on startup:
php artisan signal:consume
Checks:
- Mode is valid (
jetstreamorfirehose) - Cursor storage driver exists
- Required endpoints are configured
- Queue configuration is valid
Validation errors prevent consumer from starting.
Configuration Helpers#
Check Current Mode#
$mode = config('signal.mode'); // 'jetstream' or 'firehose'
Or via Facade:
use SocialDept\AtpSignals\Facades\Signal;
$mode = Signal::getMode();
Check Cursor Storage#
$storage = config('signal.cursor_storage'); // 'database', 'redis', or 'file'
Check Queue Configuration#
$connection = config('signal.queue.connection');
$queue = config('signal.queue.queue');
Best Practices#
Use Environment Variables#
Don't hardcode values in config file:
// Good
'mode' => env('SIGNAL_MODE', 'jetstream'),
// Bad
'mode' => 'jetstream',
Separate Staging and Production#
Use different queues and storage:
# .env.staging
SIGNAL_QUEUE=signal-staging
# .env.production
SIGNAL_QUEUE=signal-production
Document Custom Configuration#
If you change defaults, document why:
// We use Firehose mode because we have custom collections
'mode' => env('SIGNAL_MODE', 'firehose'),
Version Control#
Commit config/signal.php but not .env:
# .gitignore
.env
.env.*
# Commit
config/signal.php
Troubleshooting#
Configuration Not Loading#
Clear config cache:
php artisan config:clear
php artisan config:cache
Environment Variables Not Working#
Check .env file exists and is readable:
ls -la .env
Restart services after changing .env:
# If using Supervisor
sudo supervisorctl restart signal-consumer:*
Invalid Configuration#
Run consumer to see validation errors:
php artisan signal:consume
Signal will display specific errors about misconfiguration.
Next Steps#
- Learn about testing → - Test your configuration
- See real-world examples → - Learn from production configurations
- Review queue integration → - Configure queues optimally