Public Trust API, Single-Writer Audit Pipeline & Cross-Site Badge Integrity
Why This Matters
SpeyBooks publishes its security grade in the footer of every page. A grade is only worth showing if it is current, and until today nothing enforced that. The documentation site had been displaying an audit badge frozen at 28 February: a correct grade, three and a half months stale, which is its own kind of dishonesty. The badge data also had two writers and three copies across two sites, with no mechanism keeping them in agreement.
This release rebuilds the trust surface on a single principle: the badge must be able to prove itself. One writer produces the audit data, one committed model defines what a grade means, a public API serves it with its age attached, and a freshness gate refuses to render a grade older than 48 hours. The same data now reaches every SpeyBooks page on the same day it is measured.
Public Trust API
-
New endpoint:
GET /v1/public/trustReturns the latest security audit block: grade, score, modules passed, audit date, and report timestamp. No authentication required. Documented in the API reference. -
Staleness is a fact on the wire, not a hidden state Every response carries
ageSecondsand astaleflag computed per request against a 48-hour freshness threshold. The endpoint reports staleness rather than hiding it, so any consumer, including our own pages, can make its own rendering decision. Monitoring can distinguish “endpoint down” from “data stale” because they are different signals. -
Render fragment with an enforced freshness gate A companion HTML fragment endpoint enforces the gate: a stale, invalid, or missing audit block renders an honest “verification pending” fallback, never a stale grade. This behaviour is negative-tested in production: stale, invalid, and missing cases were each injected under controlled conditions and seen to fail closed, with no internal detail leaking into any error response.
-
The badge links to its evidence The audit link in the trust strip now deep-links to the published audit report for the exact run that produced the grade displayed.
Single-Writer Audit Pipeline
-
One writer per data population The security harness is now the sole author of all badge data. A second component that previously also wrote to the footer data has been extracted; it now records publication state in its own file, consumed read-only by the writer. A field only a second author would produce is rejected by strict schema validation at the read boundary, so the property holds by construction, not by convention.
-
Daily measurement, weekly publication The harness now runs daily (quiet runs that feed the badge) and weekly (publishing the full audit report to Insights). The badge can be at most a day behind reality; the long-form report keeps its weekly cadence. Previously a single weekly run did both, and “as audited on” could legitimately be a week old.
-
Atomic, validated writes Every badge data write is bounds-checked against the committed grade model before it lands, written atomically, and refused outright if the audit output is malformed. A grade outside the model, an impossible module count, or a corrupt report halts the pipeline rather than publishing a lie.
One Grade Model, Everywhere
-
The grade scale is data, defined once The grade enumeration, ranking, and badge visibility floor now live in a single committed model file read by the audit pipeline, the API, and both websites. No component re-types the scale, so a change to the model changes every consumer at once and a grade the model does not recognise is invalid everywhere simultaneously.
-
Rank is derived, never stored Both sites previously disagreed about whether a stored rank or a derived one was authoritative. Rank is now derived from the grade at the point of use on every surface, making the disagreement unrepresentable rather than merely fixed.
Footer Integrity, Both Sites
-
The frozen badge is gone The documentation site footer now receives the same audit data as the marketing site on every audit run. Both domains display the same grade, module count, and audit date.
-
An unmeasured claim removed The footer previously displayed a “Perimeter Active” label that no audit module measured. A constant dressed as telemetry is not honest reporting, so the label has been removed.
-
Dates readable by humans and machines Audit dates are stored and transmitted in ISO format and rendered as “10 Jun 2026” in the footer, with the machine-readable form preserved in the markup.
Operational Impact
- The documentation site badge moved from 102 days stale to same-day, with staleness now bounded at 48 hours by an enforced gate
- Badge data writers reduced from two to one, with the single-writer property protected by strict schema validation
- Grade scale definitions reduced from four independent copies to one committed model
- Freshness gate negative-tested live: stale, invalid, and missing data each verified to fail closed with no information leakage
- Trust data exposed as a public, unauthenticated, documented API with per-request freshness facts
- Audit cadence improved from weekly to daily without increasing publication noise
Files Changed
Backend:
- Public API: trust block service and two new public read endpoints (JSON and HTML fragment), exact-path allowlisted
- Audit pipeline: writer rewritten with validation at the write boundary; publication state separated into its own population; daily and weekly schedules split
Frontend / both sites:
- Footer components: stale data source replaced, unmeasured label removed, date display formatting added
- Footer schemas: grade validation now derived from the committed model on both sites; the documentation site schema previously accepted any non-empty string as a grade and no longer does
- Deploy pipelines: both sites now sync the grade model at the artifact-sync gate
Known Issues
- The website footers still bake the badge at build time from writer-maintained data files; a deploy after each audit keeps them current. A live server-side include of the fragment endpoint is the planned next phase, after which the build-time copies retire.
The next phase replaces both baked badges with live fragments served by the trust API, completing the move from “the badge was true when we built the page” to “the badge is true now”.