SDK boundary¶
This package is a software development kit, not an application. It is wrapped
by framework packages (Laravel first, Drupal next) and by applications. The
line between the two is the most important design decision in the package, so
it is written down here and enforced by a PHPStan rule: nothing under src/
may reference anything outside the package's own namespace, the PSR interfaces
and PHP itself.
| The SDK owns | A wrapper or host supplies |
|---|---|
| Vocabulary (generated from IATA's ontologies), the JSON-LD subset, the model and builders, change diffing and applying | Nothing |
The PSR-15 OneRecordServer: routing, content negotiation, errors, every endpoint, the action-request lifecycle, notification fan-out rules |
Mounting it under a route; converting the framework's request/response to PSR-7 if needed |
| SPI interfaces with in-memory reference implementations | Persistent implementations (Eloquent, Drupal entity or database API), migrations |
NotificationOutbox: the SDK enqueues outgoing notifications |
Draining the outbox (queue job, cron) and sending through the host's egress controls, using the SDK client |
| PSR-14 event objects raised on object creation, revision, received events and action-request changes | Mapping them to framework events, webhooks, business reactions |
Authenticator and AccessPolicy interfaces; an RS256 JWT verifier with static-key and JWKS resolvers; a client-credentials token endpoint with a ClientCredentialsVerifier interface |
Partner and credential storage, secret hashing, rate limiting, ownership rules such as "a partner sees only the objects routed to it" |
IriMinter interface with a deterministic UUIDv5 default |
Tenant or base-URL specifics |
The PSR-18 client, TokenProvider interface, a client-credentials provider with PSR-16 caching |
The HTTP client, cache binding, proxies |
| Consumption of PSR-20 clock, PSR-3 logger, PSR-14 dispatcher | Bindings |
| "Forget" as a first-class operation: closing access immediately and an SPI hook for the host's data-protection erasure | Retention policy and scheduling |
Layers inside the SDK¶
The inside is layered too, and a second PHPStan rule (tools/PhpStan/LayerRule.php)
enforces it from one table. Each layer is a namespace under
LambdaTwelve\OneRecord; the longest matching prefix wins, so Server\Spi
is its own layer while Server\Endpoint belongs to Server. A layer may
depend on itself and on what its row lists; everything else fails the build.
| Layer | May depend on | Why |
|---|---|---|
Spec |
nothing | Versions and namespaces: constants everything reads |
Rdf |
Spec |
Terms, triples, graphs |
Vocabulary |
Spec, Rdf |
The generated ontology terms and questions about them |
JsonLd |
Spec, Rdf, Vocabulary |
The restricted JSON-LD processor and node helpers |
Model |
the four above | Logistics objects, events, builders, local graphs |
Change |
the above, Model, Api |
api:Change is an API document with diff/apply logic |
Api |
the above, Model, Change |
Every other API document; an ActionRequest carries a Change |
Auth |
Spec, Rdf, Server\Spi |
Implements the Authenticator SPI and nothing else of the server |
Server\Spi |
the document layers | What hosts implement: documents in, documents out, no server internals |
Server\Event |
the document layers, Server\Spi |
PSR-14 events |
Server\InMemory |
Server\Spi, Server\Event, and the wiring classes ServerConfig, Services, ServerBuilder, OneRecordServer, IdGenerator, SystemClock |
Reference stores and a complete server; never endpoint code |
Server |
everything above it | Routing, HTTP, endpoints, lifecycle, fan-out |
Client |
the document layers, Auth |
Talks to other servers; must never reach into ours |
Testing |
everything | Doubles and store contract tests shipped to hosts; the one place PHPUnit may appear |
Change and Api are a declared pair: a Change is itself an API document
(api:Change) and lives in its own namespace only because of the size of its
builder and applier. Nothing else may be circular.
What this guarantees a wrapper: implementing the SPI needs only the document layers; replacing an in-memory store never pulls in endpoint code; and a future client package can be split off without touching the server.
Business concepts (shipments, tenants, ERPs, carriers) never appear in the SDK. The SDK knows logistics objects, logistics events, action requests, subscriptions and notifications: the vocabulary of the specification and nothing else.