jeffersongoncalves / laravel-satis
A Laravel package for managing private Composer repositories with Satis
Package info
github.com/jeffersongoncalves/laravel-satis
pkg:composer/jeffersongoncalves/laravel-satis
Requires
- php: ^8.2
- composer/satis: 3.x-dev
- spatie/laravel-package-tools: ^1.15
Requires (Dev)
- larastan/larastan: ^2.9|^3.0
- laravel/pint: ^1.0
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.1
- v2.0.0
- 1.17.2
- 1.17.1
- 1.17.0
- 1.16.0
- 1.15.0
- 1.14.8
- 1.14.7
- 1.14.6
- 1.14.5
- 1.14.4
- 1.14.3
- 1.14.2
- 1.14.1
- 1.14.0
- 1.13.1
- 1.13.0
- 1.12.0
- 1.11.1
- 1.11.0
- 1.10.1
- 1.10.0
- 1.9.2
- 1.9.1
- 1.9.0
- 1.8.2
- 1.8.1
- 1.8.0
- 1.7.0
- 1.6.0
- 1.5.2
- 1.5.1
- 1.5.0
- 1.4.4
- 1.4.3
- 1.4.2
- 1.4.1
- 1.4.0
- 1.3.5
- 1.3.4
- 1.3.3
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.0
- 1.1.0
- 1.0.1
- 1.0.0
- dev-chore/github-sponsors
- dev-docs/standardize-readme-structure
- dev-chore/buymeacoffee-funding
- dev-fix/pin-actions-to-sha
This package is auto-updated.
Last update: 2026-09-06 00:31:34 UTC
README
Laravel Satis
A Laravel package for managing private Composer repositories powered by Satis.
Features
- Credential Management — Dedicated Credential model for centralized authentication (URL, email, password)
- Package Management — Add and manage Composer & GitHub package sources linked to credentials
- Token-Based Auth — Secure access with per-token package scoping
- Automated Builds — Queue-driven Satis builds with configurable scheduling
- Credential Grouping — Separate builds per credential with snapshot merging
- Inline Auth URLs — RFC 3986 percent-encoded credentials in repository URLs
- Rate-Limit Retry — Exponential backoff on HTTP 429 responses during builds
- GitHub Webhooks — Auto-rebuild on push, release and create events with signature verification
- Download Tracking — Per-version download statistics
- Dependency Tracking — Public/private dependency classification with automatic processing
- Multi-Tenancy — Tenant-isolated data with configurable resolver
- Credential Validation — Verify package and credential accessibility before building
- Intelligent Validation — Timestamp-based comparison to skip unnecessary rebuilds
- Credential Sanitization — Remove transport-options and inline credentials from Satis JSON files
- Dev Packages — Mark packages as development-only with
is_devflag - Composer V2 Protocol — Full support for
packages.json,p2/and include files
Requirements
- PHP 8.2+
- Laravel 11, 12 or 13
- Satis (
composer/satis— included as dependency)
Note on stability: this package depends on
composer/satisat version3.x-dev, which does not yet have a stable release. For this reason, when installing the package in your application, Composer will require your consuming project to allow development packages. Configure this in your application'scomposer.json:{ "minimum-stability": "dev", "prefer-stable": true }
Installation
composer require jeffersongoncalves/laravel-satis
Publish and run migrations:
php artisan vendor:publish --tag="satis-migrations"
php artisan migrate
Publish the config (optional):
php artisan vendor:publish --tag="satis-config"
Configuration
The config file (config/satis.php) covers:
Multi-Tenancy
'tenancy' => [ 'enabled' => false, 'model' => null, 'foreign_key' => null, 'ownership_relationship' => null, 'resolver' => null, // callable that returns the current tenant ID ],
The resolver accepts any callable that returns the current tenant ID. Example:
// In a service provider or middleware config(['satis.tenancy.enabled' => true]); config(['satis.tenancy.model' => \App\Models\Team::class]); config(['satis.tenancy.foreign_key' => 'team_id']); config(['satis.tenancy.resolver' => fn () => auth()->user()?->current_team_id]);
Table Prefix
'table_prefix' => 'satis_',
Set to null to use table names without a prefix.
Custom Models
Override any model to extend the default behavior:
'models' => [ 'credential' => \App\Models\SatisCredential::class, 'package' => \App\Models\SatisPackage::class, 'token' => \App\Models\SatisToken::class, // ... ],
Storage
'storage_disk' => 'local', 'storage_path' => 'satis',
Queue
'queue' => [ 'connection' => null, // null = default connection 'queue_name' => null, // null = default queue 'timeout' => 86400, // 24 hours (in seconds) ],
Scheduling
'schedule' => [ 'build' => 'weekly', // any Laravel Schedule method or null 'token_build' => 'weekly', 'validate' => 'hourly', 'sanitize' => 'daily', 'dependencies' => 'weekly', ],
Routes
'routes' => [ 'api_prefix' => 'api/satis', 'composer_prefix' => 'satis', 'middleware' => ['api'], ],
Usage
Managing Credentials and Packages Programmatically
use JeffersonGoncalves\LaravelSatis\Support\ModelResolver; // Create a credential $credentialModel = ModelResolver::credential(); $credential = $credentialModel::create([ 'name' => 'My Private Repo', 'url' => 'https://repo.example.com', 'email' => 'user', 'password' => 'secret', ]); // Create a package using the credential $packageModel = ModelResolver::package(); $package = $packageModel::create([ 'name' => 'vendor/package-name', 'type' => 'composer', 'credential_id' => $credential->id, ]); // Create a GitHub credential and package $githubCredential = $credentialModel::create([ 'name' => 'GitHub', 'url' => 'https://github.com/vendor/repo.git', 'email' => 'github-user', 'password' => 'github-token', ]); $githubPackage = $packageModel::create([ 'name' => 'vendor/github-package', 'type' => 'github', 'credential_id' => $githubCredential->id, ]); // Create a dev package (reusing same credential) $devPackage = $packageModel::create([ 'name' => 'vendor/dev-tool', 'type' => 'composer', 'credential_id' => $credential->id, 'is_dev' => true, ]); // Validate a credential $result = app(\JeffersonGoncalves\LaravelSatis\Actions\ValidateCredential::class) ->execute($credential); // $result = ['success' => true, 'message' => 'Credential validated successfully.'] // Create a token $tokenModel = ModelResolver::token(); $token = $tokenModel::create([ 'name' => 'My Token', 'email' => 'user@example.com', ]); // Assign packages to token $token->packages()->attach($package->id);
Running Builds
# Build all packages (tenant-based) php artisan satis:build # Build for a specific tenant php artisan satis:build --tenant=1 # Build per token (all tokens with packages) php artisan satis:token-build # Build for a specific token php artisan satis:token-build --token=5 # Validate credentials and trigger rebuilds if needed php artisan satis:validate # Process dependencies php artisan dependency:packages # Remove credentials from Satis JSON files php artisan satis:sanitize # Clean all Satis builds from storage php artisan satis:clean # Force clean without confirmation php artisan satis:clean --force
Composer Client Configuration
After building, clients can use your private repository:
{
"repositories": [
{
"type": "composer",
"url": "https://your-app.com/satis"
}
]
}
Authenticate using the token as a password with any username:
composer config http-basic.your-app.com/satis "any-username" "your-token-here"
GitHub Webhooks
Each package gets a unique reference for webhook URLs:
POST /api/satis/webhooks/github/{package-reference}
Set the Content type to application/json and optionally configure a Secret using the package's webhook_secret.
Supported events: push, release, create — all other events are ignored with HTTP 200.
The webhook handler:
- Validates the package is a GitHub type (returns 400 otherwise)
- Filters supported events
- Verifies HMAC-SHA256 signature when a secret is configured
- Dispatches
SyncTenantPackagesfor the tenant rebuild - Dispatches
SyncTokenPackagesfor each token associated with the package
API Endpoints
Composer Protocol (requires token auth)
| Method | Endpoint | Description |
|---|---|---|
GET |
/satis/packages.json |
Root packages file |
GET |
/satis/include/{include}.json |
Include files |
GET |
/satis/p2/{vendor}/{package}.json |
V2 protocol metadata |
GET |
/satis/archives/{vendor}/{package}/{file} |
Package archives |
API
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/satis/composer/downloads |
Download notifications |
POST |
/api/satis/webhooks/github/{reference} |
GitHub webhook |
Commands
| Command | Description |
|---|---|
satis:build |
Build Satis repository (tenant-based) |
satis:token-build |
Build Satis repository (token-based) |
satis:validate |
Validate package credentials and trigger rebuilds if needed |
satis:clean |
Clean all Satis builds from storage |
satis:sanitize |
Remove credentials from Satis JSON files |
dependency:packages |
Process and sync package dependencies |
Upgrading from v1.x to v2.0
Breaking Changes
-
Credential model: Credentials are now stored in a dedicated
credentialstable instead of directly on thepackagestable. -
Package model: The
url,username, andpasswordcolumns have been removed. Packages now reference acredential_idforeign key (required). -
CreateAuthJson removed: Authentication is now handled via inline auth URLs (RFC 3986) instead of a separate auth.json file.
-
Code lengths:
webhook_secretchanged from 40 to 64 characters,referencefrom 20 to 32 characters.
Migration Steps
- Update your dependency:
composer require jeffersongoncalves/laravel-satis:^2.0
- Publish and run the new migrations:
php artisan vendor:publish --tag="satis-migrations"
- Before running migrations, migrate existing data to the credentials table:
use JeffersonGoncalves\LaravelSatis\Models\Credential; $packages = DB::table('satis_packages')->get(); $credentialMap = []; foreach ($packages as $package) { $key = $package->url . '|' . $package->username; if (! isset($credentialMap[$key])) { $credential = Credential::create([ 'name' => parse_url($package->url, PHP_URL_HOST) ?? $package->url, 'url' => $package->url, 'email' => $package->username, 'password' => $package->password, 'is_validated' => $package->is_credentials_validated, 'validated_at' => $package->credentials_validated_at, ]); $credentialMap[$key] = $credential->id; } DB::table('satis_packages') ->where('id', $package->id) ->update(['credential_id' => $credentialMap[$key]]); }
- Run migrations:
php artisan migrate
- Update code references:
$package->url→$package->credential->url$package->username→$package->credential->email$package->password→$package->credential->passwordCreateAuthJson→ removed, no longer needed
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.
