Skip to Content
Feeds referenceCivic data

Civic data

One live feed in the civic family. Civic data is everything Claros attests that does not come from the EIA energy API, and the flagship civic feed is OP-1: daily parking-meter revenue for a single City of San Diego parking pole. It is mapped, attested, and read exactly like an energy feed, because under the hood it is just another feed_id.

The point of this family is what it proves: Claros is a general real-world-data oracle, not only an energy oracle. The same agent, the same AttestationRegistry, and the same provenance model that publish a WTI crude price also publish a municipal parking number sourced from a city government CSV. Add a source, map it to a feed_id, and it reads through every Claros path with no special casing.

OP-1 is read by feed_id like any other feed. Use the SDK, the REST API, or the paid x402 endpoint. The value is real San Diego parking revenue, attested on Casper testnet.

OP-1

Daily parking-meter revenue for one San Diego parking pole, reported in US cents and attested on-chain in the same AttestationRegistry as every energy feed.

FieldValue
Feed IDOP-1
FamilyCivic
AssetA single City of San Diego parking-meter pole
SourceCity of San Diego open data (seshat.datasd.org)
DatasetParking-meter daily transactions (one row per pole per day)
Frequencydaily (data lags about 2 days)
Reported amountUS cents
Decimals2
UnitUSD

The on-chain value is the integer cents, and decimals is 2, so the human value is amount / 10^2 = amount / 100. For the period 2026-06-23 the pole took in 273420 cents, that is $2,734.20.

Periodamount (cents, on-chain)decimalsHuman value
202606232734202$2,734.20

The source: a daily CSV, no API

The City of San Diego does not expose a queryable parking API. Instead it publishes a pre-aggregated CSV per year, one row per pole per day, with amounts already summed in cents. The Claros adapter (agent/src/sandiego.ts) fetches that whole yearly file and caches it, refreshing with a conditional GET so it does not redownload unchanged data.

https://seshat.datasd.org/parking_meters_transactions_daily/treas_meters_2026_pole_by_mo_day_datasd.csv

How the adapter turns that file into a reading:

  • Whole-file fetch, cached. There is no per-pole or per-day query, so the adapter pulls the entire year file and keeps it in memory (a one-hour TTL).
  • Conditional GET. On refresh it sends If-Modified-Since and If-None-Match; a 304 Not Modified reuses the cached copy and avoids re-parsing.
  • Columns used. Each row carries pole_id, month, day, sum_trans_amt (the day’s revenue in cents), and num_trans (the transaction count).
  • Latest reading = max date. The published data lags about 2 days, so the “latest” value is the row for the most recent day present in the file, keyed to the requested pole_id.

Because the city’s file lags, treat OP-1 as a daily series that is roughly 2 days behind real time. Always check the reading’s period (the day it describes) and updated_at (when Claros attested it) before you act on the value.

Reading OP-1

OP-1 is just a feed_id, so every read path that works for an energy feed works here unchanged. The free paths return the same self-describing reading; the paid x402 path adds settlement and on-chain provenance pointers.

A plain HTTP GET, no key, open CORS. See the REST API.

curl http://localhost:4030/v1/feeds/OP-1
{ "feed_id": "OP-1", "value": 2734.2, "amount": "273420", "decimals": 2, "frequency": "daily", "period": 20260623, "source_hash": "71db7d2dd6edd2a47f3fe27c4c693fa1", "updated_at": 1750680000000 }

amount is the raw integer cents (a string, so it is safe past JavaScript’s integer range), decimals is 2, and value is the pre-computed 273420 / 100 = 2734.2 USD.

On-chain attestation and provenance

OP-1 is attested into the same AttestationRegistry as every energy feed, keyed by the feed_id OP-1, with the same provenance model. Each reading carries a source_hash: a deterministic SHA-256 over the substantive identity of the row, truncated to the first 32 hex characters. For civic data the canonical string is the asset id, period, cents, transaction count, and the exact CSV URL, joined with a literal pipe:

const source_hash = createHash('sha256') .update(`${assetId}|${period}|${rec.cents}|${rec.n}|${SOURCE_URL}`) .digest('hex') .slice(0, 32) // OP-1|20260623|273420|412|https://seshat.datasd.org/.../treas_meters_2026_pole_by_mo_day_datasd.csv // -> 71db7d2dd6edd2a47f3fe27c4c693fa1

The agent computes this once and attests it on-chain, and the x402 server recomputes it with the same function when it sells the feed, so the two digests are equal by construction.

If the source_hash you receive over the x402 feed equals the one stored in the on-chain attestation for the same period, the value you paid for is exactly what Claros committed on-chain. That string match is the trust boundary. See provenance and source hash for the full reconciliation, including how to reproduce the digest from the raw CSV.

Last updated on