artisan-build / crate-server
The server package for Crate private Composer registry integrations.
Requires
- php: ^8.3
- artisan-build/built-for-cloud: ^0.19.8
- artisan-build/crate-contracts: ^1.0
- illuminate/bus: ^13.0
- illuminate/console: ^13.0
- illuminate/contracts: ^13.0
- illuminate/database: ^13.0
- illuminate/filesystem: ^13.0
- illuminate/process: ^13.0
- illuminate/queue: ^13.0
- illuminate/support: ^13.0
- laravel/framework: ^13.0
- laravel/mcp: ^1.0
Requires (Dev)
- laravel/pint: ^1.27
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Server package for the Crate private Composer registry.
It stores the list of private repositories to serve, generates Satis configuration from that database state, dispatches Satis builds, mirrors generated registry output to storage, and serves Composer metadata and dist archives through a credential gate.
Served Repositories
The package stores served repositories in the served_repos table. Each row contains the Composer package name, source URL, repository type, status, optional encrypted source credential, and last_built_at timestamp.
Commands:
php artisan crate:repos:add vendor/pkg https://github.com/vendor/pkg.git --source-token=... php artisan crate:repos:add vendor/pkg https://github.com/vendor/pkg.git --type=vcs php artisan crate:repos:list php artisan crate:repos:remove vendor/pkg
crate:repos:add validates lowercase vendor/package names and stores source credentials encrypted via Eloquent casts. The list command intentionally does not print source credentials.
Satis Builds
Builds are dispatched with:
php artisan crate:build php artisan crate:build vendor/pkg
The command dispatches ArtisanBuild\CrateServer\Jobs\BuildSatis. A full build has no package argument. Passing a package performs an incremental build for that package and seeds the temporary output directory from the current archive storage before running Satis.
SatisConfigGenerator builds satis.json from the database:
homepageis the host application'sconfig('app.url').- repositories come from
served_repos. - full builds require all registered packages.
- incremental builds require the requested package only.
- archives are configured as zip files under
distwithprefix-urlset to the host application'sconfig('app.url').
For source credentials, the generator writes a scoped Composer auth config for the build process. GitHub hosts use github-oauth; other hosts use bearer auth.
BuildSatis runs the configured isolated Satis binary via Symfony Process, using the temporary build directory as COMPOSER_HOME. Successful builds mirror the generated output to CRATE_ARCHIVE_DISK under CRATE_OUTPUT_DIR. Failed builds store a redacted output tail so source credentials are not persisted in build logs.
The service provider schedules a daily full rebuild:
$schedule->command('crate:build --trigger=schedule')->daily();
Gated Registry Routes
The package registers Composer registry routes at the app root:
GET /packages.jsonGET /p2/{path}GET /dist/{path}
All routes use ArtisanBuild\CrateServer\Http\Middleware\EnsureValidCredential, registered as crate-server.credential.
Composer authenticates with HTTP Basic auth. The username is presentation-only; the password resolves through Built for Cloud's unified credential guard. Only an active Basic credential mapped to the fixed app purpose crate.composer.consume is admitted. Missing, malformed, wrong-purpose, expired, revoked, removed-account, and otherwise invalid credentials receive the same 401 with WWW-Authenticate: Basic realm="Crate", without streaming registry content.
Account-owned credentials follow the canonical user's versioned role, status, authority generation, and managed freshness. Installation-owned credentials are creator-independent. Their installation boundary is possession in this deployment's installation-local credential store; subject_ref is an automation routing identity, not an installation ID.
RegistryFileController streams files from the configured archive disk and output prefix:
packages.jsonserves generated Composer root metadata.p2/...serves Composer v2 provider metadata.dist/...serves mirrored dist archives.
Traversal attempts containing .. are rejected before storage access.
Configuration
Published config key: crate-server.
Crate uses the host application's config('app.url') as the public registry URL in Satis metadata and dist archive URLs. There is no separate server URL environment variable.
Environment variables:
CRATE_ARCHIVE_DISK: storage disk for generated metadata and mirrored archives. Defaults toFILESYSTEM_DISKthenlocal(on Laravel Cloud,FILESYSTEM_DISKis theprivatedisk wired to object storage, which works).CRATE_SATIS_PATH: path to the isolated Satis executable (<install-dir>/bin/satis), executed directly byBuildSatis. Defaults tobase_path('satis-tool/bin/satis')— wherephp artisan crate:install-satisinstalls it. That install has its own dependency tree and is not part of the app's vendor tree, so the isolation still holds. Set it explicitly only when Satis lives somewhere else.CRATE_OUTPUT_DIR: storage prefix for generated registry output. Defaults tosatis.CRATE_DB_*: optional separate database connection settings. If omitted, the app's default database connection is reused ascrate.
This package consumes artisan-build/built-for-cloud for the canonical credential store, Basic resolution, package-owned personal/installation credential UI, and the fixed /bfc/credentials API. CrateCredentialDeclaration implements DeclaresSelfServiceMintPolicy, admits only Basic self-service credentials, and maps crate.composer.consume to the package consumption purpose. Registry credentials carry no repository, source-secret, credential-management, transition, or shell authority.