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
Fund package maintenance!
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
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- dev-main
- 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
This package is auto-updated.
Last update: 2026-03-13 19:44:57 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 10+
- Satis (
composer/satis— included as dependency)
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
License
The MIT License (MIT). Please see License File for more information.
