application-manager-tools/am-driver
Symfony bundle and framework-agnostic PHP library for integrating managed applications with Application Manager. Supports orchestration commands, consumption webhooks, and operational state push, plus an OpenAPI 3.1.1 spec and integration docs for PHP 7.4+ and Symfony 5.4–8.0.
What the package actually is. application-manager-tools/am-driver is not a general-purpose library — it is the managed-application side of a bilateral contract with a specific control plane (Application Manager). It supplies four things: (1) inbound HTTP endpoints that receive orchestration commands (CREATE_INSTANCE, START, STOP, QUOTA_EXCEEDED, UPGRADE_INSTANCE, GET_INFO_INSTANCE, SET_STATEVIEW_INSTANCE), consumption webhooks, and operational-state pushes; (2) an outbound AmApiClient to AM's /api/v1/... surface including billing upgrade/resubscribe session creation; (3) idempotency/receipt plumbing and local snapshot stores; (4) a CLI (am-driver serve, orchestration:simulate, state:push-sample, consumption:push) plus an OpenAPI 3.1.1 spec and Swagger UI. The value proposition is therefore narrow and strategic: if we are a managed app in the AM ecosystem, this package is the sanctioned integration path; if we are not, it has essentially zero value.
Architecture fit — good ports-and-adapters, wrong framework. The design is genuinely clean. ApplicationManagerTools\AmDriver\Core\ holds the contracts (CreateInstanceHandlerInterface, StartInstanceHandlerInterface, StopInstanceHandlerInterface, QuotaExceededInstanceHandlerInterface, UpgradeInstanceHandlerInterface, GetInfoInstanceHandlerInterface, SetStateViewInstanceHandlerInterface, plus Deferred*DispatcherInterface), and the host supplies only product logic. That Core/adapter split is the single most important fact for us: the advertised target is Symfony, not Laravel. The bundle layer is Symfony-specific (compiler-pass autoconfiguration, @AmDriverBundle/Resources/config/routes.yaml, AbstractController, ReceptacleHttpKernel returning Symfony\Component\HttpFoundation\Response). In Laravel we get Core + our own transport, not a drop-in.
Integration feasibility — moderate, bounded, and front-loaded. The Laravel path is "Core-only": wire AmApiClient, OrchestrationCommandProcessor, and ResourceSnapshotManager by hand, implement seven handler interfaces and two deferred dispatchers, then re-express the routes, the X-AM-Application-Token authenticator (note 0.0.14 collapsed four tokens into one — a meaningful blast-radius change), and the CLI. OrchestrationCommandProcessor now takes handlers positionally (7th argument for upgrade as of 0.0.22), so constructor churn is a real cost. The hard dependency set — php ^7.4, symfony/http-client ^5.4|^6.4|^7.0, symfony/console — is satisfiable in any modern Laravel, but it introduces a second HTTP client alongside Guzzle, which is a governance question more than a technical one.
Technical risk — high, and mostly version risk, not design risk. The changelog is the story: 0.0.9 renames the correlation key tenantId → instanceId across DTOs, stores, state, consumption and CLI; 0.0.14 collapses the token model and removes instanceIntegrationToken; 0.0.16 makes CreateInstanceHandlerResult a hybrid fromArray() DTO; 0.0.17 removes in-bundle GET_INFO/SET_STATEVIEW and forces two more host aliases; 0.0.19 adds QUOTA_EXCEEDED as a required constructor argument; 0.0.22 adds UPGRADE_INSTANCE and another required argument; 0.0.23 changes a client method signature; 0.0.25 is a fix: upgrade deferred hotfix — which tells us the deferred path has already broken in the field. That is eight-plus breaking changes inside a 0.0.x line with 0 stars and 0 dependents. There is no semver safety net, no community to absorb upgrade pain, and no visible third party validating the contract. Additional risks: default persistence is filesystem JSON keyed by instance (snapshots/{instanceId}.json), which silently fails under horizontal scaling; deferred dispatchers appear to spawn subprocesses, which is awkward in containers/FaaS; and the reference documentation is in French, which is a staffing constraint if the team is not francophone.
Key questions before commitment.
X-AM-Application-Token?idempotencyKey?Stack fit. Laravel is workable but is a second-class citizen. Concretely: use the Core namespace only; do not install the bundle's routing or controllers. symfony/http-client and symfony/console arrive as transitive dependencies (Laravel already ships console components), so the footprint is acceptable; wrap AmApiClient behind our own AmGateway interface so the vendor type never leaks into domain code. Keep symfony/http-client out of general application code — one client per concern.
Migration path (build a thin in-repo bridge, not a fork).
app/Integration/Am/ namespace containing: an AmDriverServiceProvider binding the seven handler interfaces and two deferred dispatchers to app implementations; a VerifyAmApplicationToken middleware reproducing ApplicationTokenAuthenticator; and routes/api.php entries under /am/* matching the bundle's route_prefix contract exactly.instanceId — this is mandatory, not optional, and doing it up front avoids the classic "works on one pod" incident.SubprocessDeferred*Dispatcher with Laravel queue jobs, preserving idempotency-key semantics and callback ordering.vendor/bin/am-driver serve, orchestration:simulate, state:push-sample) as a test harness even in the Laravel world — it is the cheapest way to exercise our handlers against a real AM-shaped receptacle before AM's sandbox exists. This is the package's strongest asset and should be exploited, not discarded.Compatibility. Pin an exact version in composer.json (no ^0.0.x), and treat upgrades as scheduled work items. Maintain a contract test suite generated from the bundled OpenAPI 3.1.1 spec so breaking DTO changes fail CI rather than production. Record the version in an ADR alongside the AM-side version — the contract is bilateral, and mismatches will surface as "AM thinks the instance is pending."
Sequencing. (1) One-week spike: run am-driver serve, implement a stub CreateInstanceHandler, prove the callback loop end-to-end locally. (2) Build the Laravel bridge skeleton + token middleware + Redis idempotency store. (3) Ship CREATE_INSTANCE and STOP first (highest value, simplest semantics), then START. (4) Add operational-state push and outbound consumption push. (5) Add GET_INFO/SET_STATEVIEW (needed for the customer-facing state view). (6) Add QUOTA_EXCEEDED and UPGRADE_INSTANCE with queue-backed deferred execution — defer this until the queue dispatcher is proven, given 0.0.25 was a deferred-upgrade fix. (7) Add billing upgrade/resubscribe session endpoints behind a feature flag. (8) Canary one tenant, then widen.
Recommendation. Conditional go. Proceed only if AM onboarding is a committed business requirement; budget one backend engineer for ~2–3 weeks to reach CREATE/STOP in a non-production environment, and explicitly accept a recurring upgrade tax. If AM is speculative, stop — the 0.0.x churn rate and zero external dependents are not a good place to spend scarce integration capacity.
Maintenance. This is the dominant cost. Expect a breaking release every few weeks during the 0.0.x line (the observed pattern is roughly one per tagged release). Estimate 0.5–2 engineer-days per bump for a Laravel Core-only integration, concentrated in DTO shape changes and handler-method churn. Mitigations: exact version pinning, a dedicated AmDriverContractTest suite, an ADR logging each upgrade, and a small adapter layer so vendor types never reach domain code. Consider funding an upstream contribution of a Laravel bridge — it converts our maintenance into shared maintenance and is the only realistic lever we have with a 0-star package.
Support. There is no vendor SLA and effectively no external user base; support is whatever relationship we have with the AM team. Name an internal owner (single accountable engineer, not a rotating on-call) and establish a direct channel to AM maintainers before the first production tenant. Documentation is competent but French-first — verify the team can read docs/MANUEL-INTEGRATION-INTEGRATEURS.md and docs/ECARTS-AM.md (the "gaps vs AM" doc is the one that will explain production surprises). MIT license means we can fork Core if the upstream stalls, but forking a moving target is a last resort; prefer pinned consumption plus upstream PRs.
Scaling. Inbound handling is stateless and scales horizontally without issue. Three real constraints: (a) idempotency/receipt storage must be shared — the file-backed stores are single-node only; (b) deferred dispatchers must become queue jobs — subprocess spawning does not survive autoscaling, read-only root filesystems, or FaaS runtimes; (c) AM's own callback concurrency determines whether CREATE needs per-instance locking — assume it does and lock on instanceId inside our handler. Consumption pushes are per-instance and low volume; operational-state pushes are periodic and should be batched or debounced if instance counts grow.
Failure modes to instrument from day one.
idempotencyKey dedupe we double-provision tenants or double-count consumption. Dedupe must be transactional with the side effect, not best-effort.CREATE fails midway (tenant created, DB not) → orphaned resources. CreateInstanceHandlerResult::fromArray() validation (startedAt, integrationInstanceId non-empty) catches shape errors but not orphans; we need compensating cleanup.occurredAt and startedAt are validated as ISO-8601 and used for ordering; skew across pods produces confusing idempotency and audit results. Enforce UTC, NTP, and log both sides' timestamps.Ramp-up. Plan on 1–2 weeks for a competent Laravel/PHP engineer to reach a working local loop (the receptacle CLI makes this genuinely fast — a real strength) and 2–3 additional weeks to production-hardened CREATE/STOP/state/consumption with Redis-backed idempotency and queue-backed deferrals. Prerequisites: a Symfony-literate or at least PSR-18-comfortable engineer, French reading ability for the integration manual, and an AM-side counterpart available for sandbox access and callback debugging. Do not attempt this as a side-quest alongside other delivery — the upgrade cadence alone will erode progress if it is not someone's assigned responsibility.
How can I help you explore Laravel packages today?