The supported Symfony bundle for Atoms wires Atoms\Client\AtomsClient, the
inbound callback stack (HMAC verification, Methods dispatch, AtomJob
reconstruction), a Messenger-backed queue bridge, and thin console wrappers
around the atoms binary — all on top of atoms/client alone.
atoms/client is deliberately framework-free (integration-plan §5.3), so
this bundle depends on atoms/client and Symfony components only; it never
needs Atoms\Laravel\* or Illuminate\* (tests/LayeringTest.php is the
mechanical check, and docs/conventions.md the rule it enforces).
config/bundles.php:
return [
// ...
Atoms\Symfony\AtomsBundle::class => ['all' => true],
];
config/packages/atoms.yaml:
atoms:
environment: '%env(APP_ENV)%'
endpoint: https://atoms.your-subdomain.workers.dev # your own deployed Worker
shared_secret: '%env(ATOMS_SHARED_SECRET)%' # base64 of 32 random bytes, identical on the Worker
# shared_secret_previous: '%env(ATOMS_SHARED_SECRET_PREVIOUS)%' # add during a rotation overlap
timeout: 10.0
max_attempts: 3
callback_path: /atoms/callback
callback_timestamp_window: 300 # seconds of clock skew a callback's timestamp may deviate before rejection
http_client: null # service id, or null to auto-detect / fall back to Guzzle
psr17_factory: null # service id, or null to fall back to Guzzle (no auto-detection — see "What's wired")
methods_classes:
- App\Atoms\GameRoom\Methods # only needed for #[MethodsFor] overrides
shared_secret is the root of the app ↔ Worker boundary: the same base64 of
32 random bytes the Worker holds as ATOMS_SHARED_SECRET. Keep it on your
hosts — requests carry a bearer derived from it with HKDF-SHA256, and inbound
callbacks are verified with a second derived key; atoms token prints the
bearer for hand-issued requests. To rotate, set shared_secret_previous to
the outgoing value on both sides: callbacks signed under either secret verify
while the overlap lasts, and outbound requests use the current one.
Import the atoms resource type from your own routing, e.g.
config/routes/atoms.yaml:
atoms:
resource: .
type: atoms
There is no vendor path to import — Atoms\Symfony\Routing\AtomsRouteLoader
is registered as a routing.loader-tagged service and resolves the atoms
type itself. It always mounts at the bundle's current atoms.callback_path,
so reconfiguring that value moves the route with it; nothing else needs to
change.
Atoms\Client\AtomsClient — the RPC stub-proxy client, service id
atoms.client (public; the AtomsClient::class service itself is private).HmacVerifier, InMemoryNonceStore,
MethodsResolver, CallbackKernel — plus
Atoms\Symfony\Controller\CallbackController, which converts Symfony
Request/Response to/from PSR-7 by hand against the plain
ServerRequestFactoryInterface/StreamFactoryInterface (no
psr/http-message-bridge dependency); routed automatically as described
above.ServerRequestFactoryInterface + StreamFactoryInterface
ResponseFactoryInterface in one — GuzzleHttp\Psr7\HttpFactory and
Nyholm\Psr7\Factory\Psr17Factory both qualify) resolved, in order: the
service id in psr17_factory, else a bundled GuzzleHttp\Psr7\HttpFactory
(clear exception if guzzlehttp/psr7 isn't installed). No auto-detection:
PSR-17 spans several factory interfaces, so the bundle never guesses which
app service should serve all of them — name it explicitly in
psr17_factory or accept Guzzle. Backs CallbackKernel,
CallbackController and AtomsClient alike. Resolved in a compiler pass
so bundle registration order never matters.atoms.http_client,
else an app-defined Psr\Http\Client\ClientInterface service, else a
GuzzleHttp\Client (clear exception if guzzlehttp/guzzle isn't
installed). Resolved in a compiler pass so bundle registration order never
matters.Atoms\Client\Callback\QueueBridge: Atoms\Symfony\Messenger\MessengerQueueBridge
when symfony/messenger is installed and the app has a message bus
service, wrapping dispatched AtomJobs as AtomJobMessage (normalized,
JSON-safe constructor args only) and handling them back via
AtomJobHandler; otherwise Atoms\Client\Callback\NullQueueBridge, which
throws a clear "configure a QueueBridge" exception on first use.methods_classes entry is registered on the
MethodsResolver by name (for #[MethodsFor] overrides) and as an
autowired service, collected into a Symfony\Component\DependencyInjection\ServiceLocator
that CallbackKernel consults first — so a listed Methods class can itself
take app services as constructor dependencies. Anything not listed is
instantiated with new $class().CallbackKernel takes the app's logger service if one exists
(Monolog via FrameworkBundle, or anything else bound to that id) and null
otherwise — never a hard dependency on psr/log's presence being wired up.atoms:deploy, atoms:rollback, atoms:list — thin console wrappers that
shell out to the real atoms binary (discovered via vendor/bin/atoms,
then $PATH, then packages/cli/bin/atoms); registered only when
symfony/console is present.Every auto-detected collaborator has an explicit override point:
http_client to a service id, or just register your
own Psr\Http\Client\ClientInterface service and leave it null.psr17_factory to a service id implementing
ServerRequestFactoryInterface, StreamFactoryInterface and
ResponseFactoryInterface.symfony/messenger and register a message bus,
or bind your own Atoms\Client\Callback\QueueBridge implementation to that
service id directly.Atoms\Client\Callback\InMemoryNonceStore
(process-local, not shared across workers); alias
Atoms\Client\Callback\NonceStore to your own implementation (e.g.
Redis-backed) for a multi-process deployment.methods_classes to get container
resolution (and #[MethodsFor] support); anything else resolves by naming
convention with new $class().logger (FrameworkBundle
does this for you when Monolog is installed).Methods classes resolve from the container only when listed under
methods_classes; anything not listed there is instantiated with
new $class() rather than autowired, even if the class itself would
otherwise be autowirable.
This package is developed in the Atoms monorepo. Its standalone repository is a read-only distribution mirror; report issues and send pull requests to the monorepo. Full documentation is at docs.atomsphp.dev. Licensed under the MIT License.
How can I help you explore Laravel packages today?