GxPlex

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

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

# 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 -3

3 · 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.

/api/v1/revisions.json

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

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.

What a value in each group asserts, and what it does not. Read from the v2 spec document: 70 of its 70 declared terms carry the annotation and fall into 5 groups; 0 carry none and are outside this table.
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.

ReasonWordRowsWhat 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-1

Provenance

  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-1

Provenance

  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-1

Provenance

  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

  1. Each observation carries a SHA-256 of the normalised text of the source on that day — in observations.json and on the instrument page.
  2. Fetch the source yourself and compare, or compare against the retained snapshot where one exists.
  3. 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.

The four rights fields over the tracked entries
FieldCount OfWhat 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.

What each licence position permits a consumer
ValueCountWhat 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.

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])
EOF

It 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.crt

Granularity. 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.ots

This 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.

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.

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.

Last updated: