Skip to content
Cabuya

Checks

View the Markdown

Every check the validator can report, with a stable anchor. Error messages link here, so these URLs do not move.

62 checks catalogued · 42 implemented

Discovery

DSC001errorL1Catalogued, not yet implemented — Task 15 (behavioral probes)

Manifest reachable over HTTPS as JSON

Rule
The manifest MUST be served over HTTPS with Content-Type: application/json.
Fix
Serve the manifest over HTTPS with a JSON content type, and check it from outside your network.

Specification

DSC002errorL1Implemented

Soft-404: a discovery path answering 200 + text/html is absent

Rule
A manifest is JSON. HTML at a discovery path means an SPA catch-all is answering — the manifest is absent, not present.
Fix
Exclude the discovery path from your catch-all so the file is served instead of your app shell.

Specification

DSC003warningL1Catalogued, not yet implemented — Task 15

robots.txt returns 200 text/plain

Rule
L2+ preconditions include a real robots.txt.
Fix
Serve a real robots.txt at the root with a text/plain content type.

Specification

DSC004warningL1Catalogued, not yet implemented — Task 15

Manifest at the RECOMMENDED path, or a <link rel> advertisement

Rule
The manifest SHOULD live at /.well-known/cabuya.json, or be advertised with <link rel="cabuya">.
Fix
Move the manifest to /.well-known/cabuya.json, or advertise its location with a <link rel="cabuya-manifest">.

Specification

DSC005errorL1Implemented

Manifest validates against manifest.schema.json

Rule
The manifest MUST conform to its schema.
Fix
Correct the manifest against the published schema; the finding names the failing property.

Specification

DSC006errorL1Catalogued, not yet implemented — Task 15

publisher.canonical_url matches the registry entry

Rule
A registered publisher’s manifest MUST agree with its registry entry about its canonical URL.
Fix
Make the canonical URL in the manifest match the one in your registry entry, or open a pull request to change the entry.

Specification

DSC007errorL1Catalogued, not yet implemented — Task 15

Every feeds[].url is absolute HTTPS and reachable

Rule
Declared feeds MUST resolve.
Fix
Give every feed an absolute https URL that resolves from outside your network.

Specification

DSC008warningL1Catalogued, not yet implemented — Task 15

crawl_policy_url resolves

Rule
The declared crawl/reuse policy SHOULD be fetchable — consumers must honor it.
Fix
Point crawl_policy_url at a page that exists, or remove the field.

Specification

DSC009errorL1Catalogued, not yet implemented — Task 13 (content side) + Task 15

conformance_target does not exceed the measured level

Rule
conformance_target is a declaration; it MUST NOT claim more than this run measures. Reported as a mismatch, never as the level.
Fix
Lower conformance_target to the level you actually reach, or fix what is blocking the level you claim. The target is an intention, not a claim.

Specification

Envelope

SCH001errorL2Implemented

The document does not conform to its published JSON Schema

Rule
Every feed and manifest MUST validate against its versioned schema.
Fix
Correct the value so it satisfies the schema. The finding gives the pointer and the expected shape.

Specification

ENV001errorL2Implemented

Envelope required fields present and well-typed

Rule
last_updated, ttl, version, publisher_id and license are REQUIRED on every feed envelope.
Fix
Add the missing envelope field. All five are required on every feed.

Specification

ENV002errorL2Implemented

last_updated is RFC 3339 with a UTC offset

Rule
The generation timestamp MUST be unambiguous.
Fix
Write last_updated as an RFC 3339 timestamp with an explicit UTC offset, e.g. 2026-08-17T14:02:00Z.

Specification

ENV003errorL2Implemented

license present

Rule
An unlicensed feed does not conform — absence blocks every consumer’s legal review.
Fix
Declare a licence. An unlicensed feed blocks every consumer’s legal review, which is a harder problem than a missing field.

Specification

ENV004warningL2Implemented

license is an SPDX id, or license_url accompanies it

Rule
A machine-resolvable licence SHOULD be used.
Fix
Use an SPDX identifier, or keep the custom string and add license_url pointing at the terms.

Specification

ENV005warningL2Implemented

permitted_use present, values within the closed enum

Rule
Consent-to-reuse travels in the envelope: display | aggregate | redistribute | ai_answer | ai_train.
Fix
Add permitted_use with values from the closed enum: display, aggregate, redistribute.

Specification

ENV006errorL2Implemented

version is a supported spec version

Rule
Supported versions span at most two MAJORs.
Fix
Set version to a supported spec version. Consumers use it to decide how to read the rest.

Specification

ENV007errorL2Implemented

Access-Control-Allow-Origin: * present

Rule
The one non-obvious MUST: without it every browser-based consumer needs a proxy.
Fix
Send Access-Control-Allow-Origin: * on the feed. Without it a browser client cannot read you at all.

Specification

ENV008warningL2Implemented

ttl is a positive, plausible integer (1–86400)

Rule
ttl is the caching contract; implausible values break polling consumers.
Fix
Set ttl to a positive number of seconds between 1 and 86400. It is the caching contract, not a hint.

Specification

ENV009warningL2Implemented

Feed ≤ 5 MB and ≤ 10 000 records, or shards declared

Rule
Beyond the size guidance, publishers SHOULD shard by municipality and declare shards in the manifest.
Fix
Shard the feed and declare the shards, or reduce it below 5 MB and 10 000 records.

Specification

ENV010infoL2Implemented

Content-Type: application/json, UTF-8

Rule
Transport hygiene.
Fix
Serve the feed as application/json with UTF-8. A text/plain feed is one many clients will refuse.

Specification

Record

REC001errorL2Implemented

last_confirmed_at KEY present on every record (null is legal)

Rule
The confirmation key is REQUIRED; null is the honest "never confirmed". Omission is not.
Fix
Include last_confirmed_at on every record. If nobody has confirmed it, publish null — that is the honest answer and consumers know what to do with it. Never invent one.

Specification

REC002errorL2Implemented

id matches the {publisher_id}:{local_id} shape

Rule
Record identity is {publisher_id}:{local_id} — globally unique with zero coordination.
Fix
Use the {publisher_id}:{local_id} form for the record id, with your own publisher id.

Specification

REC003errorL2Implemented

No minting in another publisher’s namespace

Rule
A publisher MUST NOT mint ids in another publisher’s namespace.
Fix
Mint ids only in your own namespace. Reference another publisher’s record with same_as instead.

Specification

REC004errorL2Implemented

Locator rule: address_text OR lat+lon present

Rule
A place you cannot locate directs no one.
Fix
Give the record an address_text, or a lat and lon pair, or both. A place nobody can find is not a place.

Specification

REC005warningL2Implemented

Both locators present (RECOMMENDED)

Rule
Address and coordinates together survive more consumer contexts than either alone.
Fix
Add the second locator. Coordinates and an address answer different questions for a person on the ground.

Specification

REC006errorL2Implemented

public_url present and absolute

Rule
Link-out is the contact mechanism — contact values never travel.
Fix
Add an absolute public_url pointing at your own page for this place, so a consumer can hand the reader back to you.

Specification

REC007errorL2Implemented

place_kind within the enum

Rule
The shared vocabulary is what makes crosswalks possible; unknown kinds use other + a namespaced extension.
Fix
Use a place_kind from the enum, and put your own vocabulary in place_kind_ext alongside it.

Specification

REC008warningL2Implemented

municipality_code is a valid DIVIPOLA code

Rule
Territorial coding is DIVIPOLA; publishers keep their raw string in municipality_text.
Fix
Use the official DIVIPOLA code for the municipality. The shape is five digits.

Specification

REC009errorL2Implemented

source{} present with source_id

Rule
Provenance is structured, never prose — attribution and chains depend on it.
Fix
Add source with a source_id, so attribution survives every hop the record makes.

Specification

REC010errorL2Implemented

CR-2: name contains no operational-state token

Rule
Names MUST NOT encode operational state — state belongs in lifecycle_status / service_status.
Fix
Take the operational state out of the name and put it in service_status. A name that says CLOSED is a name that is wrong the moment it reopens.

Specification

REC011warningL2Implemented

name and status fields do not contradict each other

Rule
A record that says two things about its own state is a record a consumer cannot render honestly.
Fix
Reconcile the name and the status fields. When they disagree a consumer has to guess, and it will guess wrong half the time.

Specification

REC012errorL2Implemented

CR-1: updated_at is not a reused last_confirmed_at

Rule
An edit is not a confirmation; the two timestamps do not interconvert.
Fix
Set updated_at to when the row changed and last_confirmed_at to when a human verified it. Reusing one for the other tells consumers a place was checked when it was only edited.

Specification

REC013warningL2Implemented

expires_at set on inherently temporary place kinds

Rule
Temporary places SHOULD declare when they stop being true.
Fix
Set expires_at on a place that is temporary by nature, so consumers can stop showing it without asking you.

Specification

REC014warningL2Implemented

same_as entries are fully-qualified and one-hop

Rule
same_as is a one-hop, non-transitive claim — never a transitive chain.
Fix
Write same_as entries as fully-qualified {publisher_id}:{id} references, one hop only, and never as an authority claim.

Specification

REC015errorL2Implemented

Unknown members are preserved, never rejected

Rule
Extensibility applies to the validator first: an unknown member MUST NOT fail validation.
Fix
Preserve unknown members rather than dropping them. A field you do not understand may be the one another consumer needs.

Specification

REC016warningL2Implemented

x_ extensions are namespaced x_{publisher}_{field}

Rule
Namespaced extensions prevent two publishers colliding on one private field name.
Fix
Namespace the extension as x_{publisher}_{field}, so two publishers can extend the same record without colliding.

Specification

REC017warningL2Implemented

es baseline present for localized strings

Rule
es is the REQUIRED baseline for human-readable strings; en is RECOMMENDED.
Fix
Provide the es baseline for the localized string. Spanish is the floor every consumer can rely on.

Specification

REC018errorL2Implemented

No duplicate id within one feed

Rule
Two records with one id make every downstream dedupe wrong.
Fix
Remove the duplicate id. Two records with one id make every consumer’s merge non-deterministic.

Specification

Person-level data

PII001errorL2Implemented

A contact value appears in any field, extensions included

Rule
Contact values MUST NOT travel in feeds — namespaced extensions do not exempt them.
Fix
Remove the contact value. Use contact_available to say that contact exists, and public_url to send the reader to you for it.

Specification

PII002errorL2Implemented

confirmed_by is a role token, never a personal name

Rule
confirmed_by ∈ team | volunteer | official_source | partner:{publisher_id}.
Fix
Replace the personal name in confirmed_by with a role token: team, volunteer, official_source, or partner:{publisher_id}.

Specification

PII003errorL2Implemented

Free text matches a personal-data pattern

Rule
Free text is the third leak channel: publishers MUST strip personal data from description / warning_text.
Fix
Remove the personal data from the free text. The finding names the field and the pattern class, never the value it matched.

Specification

PII004errorL2Implemented

A person-level entity appears

Rule
Person-level data never federates — this is a join prohibition, not a field omission.
Fix
Remove the person-level entity. Cabuya carries places; a person’s situation stays inside the application that owns it.

Specification

PII005warningL2Implemented

A field name matches the deny-list even if the value looks clean

Rule
A field named for contact data will eventually carry it.
Fix
Rename the field. A deny-listed name signals person-level data even when today’s values look clean, and the next row may not be.

Specification

PII006errorL2Implemented

A moderation verdict about a third party is republished

Rule
Moderation verdicts do not federate; suppressed records are omitted, never labelled downstream.
Fix
Remove the moderation verdict. Republishing a judgement about a third party spreads it beyond anyone who can correct it.

Specification

Behaviour

BEH001errorL2Implemented

Feed reachable on two probes; content-type stable

Rule
A feed that answers differently on two probes cannot be consumed reliably.
Fix
Serve the feed consistently. Two probes returning different content types means a consumer cannot cache you at all.

Specification

BEH002errorL2Implemented

Always-now: last_updated advances with the probe clock on identical content

Rule
last_updated MUST be generated at build/publish time, never per request — a per-request timestamp is worse than no signal.
Fix
Set last_updated to when the content actually changed. A timestamp that follows the clock makes every consumer refetch identical bytes forever.

Specification

BEH003warningL2Implemented

last_updated older than 7 × ttl (the stale badge state)

Rule
Staleness is information, not failure — but it must be visible.
Fix
Regenerate the feed, or raise ttl to match how often you really update. Stale beyond seven times your own ttl is a promise you are not keeping.

Specification

BEH004warningL2Catalogued, not yet implemented — Task 15

Per-shard lastmod present (the incremental-sync pattern)

Rule
A per-shard lastmod is working incremental sync at zero cost.
Fix
Publish a lastmod per shard so consumers can fetch only what changed.

Specification

BEH005errorL2Catalogued, not yet implemented — Task 15

Declared shards reachable and envelope-consistent

Rule
A declared shard that disagrees with its siblings breaks every consumer that trusts the manifest.
Fix
Make every declared shard reachable and consistent with the envelope that declares it.

Specification

Licensing

LIC001warningL2Implemented

Declared licence is not share-alike

Rule
Share-alike licences poison aggregation for downstream consumers.
Fix
Choose a licence that is not share-alike. A viral licence makes every aggregator’s whole dataset derivative, which is why most will not read you.

Specification

LIC002infoL2Implemented

attribution string present for aggregators to display

Rule
Attribution is a consumption MUST; an explicit string makes it easy to honor.
Fix
Add an attribution string for aggregators to display. Credit that travels with the data is the point of the network.

Specification

Read API

API001errorL3Catalogued, not yet implemented — v0.2 (L3 checks — deferred from the walking skeleton)

Read API base reachable; envelope shape identical to the feed

Rule
One schema, four transports.
Fix
Serve the read API at the declared base with the same envelope shape as the static feed.

Specification

API002errorL3Catalogued, not yet implemented — Task 15

Static ≡ API: the same record is byte-compatible from both surfaces

Rule
The equivalence rule is what lets one schema serve four transports.
Fix
Return byte-compatible records from the API and the feed. A consumer must not have to know which one it read.

Specification

API003errorL3Catalogued, not yet implemented — v0.2

cursor pagination ordered on a server sequence, not a timestamp

Rule
Timestamp cursors silently drop offline-composed records that arrive late.
Fix
Paginate on a server-side sequence rather than a timestamp. Timestamps collide and records get skipped.

Specification

API004warningL3Catalogued, not yet implemented — v0.2

Documented query parameters accepted

Rule
municipality, kind, bbox, updated_since, limit, cursor.
Fix
Accept the documented query parameters, or remove them from the documentation.

Specification

API005errorL3Catalogued, not yet implemented — v0.2

CORS * on the API; no auth required for reads

Rule
Reads are public by design.
Fix
Serve the API with CORS * and no authentication for reads. Public-interest data behind a key is not public.

Specification

API006infoL3Catalogued, not yet implemented — v0.2

Consumes ≥ 1 peer feed (partly self-declared)

Rule
L3 requires consuming as well as serving — the one requirement a probe cannot fully measure, so it is reported as info with the limitation stated.
Fix
Consume at least one peer feed. The level is about interoperating, not only about publishing.

Specification

Write API

WRT001errorL4Catalogued, not yet implemented — v0.2

POST accepts the {source, external_id, place} envelope

Rule
The write envelope is fixed.
Fix
Accept the {source, external_id, place} envelope on POST.

Specification

WRT002errorL4Catalogued, not yet implemented — v0.2

Idempotency on (source, external_id): a replay does not duplicate

Rule
Re-sending is an upsert of the sender’s own contribution, never a duplicate.
Fix
Make writes idempotent on (source, external_id). A replay after a timeout must not create a second record.

Specification

WRT003errorL4Catalogued, not yet implemented — v0.2

409 on an id conflict outside the sender’s namespace

Rule
Namespace discipline is enforced at the write boundary.
Fix
Return 409 when a sender tries to write an id outside its own namespace.

Specification

WRT004errorL4Catalogued, not yet implemented — v0.2

In auth:none mode, a moderation state is echoed and rate limiting is observable

Rule
Open writes REQUIRE mitigations: rate limiting, a moderation queue, and an echoed state.
Fix
Echo the moderation state and make rate limiting observable, so an unauthenticated sender knows what happened to its write.

Specification

WRT005errorL4Catalogued, not yet implemented — v0.2

Republished records carry source.source_id = the original sender

Rule
The sender’s identity travels with the record forever.
Fix
Keep source.source_id as the original sender when republishing. Attribution is the thing that must survive the hop.

Specification