revolution/atproto-lexicon-contracts
PHP contracts for AT Protocol lexicons: typed interfaces and value objects that keep atproto records, XRPC payloads, and schema handling consistent across Laravel apps. Code against stable abstractions instead of raw arrays.
Architecture fit. This package is a type-contract artifact, not a runtime library: auto-generated PHP interfaces and enums derived from AT Protocol Lexicon schemas. It carries value only if you also own the layers around it — concrete DTO implementations, hydration/serialization (ATProto uses DAG-CBOR for repository records and JSON for XRPC), and an XRPC transport client. PHP interfaces cannot be instantiated, so the package supplies the shape of the domain but none of the behavior. In a Laravel codebase this maps cleanly to a hexagonal/anti-corruption boundary — contracts at the edge, Eloquent and domain services untouched — but it is additive scaffolding, not a solution.
Integration feasibility. Mechanically trivial: composer require + PSR-4 autoload, zero transitive dependencies, zero framework coupling, MIT-licensed. The friction is entirely in trust and provenance, and there are two concrete red flags worth surfacing before any engineering time is spent:
revolution/atproto-lexicon-contracts while the source repository is invokable/atproto-lexicon-contracts. Unexplained namespace divergence in a Composer package is either a fork with unmerged changes, a rename in flight, or an unverified listing — all of which require a manual diff against the upstream before the first composer require.Technical risk. The dominant risk is lexicon drift, and it is structural rather than incidental:
match in your code fatals at runtime. Enum regeneration is therefore a semver-major event for consumers, not a patch.Key questions for the maintainer/owner.
Stack fit. Compatible with Laravel 8–12 given a PHP 8.1+ floor (enums). Verify the generated code's use of readonly properties, never returns, and typed class constants — if present, the floor rises to PHP 8.2/8.3 and must be pinned in composer.json platform config and CI matrix. No service provider, config, or migration surface, which is a genuine plus: nothing to register, nothing to boot, nothing to break during framework upgrades.
Migration path. This is greenfield — no incumbent to displace. The correct posture is treat it as vendored source, not a dependency. Recommended shape:
App\Contracts\AtProto\...) so the public API is yours to evolve.AtProtoRecordSerializer, PostMapper — so no controller, job, or model ever references a generated interface directly.Compatibility. Watch for namespace collisions with any existing ATProto or Bluesky SDK already in the dependency graph; if you adopt a competing client later, the generated FQCNs will fight for the same names. Also confirm the package declares no replace/provide claims that would mask a conflict.
Sequencing.
Maintenance. Effectively you are adopting a generator workflow, not a library. Budget for a recurring ritual: watch upstream Lexicon changes, regenerate, diff, review, release. Call it 2–4 hours per schema cycle, plus unbounded time when the diff is large. With 0 stars and 0 dependents, the bus factor is 1 and abandonment should be assumed as the base case — MIT licensing makes the fork-and-own path legitimate and probably correct.
Support. No community triage, no SLA, no known-good production corpus. Internal ownership of the generated code and its tests is a prerequisite, not a mitigation. Escalation path is "read the Lexicon spec," which is well documented — this is the saving grace.
Scaling. Runtime cost is near zero: interfaces and enums are compile-time constructs, opcache-friendly, no reflection required for autoloading. The scaling risk is application-side: reflection-based or generic hydration mappers over dozens of generated types will dominate request time in hot paths. Prefer hand-written or code-generated hydrators, cache compiled serializers, and benchmark record mapping under realistic payload sizes before rollout.
Failure modes. Ranked by likelihood × impact:
match throws UnhandledMatchError in production. Mitigate with a default arm mapping to a domain-level Unknown value, plus alerting on that path.Ramp-up. Consuming the interfaces: under a day for a mid-level PHP developer. Building the serialization and hydration layer: 1–2 weeks including test fixtures, and this is the real cost center. Owning the generation pipeline and lexicon-update process: 2–3 weeks before it is trustworthy. Total credible time-to-production for a single integration: 4–6 engineer-weeks, of which the package itself contributes almost nothing.
Recommendation. Do not adopt as a direct Composer dependency at its current maturity signal. Use it as a reference implementation — fork, pin, namespace internally, and treat the generated contracts as a starting artifact you own. The interfaces are cheap to reproduce and expensive to depend on.
How can I help you explore Laravel packages today?