composer/satis
Satis is a simple static Composer repository generator. Point it at a composer.json and it builds an index of your packages and dependencies for fast, low-maintenance private or public Composer hosting, with optional archive downloads and mirroring.
Architecture fit. Satis is a single-purpose CLI build tool, not a library or runtime dependency — it takes a satis.json definition (or a set of VCS URLs), crawls each source repository with git/svn/hg, resolves Composer metadata, and emits a fully static Composer repository (packages.json plus per-package JSON, optionally mirrored dist archives). Architecturally it slots into build infrastructure, not the application: it produces an artifact that any Composer client consumes. That is an excellent fit for PHP shops that need an internal package index without operating a stateful service (no database, no daemon, no queue, no API surface). It is a poor fit for any stack that is not Composer-based.
Integration feasibility. Low effort. Install as a global Composer package or a phar, author a satis.json, run bin/satis build satis.json public/, serve public/ from nginx, S3/CloudFront, or an internal static container. The only real prerequisites are git binaries and network/SSH egress to the source hosts on the build agent. There is no coupling to application runtime versions, frameworks, or deployment topology.
Technical risk. The dominant risk is lifecycle, not technology:
master by the Composer core team on a best-effort basis, but there is no released version line to pin against, which is awkward for supply-chain and change-control policies.metadata-url); taking advantage of it means running an untagged commit.Key questions. How many source repositories today, and what is the 12-month growth curve? Do consumers need per-team/per-package authorization, or is a shared internal index acceptable? Is pinning to an untagged master commit acceptable under the org's dependency policy, or does it require an internal fork? Do we need Composer 2 v2 metadata for install-time performance? Is this intended to replace, or coexist with, a hosted option (Private Packagist, GitLab's Composer registry, Repman)? Who is the named internal owner of the build job?
Stack fit. Relevant only where Composer is the package manager. Satis sits beside source control (GitHub/GitLab/Bitbucket) and the CI runner (GitHub Actions, GitLab CI, Jenkins), and its output sits behind whatever serves static internal assets. The runtime footprint in production is exactly zero — the package repository is just files.
Migration path. If teams currently use inline vcs/path repositories or committed vendor/ directories, adoption is incremental and reversible: stand up satis.json with a small subset of packages, publish to an internal URL, add it as a single composer repository entry in one canary project, verify resolution and lockfile behavior, then broaden. If the org is already on Toran Proxy or Private Packagist, Satis is a feature downgrade (no UI, no ACL, no per-user tokens) — in that case evaluate it for cost reduction on a constrained scope rather than as a lift-and-shift.
Compatibility. Requires a supported PHP runtime on the build agent, a git binary, and egress to all source hosts. Output is consumable by Composer 1 and Composer 2 clients; v2 metadata requires building from a recent master commit. The tool's own PHP support window follows Composer's, so the version you run should be re-validated whenever the CI base image's PHP version moves.
Sequencing. (1) Spike: build an index for ~5 representative packages, including one private repo and one with non-trivial transitive dependencies. (2) Codify satis.json in a dedicated repository under review, not on a build agent's disk. (3) Wire a scheduled or webhook-triggered CI job with a pinned binary/container. (4) Publish the static output behind an authenticated endpoint. (5) Onboard consumers team-by-team with a documented repository stanza. (6) Add freshness/failure monitoring before removing any legacy repository configuration.
Maintenance. This is the main ongoing cost and it is a governance cost rather than an engineering one. With no release since 2018, the team must choose between freezing on the 2.0.0 tag (missing Composer 2 metadata improvements and any upstream fixes) or tracking a pinned master commit that they periodically re-pin. The recommended posture is to build and version your own container image or phar — effectively an internal fork point — so upgrades are deliberate, tested, and recorded. The configuration surface itself is small and stable, so recurring engineering effort is low.
Support. Upstream is community-driven under the Composer organization on GitHub issues and PRs, with no SLA and no vendor to escalate to. Every incident will be self-diagnosed. An internal owner must be named explicitly, with the satis.json and CI job treated as owned production infrastructure rather than a side project.
Scaling. Build cost is linear in repository count and history size — every run re-crawls every configured repository. Beyond a few hundred repos, mitigate with shallow clones, a local mirror or object cache of origin repositories, --skip-errors on non-critical entries, and splitting into multiple Satis instances partitioned by domain or team so a single build stays within CI time limits. Serving the output scales trivially via CDN or object storage. Enabling dist mirroring trades install-time speed for unbounded disk growth, so set retention expectations early.
Failure modes. Watch for: (a) silent incompleteness — --skip-errors lets an unreachable source repo drop packages from the index without failing the build, producing confusing resolution errors downstream; (b) stale index — if the cron or webhook dies, consumers silently resolve against an old index with no built-in signal, so monitor output mtime and package count as first-class SLIs; (c) credential expiry — SSH deploy keys or tokens silently break clone steps; (d) disk exhaustion from mirrored dists; (e) poisoned builds — a malformed composer.json in one source repo can abort or corrupt the run; (f) no rollback — publishing directly into the served directory leaves no way back, so publish atomically to a versioned/immutable path and flip a pointer.
Ramp-up. Measured in hours for any engineer already fluent with Composer. The learning curve is Composer repository semantics, version constraints, and repository precedence — not Satis itself. Documentation is adequate for the happy path and thin on CI patterns, atomic publishing, and authentication, so an internal runbook is worth writing on day one. Team-level ramp-up risk is low; the durable risk is institutional — depending on a build-critical tool that has not cut a release in years.
How can I help you explore Laravel packages today?