Polygon mainnet · chain 137 · us-ashburn-1

A Polygon node that can show its working

First-party, hardware-rooted, and able to prove that an answer is complete — not merely signed. Every response states what it establishes, on what evidence, and for how long that stays true.

node.rootz.global · JSON-RPC · index API · MCP
Status

Node panel

Snapshot · 2026-09-15 00:40 UTC
Nodepolygon-node-1Bor v2.10.1 · Heimdall-v2 · pruned full
Chain head93,816,844eth_syncing false — synced
Hardware rootRootedvTPM · AK 0x81010002 · key 0x81010003
AnchorAnchoredOCI instance identity · 2-hour window
Measurement869f1e18b5f40225…sha256 over PCR 0, 4, 7
Signing keyseted25519 + p256rotates hourly, certified by the TPM
Index storage≈2.6 MiBper 15M blocks — time is the constraint, not disk
Chaindata6.6 TiBgrowing ≈7.8 GiB/day
This panel polls /healthz on the node serving this page, on load and every 15 seconds. The service itself binds to 127.0.0.1; nginx is the only public listener. Point the field at any other node running this service to read that one instead.
Evidence

What this node can prove

Each row is a separate claim with its own strength. Measured means we observed it. Relayed means someone else asserts it and we carry their evidence. Nothing collapses to a single boolean.

This box booted with these PCRsMeasured TPM2_Quote over PCR 0, 4, 7, with the nonce bound to the keyset digest — so the quote vouches for this key, not merely for the box. The node runs tpm2_checkquote against it before signing anything with it, and publishes the AK public key so you can run the same check rather than taking our word: tpm2_checkquote -u <akPublicKeyPem> -m <quote.message> -s <quote.signature> -f <quote.pcrs> -g sha256 -q <nonce>. Valid from the quote; PCRs are frozen until the next boot.
The signing key is inside this TPM and cannot leaveMeasured TPM2_Certify by the attestation key; attributes carry fixedtpm|fixedparent. Holds until the key is destroyed.
This is OCI instance …anldif2yjxyq in tenancy …bggpwlbaRelayed Oracle instance identity certificate → PKISVC Identity Intermediate us-ashburn-1, verified cryptographically. The intermediate must name the expected regional root as its issuer — a DN check, not a key check, and we say so. Not verified onward to that root: the metadata service does not serve it, so we have never held it. The intermediate's key cannot be pinned either: Oracle mints a fresh one per issuance — measured rotating 38 minutes apart on 2026-09-15. Valid two hours, so the certificate is captured at signing time and carried as evidence; a reader in 2030 cannot fetch a certificate that expired that night.
These exact bytes were returnedMeasured Signature over a SHA-256 of the wire bytes, never a re-encoding. Permanent once signed.
This answer is bound to that questionMeasured The request hash rides inside the signed envelope, so an answer cannot be replayed against a different question.
The caller is this addressMeasured secp256k1 recovery from the request signature — the address is recovered, never trusted from the envelope. 5-minute request TTL.
This index answer is complete for this key and rangeMeasured Chunk commitment with sorted keys. Absence is proved by bracketing neighbours, not reported as an empty list. Complete within the declared key space.
Limits

What this node does not claim

Published with the same weight as the claims above. Most attestation products ship a green check; the list of refusals is the part that can't be copied without accepting the same limits.

That the TPM is genuine hardwareNot claimed It is a software TPM — manufacturer IBM, vendor string SW TPM. No EK certificate and no NV indices at all. A quote alone proves some TPM said so; only the Oracle certificate binds it to a real machine.
That the node was intact at runtimeNot claimed PCRs are extended at boot and frozen after. Measured boot says what loaded. It says nothing about what happened in memory afterwards.
That Oracle vouches for our integrityNot claimed The certificate carries identity fields only — no measurement, no PCR. A compromised guest keeps receiving valid certificates. The defensible sentence is "this node's identity was confirmed by Oracle within two hours of this signature."
That the chain data is trueNot claimed Truth of the ledger is consensus. This attests custody of a read — origin is measured, ownership is signed, and this is neither.
That the chain client is measuredNot claimed PCR 0/4/7 cover firmware, boot chain and Secure Boot state. bor is a userspace binary and is not in the measured set — IMA runs and PCR 10 is extended with 89 entries, but the client is absent from the log and PCR 10 is not in our policy. The attestation says how the box booted, not which client produced the view.
That a revocation answer is authoritativePartly The absence proof is sound and a stale range is now refused rather than answered — but only when the caller asks for coverage through a specific block. A caller who does not set that bound can still receive an honest answer over an old range.
That the exchange is confidentialNot claimed Signatures carry integrity, authenticity, freshness and request-binding. They do not carry secrecy — request and response travel in the clear, and this node observes which addresses are asked about while it serves, even though it retains nothing.
That the node is honestNot claimed A node that lies consistently produces a consistent index. That is what N≥2 is for — and only when the nodes can fail independently.
That anything here is tamper-proofNot claimed Tamper-evident. A different and smaller claim.
Completeness

Why a signature is not enough

A signed response proves custody of what was sent. It says nothing about what was left out — and an omitted row is undetectable by inspection, in an answer that is signed and therefore looks stronger.

Re-derivation is not a fallback

eth_getLogs windowTime
1,000 blocks459 ms
10,000 blocks4.1 s
50,000 blocks18.4 s
200,000 blockstimed out (30 s cap)

Checking an answer by re-querying the chain costs ~92 minutes for one year of history. The commitment has to do the work.

So the index commits

History is cut into fixed, aligned chunks, so which chunks cover a range is arithmetic the verifier computes — gaps become maths, not trust.

Within a chunk, keys are sorted. A present key returns its full event list and a Merkle path; an absent key returns the two neighbours that bracket it. That is what makes “there are none” a proof rather than a promise.

Ten adversarial cases were written into the spec before the code and all are refused: dropped events, dropped keys, false absence, coverage gaps, re-ordering, uncertified signers, scope violations, foreign nodes.

Closed 2026-09-15. A chunk committed before a reorganisation would be internally consistent and wrong, so the indexer now refuses to build one whose range is not yet final — using Heimdall's checkpoints to Ethereum rather than a confirmation depth we picked ourselves. Every chunk states its finality basis, and a chunk built without one says so.
Interface

HTTP API

One port, four doors. Signature metadata rides in headers, so the body stays byte-identical to what Bor produced and an unmodified client keeps working.

# A signed chain read. The body is untouched; the proof is in the headers.
curl -X POST https://node.rootz.global/ \
  -H 'content-type: application/json' \
  -H 'x-rootz-sign: v1' \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

# → x-rootz-sig                        the signed envelope (RFC 8785 JCS)
#   x-rootz-keyset                     the public keyset it was signed with
#   x-rootz-certification              binds that keyset to the TPM root
#   x-rootz-certification-signature
# Ask the index a question, and get the proof with the answer.
curl -X POST https://node.rootz.global/index/query \
  -H 'content-type: application/json' \
  -d '{"key_space":"rootz.v6.datawallet.notes",
       "key":"0x…","from_block":80600000,"to_block":80699999}'

# Supporting endpoints
GET /index/keyspaces          what can be asked
GET /index/coverage?key_space=…  what is ACTUALLY indexed — gaps visible, not inferred
GET /healthz                  head, and whether it admits being hardware-rooted
GET /.well-known/rootz-node   root key, certification, JWKS, DNS selectors
An over-wide range returns 400 with a hint for how to split it — never a 200 carrying a shorter answer. A truncated completeness claim is a false one.
Agents

MCP

The same node in an agent's protocol, at POST /mcp. Stateless — a reconnecting agent gets the same answers because they come from the store, never from session state.

node_attestationWhat this node is, its measurement and signing keyset — and explicitly the claims it does not make.Call this first if you intend to rely on anything else here.
polygon_headThe block this node considers the tip.Liveness — true when answered, stale immediately after.
v6_datawallet_notesEvery note written to a data wallet.Keyed on the wallet address, with the completeness proof attached.
v6_datawallet_byWriterEvery note a given writer has authored, across all wallets.Keyed on the writer, so it is a single lookup rather than a scan across every wallet.
v6_datawallet_createdEvery data wallet a given address created.Keyed on the creator, from an indexed topic — no transaction traces needed.
v6_identity_createdIdentities created by an address.Keyed on the creator.
v6_messagesOn-chain messages by sender.Keyed on the sender.
v6_rivetsRivet public-key registrations.Keyed on the rivet address.
Every result carries its own limits. An agent cannot receive a signature without also receiving what it does not cover, the depth of the claim, and how long it stays true. An unbuilt range says so rather than returning an empty list — a human squints at an empty result and wonders; an agent takes it as fact.
Reliance

What we bet on this ourselves

The strongest thing we can say about a proof is not that it exists, but that our own service stops working when it fails. These are places where the same check a reader would run is what prevents this node from serving something wrong.

The store re-derives every commitment before serving itIn use Chunks are persisted as their events, not their tree. On read the root is recomputed and compared to the stored header, and a mismatch refuses to serve. Keeping the tree would have hidden corruption — the root would still verify against itself.
The indexer refuses a range that can still reorganiseIn use Finality comes from Polygon's checkpoints to Ethereum, not from a confirmation depth we chose. If that signal is unavailable the fallback is labelled as weaker in the chunk header rather than substituted quietly.
A failed scan aborts the chunk instead of narrowing the answerIn use A window that times out halves and retries; it never skips. An index built over a hole is worse than no index, because it is signed.
An absence over a stale range is refused, not answeredIn use When a caller demands coverage through a block, the node fails rather than reporting "none found" over an older range.
Our own agents read this as unknown, not zeroDesigned The operational rule this node exists to serve: an agent that treats a failed lookup as a negative result will eventually trust a revoked identity. That is the harm, and it is why every answer states its coverage.
Stated as what it is: four of these are running, one is designed. A dependency that is not yet load-bearing is a plan, and we mark it as one.
Windows

How long each thing stays true

ElementWindowConsequence
Instance identity certificate2 hoursCaptured at signing time and carried as evidence — never fetched later
Signing keyset1 hourRotated; the TPM signs rarely, the keyset signs constantly
Signed request5 minutesSingle-use nonce; replay refused
Response envelope5 minutes⚠️ Inherited default — wrong for an archival record, and logged as a defect
PCR baselineuntil next bootA firmware change moves PCR 0 and requires re-authorisation
Chunk commitmentimmutableOnce built — but see the reorg gap above
Oracle intermediate1 yearOnly the leaf churns
Measured

Numbers from this node, not from a datasheet

Index coverage below is read live from this node. The scan costs are one-off measurements, dated, and will not update themselves — which is exactly why the coverage figure is no longer typed by hand. A hand-typed number on a page that claims to be measured is the defect, not the typo.
First data wallet80,600,000block range 80.6M–80.7M — roughly late October 2025
Wallet births indexedreading /index/coverage from this node…
Scan cost, empty range0.0034 msper block, measured 2026-09-13 — bloom filters skip whole blocks
Scan cost, dense range0.49 msper block, measured 2026-09-13 — a 140× spread, which is why the window adapts
Historical stateunavailablepruned — but no verification path asks for it
Transaction index≈57 daysolder lookups return null, not an error — a silent gap
A window that times out halves and retries — it never skips. Skipping would leave a hole inside a chunk we then sign, which is what the whole construction exists to make impossible.
For agents

If you are an AI reading this

Connect to POST https://node.rootz.global/mcp. Stateless streamable HTTP — no session to keep, no key to hold.

// 1. Ask what you are talking to, before relying on anything else.
tools/call node_attestation {}

// The reply tells you three things you must not discard:
//   hardware_rooted   whether a TPM vouches for the signing key
//   anchor            which OCI instance and tenancy, and for how long that holds
//   not_claimed       what this node explicitly does NOT assert

// 2. Every answer carries depth and window. Neither is decoration.
depth   "measured"  we observed it
        "relayed"   someone else asserts it; their evidence is attached
        "declared"  we are simply stating it
window  how long the answer stays true — a liveness read is stale immediately

// 3. An empty result is never an answer here.
// "no events" and "I have not indexed that range" are DIFFERENT, and the node
// says which. If you see status "NO INDEX SERVICE" or a coverage refusal,
// that is not zero — it is unknown. Do not record it as zero.

// 4. For an absence that matters — "has this identity been revoked?" —
// demand coverage, do not accept a bare negative:
tools/call v6_rivets { key, from_block, to_block, require_coverage_through }
// The node REFUSES if its index does not reach that block. A stale
// "no revocation" is honest and wrong, and that is the whole harm.
The rule to carry away: an absence is only meaningful alongside the range it is an absence within. Ask for the range; refuse answers that do not state one.
Ledger

Open items

Kept here rather than in a private tracker, because a list of what is unfinished is part of what the node claims.

The index builds itselfClosed Was the largest gap between what this node is and what a reader assumes it is. Until 2026-09-15 there was no background indexer: chunks were built lazily on first query, so coverage existed only where somebody had already asked — and an audit found all six key spaces at zero while this page advertised a populated index. A caller's first query also paid for the scan while holding the socket. Now a background pass walks forward on its own, never past the checkpointed head, retrying a failed chunk rather than skipping it, because a hole inside a range we call covered is worse than a gap — a gap is visible. The coverage figure above is read from the node, not typed here.
The TPM quote is verified, not merely collectedClosed Until 2026-09-15 nothing verified a quote or a certify blob: the artefacts were gathered and hardwareRooted: true was a boolean signed by a key that said so. tpm2_checkquote now runs in the same chain as the quote, and a quote that does not verify produces no attestation at all.
The OCI chain is checked against the expected rootPartly One link was verified — that the leaf was issued by the intermediate it arrived with — while this page and the signed claims document said "chain verified". A self-consistent forged pair passed both checks, and an attacker generates both halves in one command. The first fix was wrong and took this node down. We pinned the intermediate's key at 15:09; Oracle rotated it at 15:47 and the node refused to attest and crash-looped for about twenty minutes. Fail-closed is right when the thing failing is an attack, and a defect when the thing failing is the provider behaving normally. What runs now is the issuer-DN check, which survives rotation and refuses a chain rooted elsewhere. Still open: obtain the regional root out of band and pin it — a root does not rotate every half hour.
Chunk finality before commitmentClosed Designed 2026-09-14 and not actually connected until 2026-09-15 — the finality source was never constructed, so every chunk committed before then is signed NONE — committed without a finality check, permanently. It now refuses to build a chunk whose range is not yet checkpointed to Ethereum, and refuses to build at all when no finality source answers. Fallback to a confirmation depth is labelled as the weaker source, never substituted silently.
Response bound to the whole signed requestClosed Previously bound to the body hash alone, so two callers asking the same question received identically-bound answers and a captured response could be replayed to another caller. Now covers the caller, nonce, timestamp and audience.
Survives a rebootClosed Ran under nohup; now a systemd unit, enabled, ordered after the chain client with Wants so a restart of either does not cascade.
Revocation freshness policyClosed A query can demand coverage through a given block and is refused otherwise, rather than reporting an absence over a stale range. The refusal was written on 2026-09-14 and reachable from no door until 2026-09-15: neither surface read the parameter, and the agent tool schemas did not declare it, so an agent that followed this page's own instruction had its freshness bound silently discarded. Both doors now honour it.
Measure the chain clientOpen Needs an IMA policy covering /usr/bin/bor and PCR 10 in the quote. The natural trigger is the next client upgrade — that is when the measurement moves, and a measurement nobody notices moving is not one.
Reconcile checkpoint roots against our blocksOpen A checkpoint says a range existed and was agreed. It does not yet say our copy is that copy — the root has to be recomputed against what we hold.
Independent second nodeOpen Three separate reasons: quorum, staggered firmware updates, and correctness — an agent must not read its own command channel through the node it administers. Identical images in one tenancy fail identically and agree perfectly while all being wrong.
Archival response TTLOpen The response envelope inherits a 5-minute default — sensible for a live call, wrong for something meant to verify in four years.
Public endpointLive You are reading this over it. nginx terminates TLS at node.rootz.global and the service stays bound to 127.0.0.1 behind it. What makes exposure safe is the tiering: an unsigned caller is served slowly rather than refused, and there is no API key to leak because identity is the signature. This row said "the ports are closed" for several hours while serving over them — kept here rather than quietly edited, because an open-items ledger that flatters itself is worth nothing.
For agents

Start at /skill

If you are an AI reading this page to work out how to use the node, stop and fetch https://node.rootz.global/skill instead. It is the operating manual, in markdown, in one request.

The routes are the easy part and you will discover them in a minute. The rules are the part that matters and they are invisible from a route list: an empty result is not an answer; an absence that matters must carry require_coverage_through or the node's silence means nothing; absence_is_proved: false is not "none found"; and without a pinned root a valid signature proves only that some node signed.

The same file ships in the repo as a Claude skill, and a test fails if it drifts from the tool names, key spaces and query limits the service actually enforces — a manual that disagrees with its service is worse than none, because it is believed.

Independence

Checking it without asking us

The serving surface is not the proof. Verification runs in the relying party, against the bytes it actually received, with the node's public material fetched from /.well-known/rootz-node.

# 1. Rebuild the covering chunk set from the range and chunk size — yourself.
# 2. Check each signed chunk header chains to a certified keyset, and that
     keyset to a TPM quote, and that quote to the Oracle instance certificate.
# 3. Recompute the leaf from the returned events and walk the Merkle path
     to the committed root. One dropped or re-ordered event changes it.
# 4. For an absent key, check the two neighbours are adjacent — otherwise
     a key could be hidden between them.
# 5. Confirm the response is bound to the request you actually sent.
Any failure is a refusal, never a partial answer. Attestation fails closed: an error is better than a signed fact from a source you could not check.