Developers
For systems that read this archive rather than people. What the identifiers guarantee, the cadence the data moves at, the form a citation takes, the path by which a value is checked, and which entries carry text that may travel.
The record publishes what official sources published, when, how it relates, and what is forming — and never what should be done about it.
Quick start
Five stages, in the order the record itself moves in: an address is read, the read is recorded, a difference is detected, the evidence for it is assembled, and the series is addressed over time. The order is fixed and is not a ranking. Full semantics follow below.
Read-only. No key, no account, no credential of any kind is accepted or kept. The addresses are checked against the 26 routes this build serves before this page prints them.
1 · Sources
which addresses this record reads, and how each one answered a named client.
/api/v1/instruments.json · /api/v1/crawlability.json
- /sources — every address this record reads
curl -s -H 'User-Agent: your-tool/1.0' 'https://gxplex.com/api/v1/instruments.json' | jq '.items[0] | {id, source_url}'2 · Observations
what one address returned on one day: the status, the method, and a digest where text was retrieved.
/api/v1/series.jsonl · /api/v1/observations.json
- /gxp-obs — what an observation is here, field by field
- /observations/eu-gmp-annex-11 — one row’s series, with an address per record
# one record to a line; the first line is a header carrying the envelope
curl -s -H 'User-Agent: your-tool/1.0' 'https://gxplex.com/api/v1/series.jsonl' | jq -c 'select(.instrument_id == "eu-gmp-annex-11")' | head -33 · Candidates
a detected difference before anyone has decided anything about it, with the rule that reached each verdict — and, on the other side of the same pipeline, the revisions this record still asserts.
- /candidates — the detected-change candidates and their verdicts
The candidate store itself has no endpoint. Derived
from the route set by name: none of the 26 routes this build
serves is named for it, and a name census cannot see a collection
served under another word. The candidate surface is the page linked
above, where each candidate renders with the rule that reached its
verdict — a verdict without its rule is an assertion in this
record’s own voice. What IS served is the other side of the
pipeline: the revisions endpoint above carries the revisions this
record still asserts, each with confirmed_by_human. A
withdrawn revision is kept in the store and excluded from that
endpoint: deleting a confirmed claim would be the silent edit the
corrections rule forbids, and publishing one this record has
retracted would republish it. A candidate is not a revision, and
reading either surface as the other would count the decided and the
undecided alike.
4 · Evidence
what vouches for a value: the dated acts, the retained versions and their digests, and the arithmetic over them.
/api/v1/events.json · /api/v1/archive.json · /api/v1/findings.json
- /verify — the integrity root and the commands that check it without this record’s code
- /r/eu-gmp-annex-11 — one entry, with its evidence beneath it
curl -s -H 'User-Agent: your-tool/1.0' 'https://gxplex.com/api/v1/events.json' | jq '[.items[] | select(.instrument_id == "eu-gmp-annex-11")] | length'5 · TimeMap
a single address listing every version of one row with its datetime and digest.
Built, and narrower than the protocol. Derived
from the route set: 3 of the 26 routes this build serves answer as a TimeMap, one per tracked
entry, in JSON and in the link format RFC 7089 names. They list
THIRD-PARTY mementos — captures of a tracked address held at
a web archive — each carrying the archive’s own capture
instant, read from the archive’s own address. Nothing in this
build is itself a Memento and nothing here sets a
Memento-Datetime header, which is a statement
about what was built here, not about what a reader is served. No
TimeGate is served: a TimeGate negotiates a datetime over
representations, and this record serves no representation of a
tracked document to negotiate over.
A memento address is a route to a copy of a publisher’s page, so whether one is published is decided per host from the terms filings this record holds, and the rule fails closed. The JSON form carries every memento held for an entry, each with the verdict and the basis that decided it; the link format has no syntax for a withheld one, so it carries the published set and names the JSON as its own timemap.
An observation is not a memento. This record’s own dated reads travel in a separate array with their own digests, which are over normalised text rather than over any memento listed beside them.
Those are the HTTP surfaces. A Model Context Protocol server over the same data, and the whole of what it exposes, is described further down this page; it is run against a local checkout and this site serves no endpoint for it.
The basis of a value
Beside the four rights fields, an object can carry three provenance fields
— source, observed_at, distance.
They answer which tracked entry the object is about, which day this record
read the fact it carries, and how far from the primary document the reading
was taken. An envelope carrying them also publishes their census under
provenance_fields; an envelope with no such key had no
stamp written for that collection, which is an absent stamp rather than a
stamp of nulls.
Read from the route modules’ own source rather than from a list, so a
route added tomorrow answers for itself: 20 of the
26 routes this build serves compose the shared envelope and
carry the rights block. The other 6 build their
own response, carry no rights block, and are named here rather than counted
away — /api/v1/probe.json, /api/v1/probe.jsonl, /api/v1/probe/{window}.jsonl, /api/v1/schema/{name}.json, /api/v1/search.json, /api/v1/timemap/link/{slug}.link.
Each is accompanied by the state that says which absence a blank is.
observed_at_state separates an object that carries no day for
when this record looked from an object that is not a record of a retrieval
at all; distance_state separates an unmeasured distance from an
object the scale does not apply to, and a null distance is never a zero
— zero is the strongest claim the scale makes.
observed_at_read_from names the key on the emitting store the
day was read from, so a value can be checked against the store rather than
taken on trust.
The relation each value’s basis IS, and the relation its
does-not-claim sentence says it is NOT, are carried on every annotated term
of the observation spec as x-gxplex-prov-asserts and
x-gxplex-prov-does-not-assert, with
x-gxplex-prov-because beside them — in
gxp-obs-v1 for its own three groups and in
gxp-obs-v2 for those three and the two v2 adds. A validator ignores all three
annotations; a consumer reads them.
The prov: prefix on the values resolves to
http://www.w3.org/ns/prov#, which the document declares in its own
context key.
The relation is carried by GROUP rather than chosen per field, and that is a derivation rather than a judgment: a value’s basis is a property of the act that produced it, so every term produced by one act carries one relation. The table is recovered by grouping the spec’s own terms on that annotation, never from a list kept on this page.
| Terms | Asserts | Does not assert | Why |
|---|---|---|---|
| 21 | prov:wasAttributedTo | prov:wasDerivedFrom | a signal field carries what a publisher listed in its own words; it is attributed to that publisher and is not derived by this record |
| 28 | prov:wasGeneratedBy | prov:wasAttributedTo | an observation field records what this record's own collection pass produced at a time; it is not the publisher saying anything |
| 12 | prov:wasGeneratedBy | prov:wasAttributedTo | a query field records this record's own request and what it received, never a publisher's statement about the request |
| 4 | prov:wasGeneratedBy | prov:wasAttributedTo | a diagnostic field records how this record's reader behaved, which is a fact about the reader |
| 5 | prov:wasDerivedFrom | prov:wasAssociatedWith | an evidence state is derived by a published rule from the record's own fields; no person is associated with it unless a verification event says so |
Identifiers
Every tracked entry has a slug — eu-gmp-annex-11 — and that slug
is its identifier at /r/<id>/ and in every API
response. A published URL is treated as a citation here: it keeps resolving,
directly or by redirect, and scripts/test_url_stability.py
fails the build if one stops. When an entry changes kind — two moved from
instruments to bodies on 2026-08-02 — the old URL redirects rather than
disappearing.
The v1 contract
Additive only. A field is never removed and never re-typed;
a new field or a new endpoint may appear. A consumer that reads fields it
knows and ignores the rest will not break. Where a value's MEANING changes,
a correction is logged and the feed carries it —
that has happened twice, and both entries are public on
corrections. The third surface this sentence
used to name, /changelog, was retired on 2026-09-06 and 301s to
changes; nothing it carried about a document
left the site with it.
/api/v1/instruments.json ·
observations.json ·
citations.json ·
crawlability.json
The observation spec — GXP-OBS v1
GXP-OBS v1 states what an observation IS here: every
field, what it records, what a reader may not conclude from it, and what its
absence is. The machine-readable document is one JSON file at a versioned
address — /api/v1/schema/gxp-obs-v1.json
— carrying x-gxplex-does-not-claim and
x-gxplex-absent-means on every property. A validator ignores
both; a consumer reads them.
It is not the same document as
/api/v1/schema/series.json,
which is the JSON Schema for the same records and states shape alone. A
record can validate perfectly and still be read as a claim it does not make:
that a digest covers a document rather than a normalisation of one, that a
null digest is an empty document rather than an absent measurement, that a
run of unchanged digests is a document's history rather than one address's.
The spec is where those readings are refused.
A later spec version takes a later address and this one keeps resolving. The
spec version, the API contract version and a record's own
schema_version are three numbers that move separately.
/api/v1/series.jsonl —
the observation series: what a URL returned on a date. ·
probe.jsonl —
the probe matrix: how a named client was answered, which is transport
measurement rather than document observation.
The controlled topic identifiers, and the rule that forms them, are on conventions.
Model Context Protocol
A Model Context Protocol server is kept in this repository at
mcp/. It serves the same data as the JSON routes above and
exposes nothing they do not: search_sources,
get_source, get_history,
get_changes, get_change_evidence,
get_calendar, get_relations,
get_integrity, get_neighborhood and
get_signals. Every one is read-only.
It is documented here and is listed in no registry and submitted nowhere. Running it is a reader’s own act against a local checkout; this site serves no endpoint for it, and a rights field on an object reached through it means what it means on the same object reached through the JSON.
Three worked reads
Read-only, no key, no account. A consumer identifying itself honestly and staying under a request a second is asking of this record what this record asks of the sources it observes.
Changes since a date
# curl
curl -s 'https://gxplex.com/api/v1/events.json' | jq '[.items[] | select(.occurred_on >= "2026-09-01")]'
# python3 (standard library only)
import json, urllib.request
u = "https://gxplex.com/api/v1/events.json"
req = urllib.request.Request(u, headers={"User-Agent": "your-tool/1.0"})
doc = json.load(urllib.request.urlopen(req))
since = [e for e in doc["items"] if e["occurred_on"] >= "2026-09-01"]
// typescript
const r = await fetch("https://gxplex.com/api/v1/events.json");
const doc = await r.json();
const since = doc.items.filter((e: any) => e.occurred_on >= "2026-09-01");Sources by lens
A lens is the same object the board encodes in its own URL, so a view a person shares and a query a machine sends are one grammar.
# curl
curl -s 'https://gxplex.com/api/v1/instruments.json?lens=jurisdiction:EU'
# python3
import json, urllib.parse, urllib.request
q = urllib.parse.urlencode({"lens": "jurisdiction:EU"})
u = f"https://gxplex.com/api/v1/instruments.json?{q}"
doc = json.load(urllib.request.urlopen(
urllib.request.Request(u, headers={"User-Agent": "your-tool/1.0"})))
// typescript
const u = new URL("https://gxplex.com/api/v1/instruments.json");
u.searchParams.set("lens", "jurisdiction:EU");
const doc = await (await fetch(u)).json();Compare two dates
Rights-gated: where this record does not retain a document’s text, the response is a refusal naming the condition that refused it, never an empty difference. An empty difference and a comparison that could not be made are opposite facts.
# curl
curl -s 'https://gxplex.com/api/v1/compare.json' | jq '.items[] | select(.id == "eu-gmp-annex-11")'
# python3
import json, urllib.request
u = "https://gxplex.com/api/v1/compare.json"
doc = json.load(urllib.request.urlopen(
urllib.request.Request(u, headers={"User-Agent": "your-tool/1.0"})))
row = next(i for i in doc["items"] if i["id"] == "eu-gmp-annex-11")
// typescript
const doc = await (await fetch("https://gxplex.com/api/v1/compare.json")).json();
const row = doc.items.find((i: any) => i.id === "eu-gmp-annex-11");Cadence
Sources are fetched and hashed daily, and the hash is recorded whether or not anything changed — "unchanged for N days" is only a fact if every one of those days was observed. Statuses change only when a person confirms one against the primary source. There is no undertaking about when a run happens, and a day with no run is visible as a gap in the series rather than smoothed over.
This record is pull, not push. There are no webhooks, no alerts and no subscriptions that trigger on a change, and that is a boundary rather than an unbuilt feature: a subscription is a consumer record, and this record collects no consumer data of any kind — no accounts, no cookies, no client-side storage, no analytics. A surface that notified anyone would first have to hold who they are.
What is served instead is the whole series, on demand and without identification: the changes feed, the calendar, and the observation and instrument endpoints. Polling any of them returns the same bytes to everyone, and the record does not learn that it happened.
Citing a value
A value here is a reading of a source on a date, so a citation needs both:
GxPlex, "<short title>" (<instrument id>), status <status>
as recorded on <YYYY-MM-DD>. https://gxplex.com/r/<id>/Citing the status without the date cites a fact that has no owner: this record's claim is always about a day.
The word a citation calls a thing
A citation leaves this site inside somebody else’s document and is read there, years later, by somebody who will never open this record. Two things it must therefore not do: name a class this record chose, and carry an address that no longer resolves. The first is the harder one, because a class word reads as a fact and costs nothing to write.
So the rule is the publisher’s own word or none. A word travels when
three things hold together — the row stores it as the
publisher’s word, the publisher’s own banked span contains it,
and that span carries the address it was read at. The middle condition is
the one doing the work: a word stored beside a span that does not contain
it is a word vouched for by a neighbouring field. Where the three do not
hold, the citation reads [entry record] — square
brackets being where a bibliography already expects the citing
party’s own words rather than the cited party’s.
Every row, by the reason its word is its word
All 132 tracked rows, grouped by reason rather than by word: the words are two and the reasons are five. Every reason is listed with its count including the zeros, so a reason that found nothing can be told from a reason nobody wrote.
| Reason | Word | Rows | What it says |
|---|---|---|---|
publisher-word | the publisher’s | 0 of 132 | the publisher states this word for the thing, in a span this record banked with the address it was read at |
no-class-statement | [entry record] | 81 of 132 | this record holds no class statement from this publisher for this row, so no word of the publisher’s is available to travel |
class-word-is-this-records | [entry record] | 47 of 132 | the class this record holds for this row is a value from its own three-value field, reached from a publisher’s title word by a mapping in scripts/subjects.py; the publisher’s own word is not stored on the row, so no word of the publisher’s is available to travel |
designator-attested-no-class | [entry record] | 4 of 132 | this row is typed a document because a publisher’s page carries its designator; a designator attests which thing it is and states no class |
body-kind-not-inherited | [entry record] | 0 of 132 | a body states a word for what it is and this row is not among the rows that body lists as produced by it, so the word does not reach this row |
0 of 132 rows can
carry a publisher’s word today. That zero has a cause, and it
is not that publishers state nothing.
47 of 132 rows do store a class, in a field whose domain is three values declared
in src/content/config.ts. Those three values are reached from a
publisher’s title word by a mapping in
scripts/subjects.py, and that script records the
publisher’s own word beside them under an optional field the row
schema also declares. Measured over all 132 rows on this
build, the optional field is stored on
0 of those
47. The word is derived
nightly into a projection and dropped at the store.
A count, not a repair. The field that would carry the
publisher’s word lives under src/content/instruments/,
which this section does not write, and writing it is a read of a
publisher’s page rather than an edit to a component. What is built
here is the rule and the refusal. The count is what a repair would move.
The copy control on an entry page names its word through
citationKind() in src/lib/objectType.ts.
Asked the same 132 rows on this build, the two answers
differ on 51 of 132.
The provenance breadcrumb
A chain of addresses and acts. Each step names which of two parties acted — the publisher, or this record, and there is no third — what that party did, and where it can be seen. It says nothing about how good any of it is: a trail that graded its own evidence would be this record assessing itself.
Two rules hold it, and they are run on every build over the trails printed below. The structural one is the rule that holds: every step names one of the two parties and carries either an address or a stated reason there is none — measured this build, held for the parties and held for the addresses, over 16 steps in 3 trails. The second is a declared vocabulary of grading words, which returned 0 hits over the same steps. A declared vocabulary refuses the words on it and cannot refuse an assessment written without them. The rule that holds is structural: a step names one of two actors, what that party did, and where it can be seen.
Worked, on real rows
Derived, not chosen: for each reason that has members, the first row in identifier order whose last observation carries an address. Nothing selects for a digest — selecting for one would show only the citations that look complete, and the absence is the half more often met. 3 of 3 reasons with members are shown.
anvisa-ai — no-class-statement
Plain text
GxPlex. ANVISA positions on computerised systems and artificial intelligence [entry record]. Observed 25 September 2026. https://gxplex.com/r/anvisa-ai/
Provenance: Read from https://www.gov.br/anvisa/pt-br/assuntos/regulamentacao (answered) on 2026-09-25, record 1 of that day; no digest — no normalised text was retained for this read. Observation: https://gxplex.com/observations/anvisa-ai#obs-2026-09-25-1Provenance
1. The publisher answered at this address.
https://www.gov.br/anvisa/pt-br/assuntos/regulamentacao
2. This record read that address on 2026-09-25, record 1 of that day (HTTP 200, fetched browser, 433,346 bytes returned).
https://gxplex.com/observations/anvisa-ai#obs-2026-09-25-1
3. This record retained no normalised text for that read, so nothing was hashed and this citation asserts no digest.
(no address — the read itself is addressed above)
4. This record cites this row as [entry record] — this record holds no class statement from this publisher for this row, so no word of the publisher’s is available to travel.
(no address — no publisher span is held for this row)
5. This record publishes the entry at this address.
https://gxplex.com/r/anvisa-ai/BibTeX, APA and JSON for this row
BibTeX
@misc{gxplexranvisaai,
author = {{GxPlex}},
title = {{ANVISA positions on computerised systems and artificial intelligence}},
year = {2026},
note = {entry record, observed 25 September 2026},
howpublished = {\url{https://gxplex.com/r/anvisa-ai/}},
urldate = {2026-09-25},
annote = {Read from https://www.gov.br/anvisa/pt-br/assuntos/regulamentacao (answered) on 2026-09-25, record 1 of that day; no digest — no normalised text was retained for this read. Observation: https://gxplex.com/observations/anvisa-ai#obs-2026-09-25-1}
}APA
GxPlex. (2026). ANVISA positions on computerised systems and artificial intelligence [entry record]. Retrieved 25 September 2026, from https://gxplex.com/r/anvisa-ai/JSON
{
"@context": "https://schema.org",
"@type": "Dataset",
"name": "ANVISA positions on computerised systems and artificial intelligence",
"publisher": "GxPlex",
"url": "https://gxplex.com/r/anvisa-ai/",
"additionalTypeAbsent": "this record holds no class statement from this publisher for this row, so no word of the publisher’s is available to travel",
"temporalCoverage": "2026-09-25",
"observation": {
"url": "https://gxplex.com/observations/anvisa-ai#obs-2026-09-25-1",
"observedDate": "2026-09-25",
"sequence": 1,
"answeredUrl": "https://www.gov.br/anvisa/pt-br/assuntos/regulamentacao"
},
"isBasedOn": "https://www.gov.br/anvisa/pt-br/assuntos/regulamentacao"
}astm-e2363 — class-word-is-this-records
Plain text
GxPlex. ASTM E2363 — Terminology relating to process analytical technology [entry record]. Observed 25 September 2026. https://gxplex.com/r/astm-e2363/
Provenance: Read from https://store.astm.org/e2363-23.html (answered) on 2026-09-25, record 1 of that day; SHA-256 38fca073725a8580e33f943a252f9793b151822380f626e9ccddf52c47713913 of the normalised text. Observation: https://gxplex.com/observations/astm-e2363#obs-2026-09-25-1Provenance
1. The publisher answered at this address.
https://store.astm.org/e2363-23.html
2. This record read that address on 2026-09-25, record 1 of that day (HTTP 200, fetched direct, 301,853 bytes returned).
https://gxplex.com/observations/astm-e2363#obs-2026-09-25-1
3. This record hashed the normalised text of that read — SHA-256 38fca073725a8580e33f943a252f9793b151822380f626e9ccddf52c47713913. The digest is of the normalised text and not of the bytes served, so hashing the source as served will not reproduce it.
https://gxplex.com/verify/
4. The publisher states this, in its own words, at the address below: “ASTM E2363 — Terminology relating to process analytical technology”.
https://www.astm.org/e2363-23.html
5. This record cites this row as [entry record] — the class this record holds for this row is a value from its own three-value field, reached from a publisher’s title word by a mapping in scripts/subjects.py; the publisher’s own word is not stored on the row, so no word of the publisher’s is available to travel.
(no address — the publisher’s own span is the step above)
6. This record publishes the entry at this address.
https://gxplex.com/r/astm-e2363/BibTeX, APA and JSON for this row
BibTeX
@misc{gxplexrastme2363,
author = {{GxPlex}},
title = {{ASTM E2363 — Terminology relating to process analytical technology}},
year = {2026},
note = {entry record, observed 25 September 2026},
howpublished = {\url{https://gxplex.com/r/astm-e2363/}},
urldate = {2026-09-25},
annote = {Read from https://store.astm.org/e2363-23.html (answered) on 2026-09-25, record 1 of that day; SHA-256 38fca073725a8580e33f943a252f9793b151822380f626e9ccddf52c47713913 of the normalised text. Observation: https://gxplex.com/observations/astm-e2363#obs-2026-09-25-1}
}APA
GxPlex. (2026). ASTM E2363 — Terminology relating to process analytical technology [entry record]. Retrieved 25 September 2026, from https://gxplex.com/r/astm-e2363/JSON
{
"@context": "https://schema.org",
"@type": "Dataset",
"name": "ASTM E2363 — Terminology relating to process analytical technology",
"publisher": "GxPlex",
"url": "https://gxplex.com/r/astm-e2363/",
"additionalTypeAbsent": "the class this record holds for this row is a value from its own three-value field, reached from a publisher’s title word by a mapping in scripts/subjects.py; the publisher’s own word is not stored on the row, so no word of the publisher’s is available to travel",
"temporalCoverage": "2026-09-25",
"observation": {
"url": "https://gxplex.com/observations/astm-e2363#obs-2026-09-25-1",
"observedDate": "2026-09-25",
"sequence": 1,
"answeredUrl": "https://store.astm.org/e2363-23.html",
"sha256": "38fca073725a8580e33f943a252f9793b151822380f626e9ccddf52c47713913",
"verifiedAt": "https://gxplex.com/verify/"
},
"isBasedOn": "https://store.astm.org/e2363-23.html"
}fda-csa — designator-attested-no-class
Plain text
GxPlex. Computer Software Assurance for Production and Quality Management System Software [entry record]. Observed 25 September 2026. https://gxplex.com/r/fda-csa/
Provenance: Read from https://www.fda.gov/regulatory-information/search-fda-guidance-documents/computer-software-assurance-production-and-quality-management-system-software (answered) on 2026-09-25, record 1 of that day; SHA-256 4843091843f0704b36f6b3f40aaf76fc1a06f5866feaa0621f4572c3e879e3dd of the normalised text. Observation: https://gxplex.com/observations/fda-csa#obs-2026-09-25-1Provenance
1. The publisher answered at this address.
https://www.fda.gov/regulatory-information/search-fda-guidance-documents/computer-software-assurance-production-and-quality-management-system-software
2. This record read that address on 2026-09-25, record 1 of that day (HTTP 200, fetched direct, 35,471 bytes returned).
https://gxplex.com/observations/fda-csa#obs-2026-09-25-1
3. This record hashed the normalised text of that read — SHA-256 4843091843f0704b36f6b3f40aaf76fc1a06f5866feaa0621f4572c3e879e3dd. The digest is of the normalised text and not of the bytes served, so hashing the source as served will not reproduce it.
https://gxplex.com/verify/
4. This record cites this row as [entry record] — this row is typed a document because a publisher’s page carries its designator; a designator attests which thing it is and states no class.
(no address — no publisher span is held for this row)
5. This record publishes the entry at this address.
https://gxplex.com/r/fda-csa/BibTeX, APA and JSON for this row
BibTeX
@misc{gxplexrfdacsa,
author = {{GxPlex}},
title = {{Computer Software Assurance for Production and Quality Management System Software}},
year = {2026},
note = {entry record, observed 25 September 2026},
howpublished = {\url{https://gxplex.com/r/fda-csa/}},
urldate = {2026-09-25},
annote = {Read from https://www.fda.gov/regulatory-information/search-fda-guidance-documents/computer-software-assurance-production-and-quality-management-system-software (answered) on 2026-09-25, record 1 of that day; SHA-256 4843091843f0704b36f6b3f40aaf76fc1a06f5866feaa0621f4572c3e879e3dd of the normalised text. Observation: https://gxplex.com/observations/fda-csa#obs-2026-09-25-1}
}APA
GxPlex. (2026). Computer Software Assurance for Production and Quality Management System Software [entry record]. Retrieved 25 September 2026, from https://gxplex.com/r/fda-csa/JSON
{
"@context": "https://schema.org",
"@type": "Dataset",
"name": "Computer Software Assurance for Production and Quality Management System Software",
"publisher": "GxPlex",
"url": "https://gxplex.com/r/fda-csa/",
"additionalTypeAbsent": "this row is typed a document because a publisher’s page carries its designator; a designator attests which thing it is and states no class",
"temporalCoverage": "2026-09-25",
"observation": {
"url": "https://gxplex.com/observations/fda-csa#obs-2026-09-25-1",
"observedDate": "2026-09-25",
"sequence": 1,
"answeredUrl": "https://www.fda.gov/regulatory-information/search-fda-guidance-documents/computer-software-assurance-production-and-quality-management-system-software",
"sha256": "4843091843f0704b36f6b3f40aaf76fc1a06f5866feaa0621f4572c3e879e3dd",
"verifiedAt": "https://gxplex.com/verify/"
},
"isBasedOn": "https://www.fda.gov/regulatory-information/search-fda-guidance-documents/computer-software-assurance-production-and-quality-management-system-software"
}The form of an address
Every address in a citation is emitted in the form the page itself claims as its own. The two routes a citation names do not agree on that form: an entry address carries a trailing slash and an observation address carries none, because that is what each of those pages publishes as its canonical address. Neither is tidied here. A citation that normalised one of them would carry an address the page does not claim, and a citation is the one string nobody re-checks — it is read in a document where this record is not open.
The disagreement is a fact about this record rather than a defect of the
citation, and it is reported rather than resolved: resolving it means
editing whichever route claims the odd form, which is not a component of a
citation. scripts/plants/d/test_local_citation.py measures both
forms against the built pages on every run, so the rule above fails loudly
if either route moves.
The publisher spans printed in these trails are not asserted here
against the bytes they name. This site has a gate that takes a
borrowed span marked with its own class attribute and compares it to the
record field it came from, and that gate’s population is a list of
built directories that does not include this one. Marking the spans without
adding the directory would make the gate refuse the build; adding the
directory is an edit to scripts/quotefidelity.py, which this
section does not write. So the gap is stated rather than cleared: on entry
pages these same spans are marked and asserted, and here they are neither.
No request leaves the page for any of this. The formats are in the HTML as served, the copy controls use the clipboard of the device they are pressed on, and nothing is written to that device’s storage at any point.
Checking a retrieved claim
- Each observation carries a SHA-256 of the normalised text of the source
on that day — in
observations.jsonand on the instrument page. - Fetch the source yourself and compare, or compare against the retained snapshot where one exists.
- Recompute the day's integrity root from the published leaves and check it against the published root: the command is on /verify, and it is run against the shipping bytes on every build.
These are hashes, not signatures. They prove the published data is the data the root was built from. They do not prove who published it, because no signing key exists yet — a fact /verify states in its own words. When one does, the manifest carries a detached signature and this step becomes a signed hash. A consumer describing values from here as cryptographically attributable would, today, be claiming something this record does not yet support.
Rights fields — handling semantics
The four fields below state how this record HANDLES a document’s text: whether prose is held here, whether any of it appears here, whether a retrieval is retained here, and which of the two licence positions the row was read into. None of them is a grant, none is a reading of what a publisher permits of anyone else, and none travels with a copy taken from here. A pipeline that reads them inherits this record’s boundary; it does not acquire a permission.
Every row carries four rights fields, so a pipeline inherits the boundary
instead of discovering it: licence_class,
text_retained, quote_allowed and
snapshot_available. Where a row names no tracked entry all four
are null and the row says why — four nulls record the absence of
a reading, never a reading that came back negative.
The two retention fields are not the permission field.
Retention is not publication: this record retains normalised prose for
documents whose text it never publishes, and on
32 of
132 tracked entries
text_retained is true while quote_allowed is
false. A consumer reading the first alone and inferring a quotation was
available would be wrong on exactly those.
quote_allowed records whether a quotation from the document
appears in this record's own published output. It is a fact about this
record's handling, not a reading of what a publisher permits, and it is not
a grant: treating it as permission to quote elsewhere adds a claim this
record did not make.
| Field | Count | Of | What the count is |
|---|---|---|---|
quote_allowed true | 99 | 132 | Entries whose text this record quotes, each quotation citing the surface it was read from. |
quote_allowed false | 33 | 132 | Entries whose text appears nowhere here. Metadata, dates, a digest and a link to the publisher. |
text_retained true | 126 | 132 | Entries holding normalised prose a quotation can be checked against here. Held, never served. |
snapshot_available true | 126 | 132 | Entries holding a retained snapshot of a retrieval, so a digest can be re-checked here. No snapshot is served. |
| snapshot without prose | 0 | 132 | Entries where a snapshot is held and no prose came out of it. The two retention fields agree today and are two measurements; this cell is where they would part. |
| no reading recorded | 0 | 132 | Entries carrying four nulls. An absent reading, never a reading that came back negative. |
Every row also carries robots_policy: how this archive's fetch of
that row was permitted. Absent means the ordinary case — a robots file was
read and allows the path. A non-default value names the state and its
receipt, including absent-by-confirmation, where a host
publishes no robots file at all and the page's own meta-robots was read
before its content was believed.
Every row also carries record_tier. It records what this
archive undertakes for a class of entries and asserts nothing about what has
happened to any one of them: for core, this archive undertakes
to put an entry's status to a person; for register, no
person-verification is promised. Neither value is a ranking, and neither is
derived from an entry's content: register is written on the
row, and core is what the schema records where a row writes no
value. What happened to a row is in that row's own fields and its
observation records. This endpoint emits last_verified,
first_observed, checked_by and
check_date.
checked_by is the ACT, not a rank: human where a
person confirmed the status, machine where a tier method read
it. check_date is the day that act was recorded. Neither is a
status and neither is a verification — a row whose status is
unknown still carries them, and machine must not
be read as a person's confirmation.
first_observed is the earliest observation date in THAT ROW's
own series — a fact about this archive's window on the row, not about the
document. It is null where a row holds no observation yet,
which is a real state between admission and first fetch, and it is never
filled from the corpus start date: a row is not observed by its
neighbours.
| Value | Count | What it means here |
|---|---|---|
open, text retained | 81 | A snapshot and a hash exist. Quotations can be checked against retained bytes. |
restricted | 33 | The document's text is not reproduced or retained here, and none is available through this record. Metadata and a link only — a consumer that generates text for these is not quoting this archive. |
text_retained: false | 48 | Nothing was hashed, so there is nothing here to verify a quotation against. The entry states why on its own page. |
What this record warrants, and what it does not
Integrity is provable. That the published data is the data the root was built from, and that a document's text on a given day hashed to a given value, are things a consumer can check without trusting this site — the arithmetic is published and so are the inputs.
Accuracy is not a cryptographic property. A status read wrongly hashes exactly as well as one read correctly. Accuracy lives in the reading, in the human check, and in the corrections log, which is additive and never silently edits. "Verified" carries exactly one sense here — checked against the primary source by a person, on a stated date — and it is the only sense this record can support for a value taken from it.
This record makes no assessment of what any document requires of anyone. A consumer that presents its contents as a compliance obligation has added a claim that is not here.
Found something wrong? corrections@gxplex.com. Method: how this record is maintained.
The full record
These are the surfaces a claim on this site can be checked against. They are live and permanent, and each keeps its address.
- Full internal log — the 120 entries this record no
longer publishes to readers (counting rules, template repairs, figures,
quoted spans, gate work) are retained in the repository history, in the
commit that wrote each one. There is no machine endpoint for them: no
API response has ever carried the changelog, so removing the page breaks
no contract.
git log --follow src/content/changelog/reads the whole of it. - Corrections — every correction to a published claim about a document, dated, with what it corrected
- Integrity record — the Merkle root, the timestamps, and the commands that check them without anything of ours
- Archive index — every recorded document version, with its digest
- Sources — every address this record reads
Verifying the record yourself
Carried here from the retired /verify page on 2026-09-05. These
are the three commands that check this record without trusting it: recompute
the Merkle root from the published leaves, check the timestamp authority's
signature over that root, and check the OpenTimestamps attestation. Each
needs the published files and nothing from this site's code.
The integrity record
- Leaves in the tree
- 6086
- Fixed at
- 00:00:00Z — the day is this record's unit, so the time is a constant, not a clock reading
The root is a SHA-256 Merkle tree over sorted
id|date|content_hash lines. An odd node at the end of a level
is promoted, never paired with itself.
Read from state/integrity.json, written by
scripts/integrity.py at build time.
1840935ba9a24c0ffbf8fdc75b900908b51455863da9ebbc4fb333b3998563c4
The lines are distinct: 12646 records in
the series carry both a hash and a date, and they reduce to 6086 lines,
because 6560 of them repeat an
id|date|content_hash a line already covers. A source
re-fetched on a day it has already been fetched, returning the same
bytes, adds no line — there is no further state to fix in time.
What is in the file, and what is not. The first field is the id an observation was recorded under. Of the 132 instrument rows this record publishes, 126 contribute at least one line and 6 contribute none: a line stands for retained bytes. All 6 have been observed, and no observation of them has retained content.
A further 5 ids in the file account for 297 lines: sources this record observes without publishing an instrument row. The file is a record of observations that retained content, so it is neither a list of this site’s pages nor a census of the rows it publishes.
Recompute the root from the leaves
python3 - <<'EOF'
import hashlib
L = [l.strip() for l in open('leaves.txt') if l.strip()]
h = [hashlib.sha256(x.encode()).hexdigest() for x in L]
while len(h) > 1:
n = [hashlib.sha256((h[i] + h[i+1]).encode()).hexdigest()
for i in range(0, len(h) - 1, 2)]
if len(h) % 2:
n.append(h[-1])
h = n
print(h[0])
EOFIt prints the published root, or one of us is wrong — and the published data is the evidence for which, not this sentence.
Check the timestamp authority's signature
Authority time: . The token records the authority's exact instant, to the second. This page shows the date; the instant is in the token, and anyone verifying the proof reads it there. Nothing is withheld — the artifact is published unchanged, and the command below prints the time it carries.
openssl ts -verify -data root.txt -in root.tsr \
-CAfile tsa-cacert.pem -untrusted tsa.crtGranularity. One stamp per daily cycle, taken by the scheduled daily job and by nothing else — never per commit, never per working session. A completed proof names a Bitcoin block, and block times are public, so a proof discloses roughly when its stamp was taken. What that discloses here is the schedule, which is published on this site anyway. Everything this record states about its own activity is day-granular by rule.
Anchoring proves WHEN, not WHO. Both anchors here fix digests at times. Neither says who assembled them, whether the documents behind them were read correctly, or whether any person checked anything. The named-person verification this record describes elsewhere is a separate act, and it is still owed — no quantity of cryptography here discharges it.
Check the OpenTimestamps attestation
pip install opentimestamps-client==0.7.2
curl -O https://gxplex.com/integrity/root.txt
curl -O https://gxplex.com/integrity/root.txt.ots
ots verify root.txt.otsThis build does not run these lines. They install a client from PyPI, fetch this site over the network, and ask a Bitcoin block explorer for a block header — none of which a build of this site does.
Where each published reason is read from
Every reason the site gives for not tracking a document's text is read from a named field on that document's own record. The reader-facing pages give the reason in plain words; this is the field behind each, so a count can be checked against the store rather than taken on trust.
- text behind a licence
- Read from:
frozen_reason: paywalled_stage_tracked - published only within a listing
- Read from:
frozen_reason: listing_only - no public document
- Read from:
frozen_reason: no_public_document - the site refuses automated requests
- Read from:
frozen_reason: access_blocked - a register, which carries no status
- Read from:
frozen_reason: register_no_status
A shelf this browser keeps
Two controls below write to this browser’s own local storage.
Nothing written there reaches a server: this site serves identical bytes
to every visitor, and there are no accounts and no cookies. The two
scripts this page loads declare no transport of any kind — nowhere
to send, no socket, no tracking pixel and no cookie — and that is a
property scripts/formcheck.py measures on the built artifact
rather than one this page asserts about itself. The client that runs it
is /js/local-shelf.js, and
its contract — the keys, the namespace, the figures and the query
separators — is declared once in
src/lib/localShelf.ts and read by the browser from a block
in this page, so no key is spelled in two places.
Nothing is written until one of those controls is pressed. A visit that presses nothing leaves this device exactly as it found it — no kept page, no figures, and no record that the visit happened.
With scripting off the controls stay hidden and nothing is stored. A control that would do nothing is worse than an absent one: it reports a capability the page does not have.
Kept pages, and a lens over them
The lens narrows the kept list by 2 facets — section of the site, and day it was kept — and the values
under each are derived from the kept items themselves rather than from a
vocabulary this page holds, so a list with nothing in it offers no
chips at all. Each facet is declared once in
src/lib/localShelf.ts and read by the browser out of the
declaration block at the foot of this section, which carries the field
of a kept item its value is read from: the client spells no facet name
of its own, so a facet renamed in one place cannot go on narrowing by a
field nothing carries. The composed query is printed in full beside the
count it is showing and the size of the list it counted over. It is never written
to the address bar: a lens over a list only this browser holds could not
be opened by anyone it was sent to, which is the opposite of the
board’s lens and the reason that one lives in the URL.
Every chip carries a number, and it is derived at each render. The number on a chip is the number of kept pages that would be shown if it were pressed — not how many carry that value. The two differ whenever another chip is already narrowing, and the same function derives both the number and the list, so a chip cannot promise a count the list then fails to deliver. A chip reading 0 stays on the page. It means no kept page carries that value together with what is already narrowing — which is a fact worth seeing, and one a chip that quietly disappeared would have hidden.
Every chip carries a badge naming where its value came from. The question a badge answers is where the value came from — not who thought the facet worth having. This record refuses a facet built from a seed list on the ground that such a facet is a fact about the seed list; the badge is that same finding put where a reader can see it rather than where an auditor can.
- section of the site — this record’s derivation.
A value under it is the first segment of the address, which is a fact about a URL this site publishes. The badge says the value is derived by this record from something it publishes — here, the first segment of an address this site chose for itself, so the word is this record’s and not a publisher’s. The keep control takes it
from
page. - day it was kept — this device.
A value under it is the day the keep control ran, read from this device’s clock. The badge says the value was written on this device by an act of the reader, from this device’s own clock. This record has never seen the value: it is in no build, on no server, and in nothing published here. The keep control takes it
from
device-clock.
The badge is printed once per facet, and it governs every chip under it. Every value a facet offers comes from that facet’s one source, so a badge repeated on each chip would print one fact as many times as there are values. It is repeated in one place that is not visible: each chip’s accessible name carries it, because a reader who reaches a single chip through a list of controls never sees the heading it sits under, and for them a badge printed once is a badge that is not there.
The source named on each line above is what makes the badge a claim rather than a decoration. A facet badged one way whose value the keep control lifted from somewhere else would publish a false provenance in this record’s own voice, and it would read exactly like a true one — which is what a claim nothing holds to always looks like. The binding is data rather than prose, and the lane’s plant refuses a facet that breaks it.
One class has no member here, and that is stated rather than
left silent. Nothing under this lens can be badged a publisher’s word: no facet over a kept list can carry one. A kept item holds an address, the section of this site that address sits in, and the day it was kept — and no publisher printed any of the three. The binding that class would need has no
source at all — null
— so a facet claiming it could not be written. A reader shown two
badges and no third has learned nothing about the missing one, which is
why the absence is published as the fact it is.
The classes offered were two, and this lens needs three. A publisher’s own word, or this record’s derivation — and the day a page was kept is neither. The badge on that chip states the third case in its own words, and this paragraph does not repeat them: two spellings of one statement on one page is a sentence nobody wrote, which is what a seam is. Filing the value under this record’s derivation would be claiming to have produced something this record cannot read. An enumerated set of options is a hypothesis about the world rather than a partition of it, and the branch every menu leaves unstated is that reality returned something else.
Each shown row carries its own account of why it appeared, and every value in that account is read off the row rather than off the term that matched it. The two are equal for a matched row, which is exactly what makes the wrong one safe to write and impossible to notice: a reason taken from the term goes on printing after any change that lets an unmatched row through. With nothing pressed the account is still there and it states the fact — no chip is pressed, so nothing narrowed this list and this row was not selected for any reason — because a row with no account looks like a row whose account failed to render.
Since the figures kept on this device
Every figure below is published elsewhere on this site and the address beside it is where. That is what makes a device-local diff harmless: both sides are public numbers — the ones this build rendered, and a copy of the same ones as they stood on the day they were kept — and nothing about the reader is on either side. A figure that is zero in this build is left out, because a kept zero cannot tell a collection that filled from a build that started reading it.
- 132 tracked entries —
/ - 55 statuses a person has checked —
/ - 203 published corrections —
/corrections - 27 published findings —
/findings
What is stored, key by key
gxplex-kept- each address kept from this site, with the page title it carried and the day it was kept. Written by the keep control on Developers, and by nothing else.
gxplex-figures- a copy of the figures this page publishes, as they stood on the day they were kept. Written by the control that keeps the figures on Developers, and by nothing else.
gxplex-density- the row height the board is shown at, as one of two words: compact or comfortable. Written by the compact-rows control on the board, and by nothing else.
gxplex-orientation- one word, recording that the orientation above the board was dismissed on this device; no time and no page. Written by the orientation’s dismiss control on the board, and by nothing else.
Forget this device removes every key beginning gxplex-, which is the namespace everything this
site stores shares. It sweeps the namespace rather than a list of keys,
so a key added later is cleared by a control written before it existed.
A key under another namespace is not this site’s to remove and is
left alone.
There is no automatic list of recently viewed pages. The ordinary way to build one writes the current address on every page load, and that is a visit record however local it stays — the first ratified condition on device-local storage is that a passive visit stores nothing, a visit timestamp included. The kept list above is the same list assembled by an act instead: it holds what was kept, and nothing that was merely read.
The fourth condition is carried. The ratification
requires that /how/ carry
one sentence stating the mechanism. That sentence is composed from the
same declaration as the list above, so every key listed here is named
there with the control that writes it and the page that control is on.
Before the correction dated , it named two controls on the board — compact rows, and a list of regulations — of which only the first writes anything in this build, and it did not name this shelf; this
paragraph said so until the sentence changed.
An exact-match index this page carries
The index below is in this page. It is not fetched, and there is nothing to fetch: it is written into the document at build time, so searching it makes no request of any kind — not to this site, and not to anywhere else. Typing in the box moves no bytes.
A query matches a span exactly or it does not match. There is no stemming here, no synonym list, no embedding and no ranking. Each of those would be a claim about what a query meant, and a record that reports what documents say cannot also decide which words are close enough to which others. When nothing matches, nothing is offered: the empty result is the answer, and the nearest row would be a different claim rather than a partial version of that one.
Case is the one normalisation admitted, and it runs as a second pass whose hits are labelled as case-folded where they appear. A normalised read diagnoses why a strict read failed; it never stands in for the strict verdict. A span whose folded form is a different length from its own — a few characters fold that way — reports no folded hit at all, because the offsets would then name the wrong characters.
What is in it, and what each part is cited to
Two populations, and which one a span belongs to is read off the store rather than typed per row. A span sits in the publisher class when the record banked it together with the URL where the publisher said it; every other span is text this record publishes on a page of its own, cited to that page.
- 140 spans cited to a publisher, out of 1012 spans in the index — a span this record banked together with the publisher URL where it was read.
- 872 spans cited to a page of this site, out of the same 1012 — text this record publishes on a page of its own, cited to that page.
- 191 rows carry those spans, across 4 result types: 132 × a tracked entry, 26 × a subdivision of a tracked entry, 27 × a published finding about this record, 6 × an organisation that produces documents.
- 3 publisher sentences the record holds are not in this index, and the omission is measured rather than silent: each sits beside prose naming where it was read instead of a URL resolving there, so no citation this index could produce would be a publisher’s address.
The class names the citation, never the authorship. A document title is a publisher’s own words printed on a page of this site, and it sits in the page class — not because this record wrote it, but because no stored URL says where the publisher printed it, so the page is the only citation this index can honestly produce for it. Claiming less is the safe direction; the reverse would put a page of this site forward as a publisher’s surface.
Why a result matched
Every result names the field its match was found in, shows the window the match sits inside with the matched characters marked, says whether the match was byte for byte or needed case folded, and prints the address the span is cited to. All of it is derived from the index, and none of it places one result above another: no ordering or score here could support such a sentence.
A shown window is cut to 14 words — the ceiling every quote on this site is held to — and it is centred on what matched rather than taken from the start of the field, so the word that justified showing the span is inside it. Both ends land on whole words. Where the matched text is itself longer than the ceiling the window is withheld with that as its stated reason, because a span cut mid-word is a fabricated measurement of what was written. Where a window elides, it says how many words it is showing out of how many the field holds.
Search
The box is assembled in this browser and is not in the HTML. With scripting off it is absent rather than present and inert: a control that reports a capability the page does not have is worse than no control. Nothing on this page is reachable only through it.
Pressing / anywhere outside a text field opens the same index in a panel over the page, and Escape closes it. It is the same index, the same matching and the same result rows — a second surface reading a second index is two answers waiting to disagree.
What this index does not hold. It carries the spans counted above and nothing else: no primary source text, no body of any document, and no page of this site beyond the fields listed in each result. A publisher’s wording that this record has not banked finds nothing here, and that is a fact about what has been collected rather than about what the publisher wrote.
This box is on this page only. Mounting it site-wide would edit the shared layout, which is outside the path set this section was built under, so it is recorded here rather than done quietly. The older box on the tracker is a different instrument on a different index and is untouched by any of this.
Last updated: