← The specification

Repo: sdk-php

Rendered from spec at76d46f3 (2026-10-25) · its source · Edit this page

Status: Accepted

The PHP client for lemonfiber’s web API. PHP, Hippocratic 3.0.

Implements: the client half of the web API contract, as a peer of sdk-ts.


What this repo is

A library with no user interface and no server, built on Saloon for its HTTP layer. It speaks the web API and exposes it as typed calls, a typed event stream, and typed errors.

It is a peer of sdk-ts, not a translation of it (ADR-0013). Both conform to the same specification; neither is the reference for the other. An SDK that disagrees with the contract is wrong.

What is generated and what is written

The split is the same in every SDK (ADR-0014):

Generated into src/Generated/, never edited by hand Response shapes, endpoint paths and parameters, action names, event names, the wire version
Written, once, in PHP The stream’s behaviour, the token’s placement, the error model’s wording

Everything generated comes from the contract/web-api/ directory, which lemonfiber produces from the types that actually serialise the reply (ARCH-R56). A hand-written response shape here would be a second source of truth for the contract, which ARCH-R58 forbids.

What it owns

The parts a schema cannot express, and which each SDK must therefore implement and test for itself:

  • The stream — heartbeat detection (ARCH-R50), resumption, and marking values held across a reconnect gap as stale rather than current (ARCH-R51).
  • The token — supplied by the caller, sent as a header, never placed in a URL (ARCH-R52).
  • Loopback, or an address a pin vouches for — a non-loopback host is refused unless the caller supplied that stack’s certificate pin, and where one is supplied it is enforced during the TLS handshake so nothing is written to an unverified peer (ARCH-R60, ARCH-R99, ADR-0025). The pin is given when the client is built and the client configures the transport itself; there is no way to supply one address without the other, and no seam through which a caller can reach verification to weaken it. The rule cites ARCH-R60 rather than C6-R1, which is about where a server binds and not about what a client may dial.
  • The refusal on mismatch — a wire version it cannot speak names both versions and returns nothing (ARCH-R55).

What it must not own

  • Rendering or presentation. It is consumed by scripts and applications, and must assume neither.
  • Policy. It reports what the core said. What to do about a stuck download is the core’s decision.
  • State beyond the stream. No caching, no reconciliation. A figure it has not been given is one it does not have.

Quality bar

The same standard as the Rust workspace, in its PHP equivalents: static analysis at maximum with no baseline and no suppressions, 100% coverage enforced as a gate, mutation testing with a minimum score — coverage that kills no mutants proves only that lines ran — automated refactoring checks, architecture tests, and a strict formatter. Dependencies beyond the HTTP layer are avoided: a client library’s dependency tree becomes every consumer’s.

Two obligations are specific to a published library: a changelog, since people pin it, and a backward-compatibility check, so a breaking change fails the build rather than surprising a consumer.

Publishing

Not published: no release, no tag, and no Packagist registration. lemonfiber-companion takes it from this repository as a VCS dependency, pinned to a commit by its lockfile. Registration on Packagist waits for a stable major worth pinning.