Changelog · Methodology · API · Security
Source: watch/API.md in the repository, rendered here without changes. Source file
Everything the dashboard shows from the watch is a file rewritten by the Watch GitHub Action every 6 hours (17 */6 * * * UTC), committed to the watch-data branch of this repository (the root of the branch is the folder that is called watch/ in the scripts and in this document) and served raw from https://raw.githubusercontent.com/RealSpap/authority-risk-oracle-showcase/watch-data/<file>. main holds the code and the documents written by hand (this one, the README and the schemas, copied into the branch at every run), so its history is the history of the work. Before the move the same files were committed to main under watch/; those commits stay, and the old links answer with a short notice that points here. status.json carries data_location, the base URL under which every file is served, so a page or a monitor does not need a constant of its own (the notice left on main carries it too). No key, no rate limit beyond GitHub's. The on-chain source of truth stays the oracle contracts (getScore(address)): these files are a convenience read of them.
| File | What | Schema |
|---|---|---|
latest.json | the last run: one line per oracle (read ok, block, targets, stale, and self_watch: the oracle watching itself, role events since the previous run, code hash), movers, alerts, sources not read | schema/latest.schema.json |
scores/<oracle-slug>.json | every target of one oracle with its five sub-scores, composite, lastUpdated, stale flag, previous composite; self: the oracle contract's own code hash, the admin and updater roles of the known holders read directly (roles), and every RoleGranted / RoleRevoked on it since the previous run where the RPC's log range allows it (log_span, else error); self_scanned_to: the block up to which those events were actually read (a failed scan leaves it where it was, so the next run reads the missed window; a window too large to read is given up and gap_blocks says how many blocks) (an alarm when there is one, as is a change of maxStaleness or of the code, or a target list that shrank: the contract cannot remove a target) | schema/scores.schema.json |
history.jsonl | one line per run (append-only) | the run's counters: targets, stale, movers, alerts, unread, and since 2026-10-10 unread_oracles (names), from which the dashboard shows each oracle's read reliability over 7 days |
events.jsonl | one line per score move or alarm, and since 2026-10-10 per finding of the independent reader (admin_change, admin_grew), append-only and hash-chained: each line carries seq and prev = sha256 of the previous line's exact text, so an edited, removed or inserted line breaks every link after it | {run_utc, kind, oracle, target, field, before, after, label, seq, prev} (an alarm carries text) |
chain.json | the result of scripts/verify_chain.py on events.jsonl at this run: ok, lines, chained, head (sha256 of the last line); the dashboard re-verifies the chain in the browser | |
methodology.json | the methodologyHash values read on the oracles right now: which oracles carry which hash and how many targets; mixed names an oracle whose targets carry two hashes (a push half done); changes (since 2026-10-10): an oracle whose dominant hash differs from the previous run (from, to, targets, run_utc): every score of that oracle now comes from a new method, the series before this run is not comparable with the series after it; it travels with the alarm and is a line of the hash-chained events.jsonl (kind methodology_changed, fields chain, from, to) | schema/methodology.schema.json |
coverage.json | what this watch sees on each chain, from the files of the run: targets; independent_read (readable / unreadable / native); proxies_fingerprinted (an upgrade would be seen: the EIP-1967 implementation on EVM, the ProgramData deployment slot on Solana); safes and safes_modules_read; timelocks_in_admin_path against timelock_queues_watched (a timelock found by the reader whose queue is not watched means queued operations are not seen before they execute); safe_events (the Safe event scan: Safes, days, status ok / unread with the reason); tvl_matched; and blind_spots, the same in words. A blind spot is something this watch cannot tell about that chain, not a finding against its protocols; the dashboard lists them under the hero; since 2026-10-10 each chain also says how far the state mechanisms reach: pause_answered (contracts that answer paused() or isPaused()), roles_read (contracts with a secondary role getter), admin_safes_nonce_read (admin Safes whose nonce was read), admin_role_probed (unreadable contracts probed for their admin role), each null when its file is missing (not read, never a zero), and the blind spots say it in words: a pause on the contracts that do not answer is not seen, so 'nothing paused' says nothing about them | schema/coverage.schema.json |
review_queue.json, review_queue.md | what a person must look at, from the files of the run, ranked and deduplicated: priority 1, something happened (upgrade executed, admin read changed, transfer in flight, plain key as Safe module, Safe signer or threshold or module event, methodology change, queued operation executable or executable within 48 h); 2, decide (second-opinion disagreement, Safe module or guard to identify, open dispute, timelock queue unread); 3, coverage to extend. Each item: kind, chain, target, text, todo, source. Items a person already reviewed leave the list (data/second_opinion_acknowledged.json, data/digest_acknowledged.json, disputes resolved); a signer named in a Safe event is shortened. The queue remembers: an item keeps its first_seen from run to run (age_days), the file says oldest_days, and a priority-1 item older than overdue_days (2) is overdue: it re-enters the alarm with the whole queue, so a stale finding keeps nagging the maintainer in public. review_queue.md is the same for reading; since 2026-10-10 it also reads the state files: a contract that answers paused() true and a secondary role that changed hands are priority 1; a delay announced shorter is priority 1, a longer delay or a new guardian announced, a balance the contract no longer reports and an admin Safe that woke up are priority 2; an admin Safe that has never executed (nonce 0) and an admin role seen on an unreadable contract are priority 3; a stale record adds nothing, and no text carries a value that changes from run to run, so an item keeps its age and a priority-1 item left alone for more than 2 days re-enters the alarm (review_queue.md is the same list as Markdown, the first section of the daily report) | schema/review_queue.schema.json |
cross_chain.json | the same protocol on several chains, hand by hand: the tracked contracts grouped by DefiLlama protocol (tvl.json), kept when present on two chains or more; per chain the composite range, the admin kinds the independent reader found, the postures in one line (Safe 5-of-10 (10 plain keys), timelock 24 h, plain key); weakest_chain and strongest_chain by lowest composite, spread, postures_differ (admin kinds differ or composites 15 points apart), text for a reader. A user moving between deployments of one protocol does not move between equal hands; the dashboard lists them under the hero | schema/cross_chain.schema.json |
impl_sources.json | for every implementation behind a tracked proxy (second_opinion.json, impl): whether Sourcify (public, no key) holds a verified source for it on that chain, with match (exact match / match), name, compiler, verified_at; verified false = nothing verified on Sourcify (this watch asks Sourcify only: an explorer of the chain may hold a verified source; an upgrade to an implementation nobody can read anywhere is an upgrade nobody can read), null = Sourcify not answering this run (the previous answer for the same implementation is kept, stale); chains_not_on_sourcify names the chains it cannot ask about (Tempo). Solana (since 2026-10-10): every program behind a tracked account against OtterSec's verified-build registry (source: osec): verified when a build reproduced from repo_url matches the deployed binary now, verified_at the last match, note when a repository is registered but no build matches (an upgrade since the last verified build), or none is registered. The dashboard says it under each proxy and counts them in the hero | schema/impl_sources.schema.json |
risk_by_cause.json | money at risk by cause, from the independent reads of the run and DefiLlama: for each cause (plain_key_admin, keys_only_multisig, no_delay, unverified_code, one_key_freezes, upgrade_executable, transfer_in_flight, module_on_safe, and since 2026-10-10 side_role_plain_key: a curator, guardian or pauser held by a plain key) the contracts under it, the money behind them (one figure per protocol and chain, the 08/10 rule), the share of all tracked money, and the targets; tracked_tvl_usd is the denominator; a contract listed in data/tvl_scope.json as retired or peripheral (the protocol's TVL does not sit behind it) counts as a contract, never as money (out_of_scope per cause), and the same registry empties the exposure ranking and the cross-chain figures of it. A contract can sit under several causes: a line is the money a single failure of that kind would expose, not a sum across lines. The dashboard shows the table under the hero | schema/risk_by_cause.schema.json |
held_assets.json | what each tracked contract holds by its own account: ERC-4626 totalAssets() in asset(), the asset's decimals and symbol, the price from coins.llama.fi (price_usd, price_at) and held_usd; by_target keyed chain:address; a contract without the two getters is absent (a router, a pool, a token, a Solana program), a price DefiLlama does not know leaves held_usd null and the key in unpriced, never a zero; a contract the RPC did not answer keeps its previous record marked stale (stale_since), a price the API did not answer falls back to the previous run's price marked price_stale, and a chain whose every call went unanswered is listed in unread_chains: a dead endpoint is not a vault that disappeared; since 2026-10-10 each record also carries the move of the balance since the previous run, in token units so that a price move does not count (prev_amount, prev_at, amount_change_pct; none when the asset changed or there is no previous record), and drops lists the contracts down 30% or more; a contract that reported its balance in the previous run and now answers without one (totalAssets() reverts, asset() empty: an upgrade, a pause) keeps its previous record marked stale and vanished (vanished_since), is listed in vanished, and fires one medium alert; it never vanishes from the file silently; the attacker's question (second_opinion.json, exposure, money_source = contract) uses it before the protocol TVL | schema/held_assets.schema.json |
pause_state.json | whether each tracked EVM contract that exposes paused() or isPaused() is paused (28 of 161 on 10/10, none paused), and the flips since the previous run: paused (keys now paused), changes (paused or unpaused, with the time of the previous read), by_target keyed chain:address (paused, via, since); a contract without the getter is absent, not running; a call that went unanswered keeps the previous record marked stale, which never fires; a flip to paused fires a high alert (alerts.json, pause), a flip back an info one | schema/pause_state.schema.json |
side_roles.json | the secondary roles of the tracked EVM contracts, named by the getter that answered (pauser(), guardian(), curator(), pauseGuardian(), emergencyGuardian(), emergencyAdmin(); 28 of 161 answered on 10/10): for each, who holds it (kind: eoa shortened, safe with threshold and owners and no signer, timelock with delay_seconds, contract, unread), plain_key_roles (contracts where one is held by a plain key), and changes since the previous run (changed, added, cleared); a role is named by its getter, never by what it is assumed to do (in a MetaMorpho vault guardian() can revoke a pending change, it does not move funds); a vault that announces a new guardian (pendingGuardian()) or a new delay (pendingTimelock()) before it applies is listed in pending and in changes as announced with valid_at (when it applies), from_seconds/to_seconds and shorter (the delay is announced lower than the current timelock()); a call nobody answered keeps the previous role or announcement marked stale; a change fires a medium alert, a shorter delay a high one (alerts.json, side_role) | schema/side_roles.schema.json |
safe_activity.json | did a Safe that governs tracked contracts wake up (mechanism 86, since 2026-10-10): the nonce() of every Safe that is the admin of a tracked contract (a Safe holder, or the Safe behind a bare-contract holder, from second_opinion.json), 39 on 10/10, read every run; records are keyed by chain and a hash of the Safe address (the redaction step shortens signer addresses in place, and a key made of an address would lose its history), labelled with the shortened address; idle_since is the first run that read the current nonce, so idle time is time the watch observed, and a cold start fires nothing; changes lists the Safes whose nonce moved since the previous run (delta transactions executed, idle_days, governs: the contracts it is the admin of), woke those that moved after wake_days (14) or more unchanged; regressed: the nonce went down, the Safe was replaced, no wake claimed; never_executed: Safes still at nonce 0, an admin path that has never been used (2 on 10/10); a Safe that does not answer keeps its record, marked stale, and never fires. A wake on a contract with $10M or more behind it fires a medium alert (safe_woke in rules.json, payload safe_wake: the Safe shortened, delta, idle_days, previous_run) that reaches the alerts feed and the contract's own feed | schema/safe_activity.schema.json |
admin_probe.json | who holds the admin role of a contract whose role holders cannot be listed (mechanism 91): for an AccessControl-style target the independent reader leaves unreadable (it answers hasRole(0, address(0)) with a false boolean word), hasRole(DEFAULT_ADMIN_ROLE, X) is asked for every contract the reader already knows as an admin elsewhere on the same chain (Safes, timelocks, other contracts, whole addresses only); by_target[<chain>:<target>].holders lists the ones that answered yes, with their kind, threshold and owners; a yes is proof, absence proves nothing: complete is always false, probed and answered say how many candidates were asked and answered, and a target where no candidate answered has no record; plain keys are not probed, the Tempo precompiles (hasRole in the other order) are left out. 2 of 3 such targets had a known holder on 10/10 (a Safe 4-of-8 on HyperEVM, a timelock on Robinhood); never counted as agreement or disagreement | schema/admin_probe.schema.json |
squads_vaults.json | the cache of the Squads v4 vault 0 resolution (Solana): for each program-derived upgrade authority the reader met, the Multisig account whose vault 0 it is, or null when it is not the vault 0 of any v4 multisig; derived once by deriving vault 0 of every v4 Multisig account on mainnet (about 160 000 accounts, 90 s of pure Python), then kept so the next runs only look up | (cache, no schema) |
catalog.json | where each published file is and what it holds (mechanism 89): one entry per file or folder (url, description taken from this document, schema, size), the contract page pattern, and the ten questions people ask with the file and the field that answers each; a file with no row here is named under undocumented. Written near the end of the run, derived like status.json: not in the signed manifest (signature.json is the integrity record). The site's llms.txt points to it | (no schema) |
metrics.prom | the run in the Prometheus text format (mechanism 88), for a monitor that does not parse nine JSON files: aro_run_timestamp_seconds, aro_status{state} (one series per state, 1 for the current one), aro_oracle_read{oracle}, aro_oracle_targets and aro_oracle_stale_targets (only for an oracle that was read), aro_targets, aro_targets_stale, aro_alerts_active{severity}, aro_second_opinion{result}, aro_contracts_paused, aro_safes, aro_safes_woke, aro_safes_never_executed, aro_chain_ok, aro_signed and, per tracked contract, aro_target_score{chain,target} and aro_target_stale. A source that is missing or unreadable gives no series, never a zero. The alert that matters most is the Watch stopping: time() - aro_run_timestamp_seconds > 8 * 3600 (a run every 6 hours: two missed). Written after status.json, so like it this file is derived and not in the signed manifest: recompute it from the signed files | (text format, no schema) |
alarm_state.json | what the Alarm step last saw (findings, identified not counted) and when it last opened an issue (notified_utc); a finding not in the list opens a new private issue, a standing state a reminder once every 24 hours, an unchanged one nothing; written only when ISSUES_TOKEN is set, not signed (written after the signature, like status.json); no schema, an internal state | none |
trend.jsonl | one line per run, appended, never rewritten: the numbers the run measured (targets, stale, tracked TVL, money and contracts per cause, agree / disagree / unreadable / changed / upgrades of the independent reader, signer composition, blind spots, verified code, review items), so that the next runs can say whether the tracked set improves; the dashboard draws one sparkline per measure under the hero from the second run | |
warning_ledger.json | the warning ledger (since 2026-10-12, one entry per queued timelock or Squads operation, kept between runs): the run that first saw it, outcome (warned: seen queued and not yet executable, notice_hours is the real lead time; seen_without_notice: queued but already executable, or no time in the queue file; missed: queued after the ledger started and already applied when first seen, it went through between two runs; baseline: applied before the ledger existed, counted as neither), and a summary with the shortest and median notice. Misses are kept next to the warnings; a young ledger says since when it counts | |
signature.json | the manifest of this run (sha256 of every published watch file, head of the event-log chain, run time) and its Ed25519 signature when the maintainer's key is set (signed, public_key, signature), plus the sha256 of the code that produced it (code: scripts, rules, the workflow) and the commit; a file changed by a commit made outside the Watch workflow (a push by hand, a leaked token) carries no valid signature; the workflow itself runs with the key, so a change to .github/workflows/watch.yml is what to review. Verify: python3 scripts/sign_run.py --watch watch --verify; the dashboard verifies it in the browser where WebCrypto supports Ed25519 | |
second_opinion.json | an independent reader on every EVM target's real chain (EIP-1967 admin slot then the ProxyAdmin's owner(), else owner(), else admin(); since 2026-10-10, for a target with none of those, the roles path: AccessControl (ERC-165 id 0x7965db0b) with an enumerable DEFAULT_ADMIN_ROLE gives its first holder (path says member 1 of n), AccessManaged gives authority(), the AccessManager that holds every role; an AccessControl whose holders are not enumerable stays unreadable with access_control: true, its holders live in RoleGranted events; the holder classified as EOA / Safe t-of-n / timelock, OpenZeppelin or Compound-style / contract; on Arbitrum-stack chains a holder without code is un-aliased and looked up on Ethereum L1, alias_of: Uniswap's L1 governance timelock owns the V3 factory on Robinhood Chain that way); Solana: the program's upgrade authority through its ProgramData account, classified as a plain key (on the ed25519 curve), a program-derived address (off the curve, pda), a Squads multisig account, an immutable program, or a program-owned account; since 2026-10-10 a program-derived authority that is the vault 0 of a Squads v4 multisig is read through to it (squads, multisig: the Multisig account, threshold, owners, delay_seconds: the native time lock, config_authority: an address that can change threshold, members and time lock without the members, null when only the members can, owners_kinds: members on the curve (keys) or not; voters, initiators, executors from each member's permission mask, the threshold counting votes only, so keys_lost_to_freeze follows the voting set and effective says 5-of-13 members, 9 can vote: effectively 5-of-9; a note when fewer members can vote than the threshold; former_members_in_account: member entries left in the account beyond the current ones, kept by Squads after a removal; keys_to_act, keys_lost_to_freeze); the vault is resolved by deriving vault 0 of every v4 Multisig account on mainnet (one getProgramAccounts, about 160 000 accounts, 90 s of pure Python once), cached in squads_vaults.json (null = not a vault 0 of a v4 multisig); the same two axes as a Safe then apply (threshold against the multisig score, native time lock of a day or more against a timelock score of 0); every upgradeable program also carries deployed_slot and deployed_at (ProgramData: the slot of the last deployment, dated once per slot): a slot that moved since the previous run is an upgrade executed, listed under upgrades like an EVM implementation change, whoever the authority is compared with the published adminKeyScore and, for a Safe, its threshold with the multisigScore (a threshold of 1 against "at least a 3-of-5", or 3 and more against "no real multisig"; and a live timelock of a day or more in the admin path against a timelockScore of 0): agree, disagree (with the reason; since: the run since which the same holder has been read; shares_admin_with and the top-level clusters: the other tracked contracts the same holder governs, from this reader's own reads and no private index, with tvl_usd: the money behind the whole group, DefiLlama, a protocol counted once per chain; concentration: the share of all tracked money under the five heaviest holders; cluster_changes: holders that govern more tracked contracts than at the previous run, travels with the alarm; an admin change carries its blast radius, how many contracts and how much money the holder governs; oracle_self: the Solana oracle program's own upgrade authority, read on its cluster, a change travels with the alarm), unreadable (never counted as agreement), native (no code at the address on the chain's mainnet: an identifier the methodology derives for a non-EVM entity such as HyperCore's validator set, HIP-3 dexes, HLP or Unit, or a contract that lives on another chain, such as a rollup's own L1 contracts; outside this reader's reach). A disagreement travels with the alarm; it is a finding to review, the scorer may weigh another role. A disagreement a human has reviewed is listed in data/second_opinion_acknowledged.json (key <chain>:<address>, value the note): it then stops travelling with the alarm and is shown as reviewed, with the note (acknowledged_disagreements). changed: the targets whose live admin read differs from the previous run (holder, kind, threshold, owners, and since 2026-10-10 the timelock delay: a delay that drops is the admin path changing): an early warning between two pushes of the oracle, travels with the alarm; since 2026-10-10 each EVM result also carries impl and impl_via: the code behind the proxy (EIP-1967 implementation slot, or the beacon's implementation(); null for a non-proxy and for a proxy of another pattern, such as a Diamond or a FiatToken with its Zeppelin slot, which this reader does not fingerprint; the private pipeline's implementation check covers those), a contract address, public by nature; upgrades: the targets whose implementation differs from the previous run, both reads answered: an upgrade was executed between two runs; it travels with the alarm, fires the default high rule (upgraded field), enters the contract's Atom feed and the chained log (kind upgraded, field impl), and counts as an authority action in score_series.json's calibration; for a Safe holder, since 2026-10-10, modules (the enabled modules, getModulesPaginated: a module executes through the Safe without the t-of-n signatures, so a threshold means little until its modules are read; [] none, null not read) and guard (the transaction guard, storage slot guard_manager.guard.address; null none): both enter the run-to-run comparison, a module that appears or a guard that changes is an admin change; block_start and block_end on every record and blocks per chain (since 2026-10-10): the reads happened between these blocks (public RPCs answer at latest; the span is a few blocks), and the reproduction commands on the pages pin that block where the RPC honours a block tag; holder_tx_count for a plain-key holder: how many transactions the key has sent (context: an operational hot key, or one that never acted); exposure per contract and the top-level exposure ranking (since 2026-10-10): the attacker's question, keys_to_move (the threshold of the admin multisig, 1 for a plain key, null behind a timelock or an unclassified contract), react_hours (the shortest delay on the admin path: holder timelock, Squads time lock, the contract's own), tvl_usd and money_per_key_usd; the ranking lists the most money behind one key, the dashboard shows it under the hero; the money is what the contract holds by its own account when it answers totalAssets() (held_assets.json, money_source = contract, with held_symbol and held_amount), otherwise the protocol's TVL per DefiLlama attached to every tracked contract of that protocol (money_source = protocol_tvl), a retired or peripheral contract included (the dashboard titles say which); delegated_to (since 2026-10-10: an admin or role holder whose account carries only an EIP-7702 delegation, 0xef0100 + a 20-byte address, is a plain key, kind: eoa, and delegated_to names the code its account runs; it used to be read as a contract); posture (since 2026-10-10): one line, the reader's own view of the contract from its fields alone (admin: Safe 5-of-10 of 10 plain keys; delay: none; in flight: upgrade executed since the previous run), shown first on the dashboard row and the page and exported in the CSV as read_live; behind (since 2026-10-10): for a holder read as a bare contract (no Safe, no timelock getter), its own owner(), classified the same way (address, kind eoa / safe / timelock / contract, threshold, owners, delay_seconds, a plain key shortened), and the path says so; the same axes apply to it (a timelock of a day or more under a timelock score of 0, a plain key under an admin-key score of 50 or more, a Safe of 3 or more under a multisig score below 20), and a change of what stands behind the holder is an admin change; self_delay_seconds (since 2026-10-10): timelock() on the contract itself, a delay it enforces on its own admin actions (MetaMorpho vaults: 7 days), null when there is no such getter, 0 when it answers zero; it enters the run-to-run comparison and the timelock axis in both directions: an own timelock of a day or more under a timelock score of 0 is a disagreement, and a confirmed delay in the score (60 and above) with no delay anywhere the reader looked (holder, its modules, its owners, the contract's own timelock) is one too, claimed only for a bare key or a Safe on a contract whose timelock() answers zero; a beacon proxy (EIP-1967 beacon slot, no admin slot) has the owner of its beacon as holder, path: beacon owner() (…) (the target's own owner() is forwarded to the implementation and is not the upgrade authority); a transparent proxy whose admin is an immutable written in its bytecode (the EIP-1967 admin slot reads zero, owner() is forwarded to the implementation) is read from the code, recognised by its own revert text plus upgradeTo and only when one address-shaped constant is left, path: admin fixed in the proxy's bytecode (transparent proxy); pending_admin and pending_via (since 2026-10-10): a transfer in flight, the address designated as the next owner or admin that has not accepted yet (pendingOwner() of Ownable2Step on the contract or on its ProxyAdmin, pendingAdmin() of Compound-style contracts), the earliest warning of a change of hands; it enters the run-to-run comparison and fires the default medium rule (admin_pending field); keys_to_act and keys_lost_to_freeze on every Safe (since 2026-10-10): the two faces of a multisig, t keys collude to act and n-t+1 keys lost mean nobody can act (a 3-of-3 freezes on one lost key; signer_composition.safes_frozen_by_one_lost_key counts them); owners_kinds on every Safe (since 2026-10-10): the signer set in kinds and counts (eoa, safe, timelock, contract, unread), read live on the first 20 signers (the largest tracked Safe has fewer), no signer address (owners_detail, shortened, stays for threshold-1 Safes only, where it decides nested); a change of composition is an admin change; the top-level signer_composition sums them (how many of the tracked admin Safes are multisigs of plain keys only); modules_detail and guard_detail say what each one is, from four reads (code, txCooldown(), avatar(), owner()): eoa (a plain key as a module executes through the Safe alone: a disagreement with the multisig score, listed under disagreements), zodiac-delay with cooldown_seconds (adds a delay), zodiac-module (Roles, Bridge, Exit: scoped), contract; with owner, owner_kind and owner_is_the_safe (self-governed) | schema/second_opinion.schema.json |
second_opinion.md | the same findings as second_opinion.json in words (the disagreements, the upgrades, the admin changes of the run), for a person or an issue body; signer addresses are shortened or redacted like every published file | Markdown |
disputes.json | public disputes of a published score: the registry data/disputes.json (kept by hand, one entry per dispute: id, chain, target, opened, issue, claim, status open / upheld / revised / withdrawn, and resolved, resolution once decided) checked at every run against the tracked set (invalid: an entry naming an untracked contract or lacking a field, with the reason, never counted); by_target keyed <chain>:<address>; open, resolved. Every opening, resolution and reopening is appended to the hash-chained events.jsonl (kinds dispute_opened, dispute_resolved, dispute_reopened, field dispute = the id), so a dispute cannot be opened, closed or edited away quietly; it enters the contract's Atom feed and the dashboard (dispute field of the rules: the newest status on the target, the default rule files an info alert while one is open). Anyone opens one from the per-contract page (issue form) with evidence anyone can re-read; no score changes for a request, only for evidence | schema/disputes.schema.json |
score_series.json | series, keyed <chain>:<address>: every composite score over time, oldest first, {t, composite, from} (push: the on-chain value at its push time; watch: a move the watch recorded in the hash-chained events.jsonl, at its run time; first: the value the first recorded move started from, dated at that move), so a consumer reads a score's whole life, not its latest value; calibration: the authority actions the watch itself observed in the window (window_days, 14: a timelock operation queued or executed against a tracked contract, an admin change read independently, an upgrade executed (implementation changed)) with score_then and band_then, the score and band the contract carried at that moment, by_band and under_40 totals, the list. Read as: do real actions fall on the contracts the oracle scores low? Small counts prove little, the file grows with every run; an operation whose instructions were not read (undecoded, the Squads proposals) is not counted, it may be a treasury transfer. The dashboard draws the series (sparkline in each row) and the per-contract JSON carries it as history; methodology_changes keyed by chain, from the chained log: the dates after which the points of that chain are not comparable with the earlier ones (the dashboard and the per-contract pages say so next to the series) | schema/score_series.schema.json |
feed/<oracle-slug>/<address>.xml | one Atom feed per tracked contract: its score moves, the alerts that fired on it, the operations queued against it; linked from each share page (<link rel="alternate">) | Atom |
feed/protocol/<slug>.xml | one Atom feed per protocol (mechanism 98, since 2026-10-10), <slug> the DefiLlama protocol slug of tvl.json: the entries of the feeds of all its tracked contracts, newest first (50 at most), each titled [Chain 0xabcd…ef12] ... and with an id prefixed by chain and contract so that two contracts sharing an operation both appear; empty until something happens, then dated from the last push of its contracts; a contract with no DefiLlama protocol has its own feed only; a slug that is not a safe file name is made one | |
tvl.json | DefiLlama TVL per target, matched by hand (scripts/tvl_match.py), with the scope of each number | schema/tvl.schema.json |
names.json | the name of the targets the dashboard has no title for, read once at the site build: on-chain name() and symbol(), else the verified source's contract name on Sourcify with the suffix (verified source) (null = neither; a node answer of 0x is never taken as "no name") | `{"<chain>:<address>": "Steakhouse USDC (steakUSDC)" \ |
pending_ops.json | operations queued behind the watched timelocks (ready_at, state, selector, target, tx) and what cleared recently; an operation that became executable more than 7 days ago is not listed, in any file (since 2026-10-15); since 2026-10-10 results also carries the timelocks the independent reader found in admin paths on every chain (discovered: true, governs: the tracked contracts behind it, log_rpc, window_blocks, window_days_est, window_note): their queues are read in chunks over a bounded window on public RPCs, so an operation queued before that window is not seen, which the record says; the hand-written watchlist keeps its whole-history reads; since 2026-10-10 also the Squads v4 multisigs the reader resolved on Solana (family: squads, chain: solana): their proposals as a queue, pending = approved and live (past the multisig's stale index: executable by any executor from ready_at = approval + time lock, state ready / waiting), recent = executed in the window, active = awaiting votes, proposals = counts by status; each live or recent proposal's transaction account is decoded (decoded: program upgrade, program authority change, transfer, multisig config, call; what: the instructions in words, with the program concerned; target: the program the operation hits; touches_governed: whether it is one of the tracked programs; undecoded when the account could not be read); a BPF loader Upgrade of a tracked program approved and executable is the sharpest finding this watch makes on Solana. EVM operations (since 2026-10-10): signature named from a keccak-checked table of authority selectors or the public signature database (cached in selectors.json) and decoded: upgrade, authority change, pause, nested timelock, cross-chain message, parameter change, value transfer or empty call, unknown; the review queue lists a queued upgrade, authority change or pause even before its day | schema/pending_ops.schema.json |
selectors.json | the cache of function names resolved for queued operations (scripts/classify_ops.py): a 4-byte selector to its signature, from a table of authority selectors checked by keccak, else from the public signature database; written once per new selector, read by the next runs so a queued call is named without asking again | (cache, no schema) |
pending_ops.ics | the same queue as a calendar feed | iCalendar |
safe_changes.json | Safe signer, threshold, module and guard events over 7 days, per ecosystem, with the ones not read; since 2026-10-10 each ecosystem says days_read (a chain that serves only short log ranges gets a shorter, stated window: Monad 100 blocks per call, about 7 hours read every 6 hours; HyperEVM 3 days) and control_witness: when the tracked Safes executed nothing in the window, executions from any Safe on the window's tail through the same endpoint prove it serves logs, and the status reads ok (quiet…) instead of UNREAD | |
keys_summary.json | how many root signers sit on several tracked groups, and the union of groups the ten most connected sit on; no address (the full index is a targeting map and is not published) | |
paper_trail.json | address → dated documents of data/ and docs/ that mention it | |
badge.json | shields.io endpoint badge of the whole watch (targets read, stale) | shields endpoint |
prev/<oracle-slug>.json | the previous run's scores, kept so that moves.json can say which axis moved | same as scores/ |
moves.json | why each score moved: the five sub-scores against the previous run, axis by axis, with a plain sentence, and methodology_changed (true when the oracle's methodologyHash changed with the same push: part of the move may be a scorer update, not the target); prev/ keeps the previous run's scores | |
badge/<oracle-slug>/<address>-req.json | shields.io endpoint badge: how many of the 6 requirements the setup meets | shields endpoint |
badge/<oracle-slug>/<address>-so.json | shields.io endpoint badge: what the independent reader says about this target (agrees / disagrees / reviewed / not readable, with what was read: Safe t-of-n, timelock, plain key, program-derived…) | shields endpoint |
alerts.json | rules of ../rules.json firing now (active, each with its evidence: the queued operation, tx, when it executes; for the two balance rules, balance: symbol, from, to, change_pct, since; the same numbers are in the history, the alerts feed, the per-contract feeds, the notification and the dashboard) and their history | schema/alerts.schema.json |
alerts.xml | the same alerts as an Atom feed (one entry per rule × target, updated when it fires again) | Atom |
digest.md | the daily report (six read-only checks); since 2026-10-10 it opens with the review queue (what needs a person, from review_queue.md), or with a line saying the queue was not read: a missing list is never shown as an empty one | Markdown |
og/<oracle-slug>/<address>.png, og/summary.png | share cards, 1200×630 | PNG |
badge/<oracle-slug>/<address>.json | shields.io endpoint badge per target | shields endpoint |
badge/protocol/<defillama-slug>.json | shields.io endpoint badge per protocol: its weakest tracked deployment and how many contracts that covers ("weakest 55/100 (stale), 4 contracts"), same colour bands as the per-contract badge | shields endpoint |
badge/alerts.json | shields.io endpoint badge: how many rules fire right now | shields endpoint |
status.json | the run's health in one record: state ok / degraded / alarm, the reasons, and the counters (oracles read, stale, alarms, chain, signed, second opinion); for a monitor that alerts on state; checks.disputes_open: open public disputes, a review pending counts as degraded | |
main_activity.json | how old the last commit on main is (main_last_commit_age_days, level ok / warn / unknown): GitHub disables the scheduled workflows of a public repository after 60 days without repository activity, and they run only on the default branch, so from 45 days the Watch sends one Telegram message (again at 52 and at 58 days, each once: notified_tier is the last tier sent) and, when a token that can write issues is set, opens one issue asking for a small commit on main; status.json repeats the number, scripts/watch_age.py prints it as a NOTE and, from 45 days, a WARN | |
badge/integrity.json | shields.io endpoint badge: signed or not, chain intact or broken, second opinion agrees or N to review; red / orange / green with the state | shields endpoint |
feed.xml | Atom feed of moves and alarms, plus the independent reader's findings (admin_change, admin_grew), rebuilt after that reader ran | Atom |
Verifying the signature from your own service, without this repository's code (Node 18+, raw Ed25519 key wrapped in SPKI DER; the manifest is canonical JSON: keys sorted, no spaces):
const crypto = require('crypto');
const sig = await (await fetch('https://raw.githubusercontent.com/RealSpap/authority-risk-oracle-showcase/watch-data/signature.json')).json();
const canon = (v) => Array.isArray(v) ? '[' + v.map(canon).join(',') + ']' : (v && typeof v === 'object') ? '{' + Object.keys(v).sort().map(k => JSON.stringify(k) + ':' + canon(v[k])).join(',') + '}' : JSON.stringify(v);
const key = crypto.createPublicKey({ key: Buffer.concat([Buffer.from('302a300506032b6570032100', 'hex'), Buffer.from(sig.public_key, 'hex')]), format: 'der', type: 'spki' });
console.log(sig.signed && crypto.verify(null, Buffer.from(canon(sig.manifest)), key, Buffer.from(sig.signature, 'hex'))); // true: every file listed in sig.manifest.files hashes to what the key signed
In Python: from nacl.signing import VerifyKey; VerifyKey(bytes.fromhex(sig["public_key"])).verify(json.dumps(sig["manifest"], sort_keys=True, separators=(",", ":")).encode(), bytes.fromhex(sig["signature"])). Compare public_key with the one the README publishes before trusting it.
What the checks do not prove (written down on 2026-10-11 after a security review). (1) The signature is checked with the public key written inside signature.json: a forged manifest signed with a random key still says "valid". It proves the files match what that key signed, not who holds the key; the maintainer's public key is 11baca3ef6a69de4901c185f6cbbd3f93318fbb74f9961a8d39aead841adf409, pinned in the dashboard (ARO_PUBKEY) and written here: compare it with the public_key of signature.json, and treat a signature made by any other key as invalid. Before 2026-10-11 21:18 UTC the runs were unsigned (signed: false), and a run that cannot read the secret says so. (2) verify_chain.py checks that every line commits to the previous one; it cannot see that the end of the log was cut off. The head of the chain is pinned in the signed manifest (events_chain_head), so compare that value, not only "chain intact". (3) An alarm is a line of events.jsonl and an issue in the maintainer's private repository, and only when the ISSUES_TOKEN secret is set: without it the run prints "alarm raised but no ISSUES_TOKEN" and nobody is told. An oracle that stops answering raises one alarm, then stays silent while it stays unreadable: read status.json for the current state.
Auditing all of this from outside, in one command and with the standard library only: python3 scripts/audit_public.py fetches the published files, checks the event-log chain, the signed manifest and its signature (pynacl), the schemas, the methodology registry, and re-reads a random sample of scores from the oracle contracts over public RPCs; --watch watch audits a local checkout, --sample N sets the sample, --ref <commit> audits a past run as it was committed (its signed manifest, chain and schemas), since every run commits its manifest. Exit 0 means every check passed.
Each share page's JSON also carries consumer_example (what src/ExampleConsumer.sol answers today for the target: revert when stale, collateral factor 0 under 60, 50% to 80% above) and cheapest_fix (the smallest single-axis move that reaches the next band, from the published formula 40% admin + 30% multisig + 30% timelock).
Reproducing a read: every share page (/oracle/p/<chain>-<address>.html and its .json twin, field reproduce) carries the exact eth_call (oracle, calldata, block of the build) that re-reads the score from the public RPC; the 6th word of the answer is the composite. One RPC (Hyperliquid testnet) answers for the latest block whatever block is asked, measured on 2026-10-10 with the same trackedTargetsCount() at block 1 and at latest: those pages say so and pin latest (block_honoured: false).
What changed since a moment: the dashboard takes #since=<UTC time> (for instance #chain=all&since=2026-10-10T06:00Z) and lists every move and alarm of events.jsonl recorded after it; the button under the hero copies such a link marked at the current time.
Subscribing: every run commits to watch/, so a GitHub webhook on this repository (event push, filter on paths under watch/) tells your service within seconds; the Atom feeds (feed.xml, alerts.xml) and the calendar (pending_ops.ics) need no account; the dashboard's "notify me" polls latest.json every 10 minutes in the browser.
Signed webhook for a machine. If the workflow has the secrets ALERT_WEBHOOK_URL (https only) and ALERT_WEBHOOK_SECRET, every run that raises new alerts POSTs {"event":"alerts","version":1,"sent_utc","alerts":[{rule,severity,chain,target,title,score,delta,tvl_usd,first_seen}],"page"} (ten at most). Headers: X-ARO-Timestamp (unix seconds) and X-ARO-Signature: sha256=<hex>, the HMAC-SHA256 of <timestamp>.<raw body> with your secret. Check the signature on the raw bytes with a constant-time comparison and refuse a timestamp more than five minutes old. This repository is public: the secret and the URL are the owner's, set in the repository settings, never committed.
Versioning: fields are only added, never renamed or removed, within v1. A breaking change becomes watch/v2/ with this file updated first and v1 kept for at least 90 days. Attribution when you reuse the data: "Authority Risk Oracle, realspap.github.io/oracle" with a link; the code is MIT (repository LICENSE), the data CC BY 4.0, as stated in the repository README.
Signer addresses: every root-signer address is shortened (except a tracked contract that also signs elsewhere) (0x1234…abcd) in these files by scripts/redact_signers.py; the index of who can reach what is never published.
Not a prediction: the composite measures exposure (how much one compromise can do), not the chance of being hit; see data/finding_2026-10-01-backtest-victims-vs-controls.md.
https://realspap.github.io/oracle/p/<chain>-<address>.html and the same path with .json (CC BY 4.0), rebuilt when the site is published; p/index.json lists them (about, count, addresses). Fields of one JSON twin:
| field | meaning |
|---|---|
name, named_from, chain, address | the dashboard's title (or the on-chain name, or "Unnamed contract"), where the name comes from (dashboard, on-chain name() and symbol(), verified source name (Sourcify), DefiLlama match, unnamed), the chain label, the target id (checksum case kept for EVM, base58 case for Solana) |
composite, band | the published composite and its band: critical 0 to 19, weak 20 to 39, moderate 40 to 59, decent 60 to 79, strong 80 to 100 |
sub | the five readings: adminKeyScore, multisigScore, timelockScore, oracleAuthorityScore, crossExposureScore |
requirements | six {label, met} checks (admin not a bare key, at least 3-of-5, a confirmed delay, bypass paths ruled out, price feed not under one hand, no shared key) |
exit_window | the plain-words reading of the timelock score |
tvl_usd, category, at_risk_usd | DefiLlama figures when matched (at_risk_usd = TVL x (100 - composite) / 100, a weight, not a prediction); null otherwise |
narrative | the dashboard's hand-written card for this target, when there is one: summary, tag, detail (the path behind the score); null otherwise |
score_account, score_cluster | Solana only: the PDA that holds the score (seeds ["score", target]) and the cluster it lives on (devnet); null on EVM |
last_pushed, stale, methodology_hash, block | when the score was pushed on-chain (unix seconds), whether the oracle reported it stale, the methodology hash it was scored under, the block the watch read it at: all as of the site build (these pages are static; the dashboard reads the oracles live) |
live, page, license | the dashboard deep link, this page's URL, the data licence |
independent_read | the independent reader's finding for this contract, null when it was not read: kind (eoa, safe, timelock, squads, contract, ...), holder (a plain key shortened), path (how the holder was reached), threshold and owners for a Safe or a Squads multisig, delay_seconds, disagree (the reader's disagreement with the score, in words) and acknowledged, since, shares_admin_with (other tracked contracts under the same holder), and since 2026-10-10 delegated_to (a plain key whose account carries an EIP-7702 delegation: the code it runs), behind (a bare contract holder and the hand behind it), posture (the whole finding in one sentence), exposure (keys_to_move, react_hours, tvl_usd, money_per_key_usd, money_source), owners_kinds (what the signers are, counts only), keys_to_act, keys_lost_to_freeze, self_delay_seconds, pending_admin and pending_via (an admin transfer in flight) |
keys_to_move, react_hours, money_per_key_usd | the attacker's question for this contract (scripts/second_opinion.py): the fewest signatures that can move it, whole hours of the shortest delay that binds them (0 when there is none), and the money behind one key; each null when not read (never zero) |
independent_read_reproduce | the exact RPC calls (rpc, steps: what, method, params) that redo the reader's first hops yourself, pinned to a block; null when the reader has nothing to reproduce (Solana, a native entity, an unreadable target: 73 of 179 on 10/10) |
reproduce | the exact eth_call that re-reads the published score from the oracle (rpc, to, data, block, composite_word, ...); null on Solana, where the score is an account |
consumer_example | what the example consumer contract would do with this score, verdict (revert for a stale score, which is every score while the oracles are stale) and text in words |
cheapest_fix | the smallest change that moves the contract up a band: band and next score, the axis to change, its from and to score, what in words; null when no single change moves it up a band (19 of 179 on 10/10) |
main_gap | the first of the six published requirements the contract does not meet, {requirement, reason}, in the dashboard's own words; null when all six are met. cheapest_fix is the smallest step into the next band; main_gap says what is actually missing, which is not always the same thing |
not_assessed | what was NOT read for this contract, in words, generated from the same fields (a stale score, no independent read of who controls it, keys to move, time to react, money behind one key, no TVL match); an empty list means everything the label shows was read. A page that hides its gaps looks complete: this makes them part of the label |
version, generatedAt | version is the format of this file, v1 (a breaking change gets v2 under a new path); generatedAt is the UTC time the page was built, not the time of the reading (see block and last_pushed). The same file is served at https://realspap.github.io/oracle/v1/p/<chain>-<address>.json, and https://realspap.github.io/oracle/v1/scores.json lists every contract with its score, band, keys to move, hours to react, stale flag and the address of its file |
history | the published score changes, [{t, composite, from}], oldest first; empty when the contract has only ever had one push |
feed | the Atom feed of this one contract: its score moves, alerts and queued operations |
dispute | the newest public dispute on this contract: id, status, opened, resolved, issue (the link); null when there is none |
held | what the contract holds by its own account when it answers totalAssets() (an ERC-4626 vault): symbol, amount, price_usd, held_usd, and stale, price_stale, amount_change_pct, vanished (see held_assets.json); null otherwise |
paused | true when the contract answers paused() or isPaused() true at the build, null otherwise (it never says false: a contract without the getter is not "running") |
side_roles, side_pending | who holds the secondary roles (pauser(), guardian(), curator(), ...): {role: {kind, holder, threshold, owners, ...}}, and the announced change of guardian or delay, {guardian, timelock} with valid_at and shorter; null when there are none (see side_roles.json) |
safe_activity | the Safe(s) that administer this contract and what they have done on-chain (mechanism 87, from safe_activity.json): [{safe, nonce, idle_since, last_moved, never_executed}], safe the chain and the shortened address, nonce the transactions executed, idle_since the first watch run that read this nonce, never_executed true at nonce 0; null when no administering Safe is known or the Safe did not answer |
admin_probe | known contracts that hold the admin role of this contract when its role holders cannot be listed (mechanism 91, from admin_probe.json): {role, probed, answered, holders: [{holder, kind, threshold, owners}], complete: false}; proof of the holders listed, never a complete list; null when none was seen |
The JSON Schema of each published file, as in watch/schema/.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/admin_probe.json v1",
"type": "object",
"required": [
"generated_utc",
"unreadable_targets",
"probed_targets",
"with_holder",
"by_target"
],
"properties": {
"generated_utc": {
"type": "string"
},
"unreadable_targets": {
"type": "integer",
"description": "targets of the independent reader that stayed unreadable, on the chains this probe covers"
},
"probed_targets": {
"type": "integer",
"description": "of those, AccessControl-style contracts where at least one candidate answered"
},
"with_holder": {
"type": "array",
"items": {
"type": "string"
},
"description": "<chain>:<target> keys where a known contract holds DEFAULT_ADMIN_ROLE"
},
"by_target": {
"type": "object",
"description": "<chain>:<target> -> role, method, probed (candidates asked), answered (candidates that answered), holders [{holder, kind, threshold, owners, delay_seconds}], complete (always false: absence proves nothing)"
},
"what": {
"type": "string"
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/alerts.json v1",
"type": "object",
"required": [
"evaluated_utc",
"rules",
"active",
"history"
],
"properties": {
"evaluated_utc": {
"type": "string"
},
"run_utc": {
"type": [
"string",
"null"
]
},
"rules": {
"type": "array",
"items": {
"type": "string"
}
},
"errors": {
"type": "array"
},
"active": {
"type": "array",
"items": {
"type": "object",
"required": [
"rule",
"severity",
"chain",
"target",
"title",
"score",
"first_seen",
"new"
],
"properties": {
"rule": {
"type": "string"
},
"severity": {
"enum": [
"info",
"medium",
"high"
]
},
"chain": {
"type": "string"
},
"target": {
"type": "string"
},
"title": {
"type": "string"
},
"score": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"delta": {
"type": [
"integer",
"null"
]
},
"tvl_usd": {
"type": [
"number",
"null"
]
},
"first_seen": {
"type": "string"
},
"new": {
"type": "boolean"
},
"evidence": {
"type": "array",
"items": {
"type": "object",
"properties": {
"timelock": {
"type": "string"
},
"chain": {
"type": "string"
},
"tx": {
"type": [
"string",
"null"
]
},
"selector": {
"type": [
"string",
"null"
]
},
"signature": {
"type": [
"string",
"null"
]
},
"ready_at": {
"type": [
"integer",
"null"
]
},
"state": {
"type": [
"string",
"null"
]
},
"timelock_address": {
"type": [
"string",
"null"
]
},
"family": {
"type": [
"string",
"null"
]
},
"id": {
"type": [
"string",
"null"
]
}
}
}
},
"previous_run": {
"type": [
"string",
"null"
]
},
"notified": {
"type": "boolean",
"description": "delivered to the configured channels, or no channel configured; false = sent again at the next run"
},
"balance": {
"type": "object",
"properties": {
"symbol": {
"type": [
"string",
"null"
]
},
"from": {
"type": "number"
},
"to": {
"type": "number"
},
"change_pct": {
"type": "number"
},
"since": {
"type": [
"string",
"null"
]
}
}
},
"pause": {
"type": "object"
},
"side_role": {
"type": "array"
},
"safe_wake": {
"type": "object",
"description": "the Safe that governs this contract, its shortened address, the transactions it executed (delta), the observed idle days before them and the previous run"
}
}
}
},
"history": {
"type": "array",
"items": {
"type": "object",
"required": [
"key",
"rule",
"severity",
"first_seen",
"last_seen",
"times"
],
"properties": {
"balance": {
"type": "object"
},
"pause": {
"type": "object"
},
"side_role": {
"type": "array"
},
"key": {
"type": "string"
},
"last_seen": {
"type": "string"
},
"times": {
"type": "integer"
},
"chain": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"delta": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"first_seen": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"rule": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"score": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"severity": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"target": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"title": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"tvl_usd": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"safe_wake": {
"type": "object",
"description": "the Safe that governs this contract, its shortened address, the transactions it executed (delta), the observed idle days before them and the previous run"
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/coverage.json v1",
"type": "object",
"required": [
"generated_utc",
"chains"
],
"properties": {
"generated_utc": {
"type": "string"
},
"chains": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"targets",
"independent_read",
"proxies_fingerprinted",
"safes",
"timelocks_in_admin_path",
"timelock_queues_watched",
"tvl_matched",
"blind_spots"
],
"properties": {
"targets": {
"type": "integer"
},
"independent_read": {
"type": "object",
"required": [
"readable",
"unreadable",
"native"
]
},
"proxies_fingerprinted": {
"type": "integer"
},
"safes": {
"type": "integer"
},
"safes_modules_read": {
"type": "integer"
},
"timelocks_in_admin_path": {
"type": "integer"
},
"timelock_queues_watched": {
"type": "integer"
},
"safe_events": {
"type": [
"object",
"null"
]
},
"tvl_matched": {
"type": "integer"
},
"blind_spots": {
"type": "array",
"items": {
"type": "string"
}
},
"pause_answered": {
"description": "contracts of this chain that answer paused() or isPaused() (non-stale records); null when pause_state.json is missing: not read, never zero"
},
"roles_read": {
"description": "contracts with a secondary role (pauser, guardian, curator ...) read by side_roles.json; null when the file is missing"
},
"admin_safes_nonce_read": {
"description": "admin Safes of this chain whose nonce was read (safe_activity.json, non-stale); null when the file is missing"
},
"admin_role_probed": {
"description": "unreadable contracts of this chain probed for their admin role (admin_probe.json); null when the file is missing"
},
"held_read": {
"description": "contracts with a money figure from their own totalAssets() instead of a DefiLlama protocol"
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/cross_chain.json v1",
"type": "object",
"required": [
"generated_utc",
"protocols",
"differing",
"items"
],
"properties": {
"generated_utc": {
"type": "string"
},
"protocols": {
"type": "integer"
},
"differing": {
"type": "integer"
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"slug",
"protocol",
"chains",
"weakest_chain",
"postures_differ",
"text"
],
"properties": {
"chains": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"chain",
"contracts",
"composite_min",
"composite_max",
"admin_kinds",
"postures"
]
}
},
"postures_differ": {
"type": "boolean"
},
"spread": {
"type": "integer"
},
"protocol": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"slug": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"strongest_chain": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"strongest_composite": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"text": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"weakest_chain": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"weakest_composite": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/disputes.json v1",
"type": "object",
"required": [
"open",
"resolved",
"disputes",
"by_target",
"invalid"
],
"properties": {
"run_utc": {
"type": [
"string",
"null"
]
},
"open": {
"type": "integer"
},
"resolved": {
"type": "integer"
},
"invalid": {
"type": "array",
"items": {
"type": "object",
"required": [
"why"
]
}
},
"by_target": {
"type": "object"
},
"disputes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"chain",
"target",
"opened",
"issue",
"claim",
"status",
"key"
],
"properties": {
"status": {
"type": "string",
"enum": [
"open",
"upheld",
"revised",
"withdrawn"
]
},
"resolved": {
"type": [
"string",
"null"
]
},
"resolution": {
"type": [
"string",
"null"
]
},
"score_now": {
"type": [
"integer",
"null"
]
},
"chain": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"claim": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"id": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"issue": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"key": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"opened": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"target": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/held_assets.json v1",
"type": "object",
"required": [
"generated_utc",
"asked",
"count",
"unpriced",
"by_target"
],
"properties": {
"generated_utc": {
"type": "string"
},
"asked": {
"type": "integer"
},
"count": {
"type": "integer"
},
"unpriced": {
"type": "array",
"items": {
"type": "string"
}
},
"by_target": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"asset",
"total_assets",
"held_usd"
],
"properties": {
"asset": {
"type": "string"
},
"symbol": {
"type": [
"string",
"null"
]
},
"decimals": {
"type": [
"integer",
"null"
]
},
"total_assets": {
"type": "string"
},
"amount": {
"type": [
"number",
"null"
]
},
"price_usd": {
"type": [
"number",
"null"
]
},
"price_at": {
"type": [
"integer",
"null"
]
},
"held_usd": {
"type": [
"number",
"null"
]
},
"stale": {
"type": "boolean"
},
"stale_since": {
"type": [
"string",
"null"
]
},
"price_stale": {
"type": "boolean"
},
"prev_amount": {
"type": "number"
},
"prev_at": {
"type": [
"string",
"null"
]
},
"amount_change_pct": {
"type": "number"
},
"vanished": {
"type": "boolean"
},
"vanished_since": {
"type": [
"string",
"null"
]
}
}
}
},
"what": {
"type": "string"
},
"unread_chains": {
"type": "array",
"items": {
"type": "string"
}
},
"stale": {
"type": "integer"
},
"drops": {
"type": "array",
"items": {
"type": "object"
}
},
"vanished": {
"type": "array",
"items": {
"type": "string"
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/impl_sources.json v1",
"type": "object",
"required": [
"run_utc",
"checked",
"verified",
"unverified",
"by_target"
],
"properties": {
"run_utc": {
"type": "string"
},
"checked": {
"type": "integer"
},
"verified": {
"type": "integer"
},
"unverified": {
"type": "integer"
},
"not_answered": {
"type": "integer"
},
"chains_not_on_sourcify": {
"type": "array"
},
"by_target": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"impl"
],
"properties": {
"impl": {
"type": "string"
},
"verified": {
"type": [
"boolean",
"null"
]
},
"match": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"compiler": {
"type": [
"string",
"null"
]
},
"verified_at": {
"type": [
"string",
"null"
]
},
"repo_url": {
"type": [
"string",
"null"
]
},
"source": {
"type": "string"
},
"note": {
"type": [
"string",
"null"
]
},
"via": {
"type": [
"string",
"null"
]
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Authority Facts v0.1: the JSON twin of one label",
"description": "The fields every label must carry (specification section 2). A line that cannot be read is null, never 0. Extra fields are allowed.",
"type": "object",
"required": ["chain", "address", "composite", "band", "keys_to_move", "react_hours", "money_per_key_usd", "sub", "requirements", "last_pushed", "stale", "methodology_hash", "block", "license"],
"properties": {
"name": {"type": "string"},
"chain": {"type": "string"},
"address": {"type": "string"},
"composite": {"type": "integer"},
"band": {"enum": ["critical", "weak", "moderate", "decent", "strong"]},
"keys_to_move": {"type": ["integer", "null"]},
"react_hours": {"type": ["integer", "null"]},
"money_per_key_usd": {"type": ["number", "null"]},
"sub": {
"type": "object",
"required": ["adminKeyScore", "multisigScore", "timelockScore"],
"properties": {"adminKeyScore": {"type": "integer"}, "multisigScore": {"type": "integer"}, "timelockScore": {"type": "integer"}}
},
"requirements": {"type": "array", "items": {"type": "object", "required": ["label", "met"], "properties": {"label": {"type": "string"}, "met": {"type": "boolean"}}}},
"independent_read": {"type": ["object", "null"]},
"last_pushed": {"type": ["integer", "null"]},
"stale": {"type": "boolean"},
"methodology_hash": {"type": "string"},
"block": {"type": ["integer", "null"]},
"license": {"type": "string"}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/latest.json v1",
"type": "object",
"required": [
"run_utc",
"oracles",
"total_targets",
"stale_total",
"movers",
"alerts",
"unread"
],
"properties": {
"run_utc": {
"type": "string",
"format": "date-time"
},
"previous_run_utc": {
"type": [
"string",
"null"
]
},
"total_targets": {
"type": "integer"
},
"stale_total": {
"type": "integer"
},
"oracles": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"slug",
"read_ok"
],
"properties": {
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"oracle": {
"type": "string"
},
"read_ok": {
"type": "boolean"
},
"block": {
"type": [
"integer",
"null"
]
},
"targets": {
"type": "integer"
},
"stale": {
"type": "integer"
},
"lastUpdated": {
"type": [
"integer",
"null"
]
},
"maxStaleness": {
"type": [
"integer",
"null"
]
},
"kind": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"self_watch": {
"description": "(observed as object on 2026-10-10; not enforced: a documentation entry)"
}
}
}
},
"movers": {
"type": "array"
},
"alerts": {
"type": "array"
},
"unread": {
"type": "array",
"items": {
"type": "string"
}
},
"note": {
"type": "string"
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/methodology.json v1",
"type": "object",
"required": [
"generated_utc",
"hashes",
"mixed",
"changes"
],
"properties": {
"generated_utc": {
"type": "string"
},
"hashes": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"oracles",
"targets"
]
}
},
"mixed": {
"type": "array"
},
"changes": {
"type": "array",
"items": {
"type": "object",
"required": [
"oracle",
"from",
"to",
"targets",
"run_utc"
]
}
},
"method_version": {
"type": [
"string",
"null"
]
},
"method_version_date": {
"type": [
"string",
"null"
]
},
"registry_read": {
"type": "boolean"
},
"unregistered": {
"type": "array"
}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/pause_state.json v1",
"type": "object",
"required": [
"generated_utc",
"asked",
"count",
"paused",
"changes",
"by_target"
],
"properties": {
"generated_utc": {
"type": "string"
},
"asked": {
"type": "integer"
},
"count": {
"type": "integer"
},
"paused": {
"type": "array",
"items": {
"type": "string"
}
},
"changes": {
"type": "array",
"items": {
"type": "object",
"required": [
"target",
"change"
],
"properties": {
"target": {
"type": "string"
},
"change": {
"enum": [
"paused",
"unpaused"
]
},
"since": {
"type": [
"string",
"null"
]
}
}
}
},
"stale": {
"type": "integer"
},
"unread_chains": {
"type": "array",
"items": {
"type": "string"
}
},
"by_target": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"paused",
"via"
],
"properties": {
"paused": {
"type": "boolean"
},
"via": {
"enum": [
"paused()",
"isPaused()"
]
},
"since": {
"type": [
"string",
"null"
]
},
"change": {
"enum": [
"paused",
"unpaused"
]
},
"from": {
"type": "boolean"
},
"previous_run": {
"type": [
"string",
"null"
]
},
"stale": {
"type": "boolean"
},
"stale_since": {
"type": [
"string",
"null"
]
}
}
}
},
"what": {
"type": "string"
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/pending_ops.json v1",
"type": "object",
"required": [
"results"
],
"properties": {
"days": {
"type": "integer"
},
"results": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"chain",
"address",
"status"
],
"properties": {
"name": {
"type": "string"
},
"chain": {
"type": "string"
},
"address": {
"type": "string"
},
"family": {
"type": "string"
},
"status": {
"type": "string"
},
"delay": {
"type": [
"integer",
"null"
]
},
"pending": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ready_at": {
"type": "integer"
},
"state": {
"type": "string"
},
"selector": {
"type": [
"string",
"null"
]
},
"signature": {
"type": [
"string",
"null"
]
},
"target": {
"type": "string"
},
"tx": {
"type": "string"
},
"id": {
"type": "string"
},
"calls": {
"type": "integer"
}
}
}
},
"recent": {
"type": "array",
"items": {
"type": "object",
"properties": {
"queued_at": {
"type": "integer"
},
"still_queued": {
"type": "boolean"
},
"selector": {
"type": [
"string",
"null"
]
},
"target": {
"type": "string"
},
"tx": {
"type": "string"
}
}
}
},
"active": {
"description": "(observed as array on 2026-10-10; not enforced: a documentation entry)"
},
"discovered": {
"description": "(observed as boolean on 2026-10-10; not enforced: a documentation entry)"
},
"governs": {
"description": "(observed as array on 2026-10-10; not enforced: a documentation entry)"
},
"grace": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"latest_queued": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"log_rpc": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"proposals": {
"description": "(observed as object on 2026-10-10; not enforced: a documentation entry)"
},
"queued_events": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"transaction_index": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"window_blocks": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"window_days_est": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"window_note": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/review_queue.json v1",
"type": "object",
"required": [
"generated_utc",
"total",
"by_priority",
"items"
],
"properties": {
"generated_utc": {
"type": "string"
},
"total": {
"type": "integer"
},
"by_priority": {
"type": "object"
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"priority",
"kind",
"text",
"todo",
"source"
],
"properties": {
"priority": {
"type": "integer",
"minimum": 1,
"maximum": 3
},
"kind": {
"type": "string"
},
"chain": {
"type": [
"string",
"null"
]
},
"target": {
"type": [
"string",
"null"
]
},
"text": {
"type": "string"
},
"todo": {
"type": "string"
},
"source": {
"type": "string"
},
"first_seen": {
"type": "string"
},
"age_days": {
"type": "number"
},
"overdue": {
"type": "boolean"
}
}
}
},
"overdue": {
"type": "integer"
},
"overdue_days": {
"type": "number"
},
"oldest_days": {
"type": "number"
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/risk_by_cause.json v1",
"type": "object",
"required": [
"generated_utc",
"tracked_tvl_usd",
"contracts",
"causes"
],
"properties": {
"generated_utc": {
"type": "string"
},
"tracked_tvl_usd": {
"type": "number"
},
"contracts": {
"type": "integer"
},
"causes": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"label",
"contracts",
"tvl_usd",
"targets"
],
"properties": {
"label": {
"type": "string"
},
"contracts": {
"type": "integer"
},
"with_tvl": {
"type": "integer"
},
"tvl_usd": {
"type": "number"
},
"share_of_tracked": {
"type": [
"number",
"null"
]
},
"targets": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/safe_activity.json v1",
"type": "object",
"required": [
"generated_utc",
"wake_days",
"safes",
"read",
"woke",
"changes",
"by_safe"
],
"properties": {
"generated_utc": {
"type": "string"
},
"wake_days": {
"type": "integer",
"description": "idle stretch, in days of observed time, after which a move counts as a wake"
},
"safes": {
"type": "integer",
"description": "Safes that are the admin of a tracked contract (holder, or the Safe behind a bare-contract holder)"
},
"read": {
"type": "integer",
"description": "of those, how many answered nonce() this run"
},
"woke": {
"type": "array",
"items": {
"type": "string"
},
"description": "<chain>:<shortened Safe address> labels of the Safes whose nonce moved after wake_days or more unchanged"
},
"changes": {
"type": "array",
"description": "every Safe whose nonce moved since the previous run: safe, delta (transactions executed), idle_days (observed idle time before the move), woke, regressed (the nonce went down: the Safe was replaced), governs (the <chain>:<contract> keys), previous_run"
},
"never_executed": {
"type": "array",
"items": {
"type": "string"
},
"description": "<chain>:<shortened Safe address> labels of the Safes still at nonce 0: an admin path that has never been used"
},
"stale": {
"type": "integer"
},
"unread_chains": {
"type": "array",
"items": {
"type": "string"
}
},
"by_safe": {
"type": "object",
"description": "<chain>:<hash of the Safe address> -> safe (the <chain>:<shortened address> label), nonce, observed_since, idle_since (first run that read this nonce), last_moved, never_executed, governs, stale / stale_since when the Safe did not answer this run"
},
"what": {
"type": "string"
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/score_series.json v1",
"type": "object",
"required": [
"run_utc",
"targets",
"moves_recorded",
"series",
"calibration"
],
"properties": {
"run_utc": {
"type": "string"
},
"targets": {
"type": "integer"
},
"moves_recorded": {
"type": "integer"
},
"methodology_changes": {
"type": "object"
},
"series": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "object",
"required": [
"t",
"composite",
"from"
],
"properties": {
"t": {
"type": "string"
},
"composite": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"from": {
"type": "string",
"enum": [
"push",
"watch",
"first"
]
}
}
}
}
},
"calibration": {
"type": "object",
"required": [
"window_days",
"actions",
"under_40",
"by_band",
"list"
],
"properties": {
"window_days": {
"type": "integer"
},
"actions": {
"type": "integer"
},
"under_40": {
"type": "integer"
},
"by_band": {
"type": "object"
},
"list": {
"type": "array",
"items": {
"type": "object",
"required": [
"kind",
"key",
"t"
],
"properties": {
"score_then": {
"type": [
"integer",
"null"
]
},
"band_then": {
"type": [
"string",
"null"
]
}
}
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/scores/<oracle-slug>.json v1",
"type": "object",
"required": [
"name",
"oracle",
"targets"
],
"properties": {
"name": {
"type": "string"
},
"oracle": {
"type": "string"
},
"rpc": {
"type": "string"
},
"block": {
"type": [
"integer",
"null"
]
},
"maxStaleness": {
"type": [
"integer",
"null"
]
},
"readAt": {
"type": "integer"
},
"kind": {
"type": "string"
},
"targets": {
"type": "array",
"items": {
"type": "object",
"required": [
"target",
"compositeScore"
],
"properties": {
"target": {
"type": "string"
},
"index": {
"type": "integer"
},
"adminKeyScore": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"multisigScore": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"timelockScore": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"oracleAuthorityScore": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"crossExposureScore": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"compositeScore": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"previousComposite": {
"type": [
"integer",
"null"
]
},
"lastUpdated": {
"type": "integer"
},
"methodologyHash": {
"type": "string"
},
"stale": {
"type": "boolean"
},
"previousRun": {
"description": "UTC time of the previous watch run this score was compared to; null on a first read (the Solana targets had no previous run on 10/10). Not enforced: a documentation entry."
}
}
}
},
"self_scanned_to": {
"description": "the last block up to which the oracle's own role events were scanned successfully (the next scan starts there, so a window lost to an eth_getLogs failure is read the next time); the block of the run when the scan worked. Not enforced: a documentation entry."
},
"self": {
"description": "the oracle's own authority since the previous run: role events, code hash, hasRole of the known holders, and the error when a read failed. Not enforced: a documentation entry."
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/second_opinion.json v1",
"type": "object",
"required": [
"generated_utc",
"checked",
"agree",
"disagree",
"unreadable",
"native",
"disagreements",
"changed",
"upgrades",
"results"
],
"properties": {
"generated_utc": {
"type": "string"
},
"checked": {
"type": "integer"
},
"agree": {
"type": "integer"
},
"disagree": {
"type": "integer"
},
"unreadable": {
"type": "integer"
},
"native": {
"type": "integer"
},
"acknowledged": {
"type": "integer"
},
"clusters": {
"type": "array"
},
"concentration": {
"type": [
"object",
"null"
]
},
"cluster_changes": {
"type": "array"
},
"disagreements": {
"type": "array"
},
"acknowledged_disagreements": {
"type": "array"
},
"changed": {
"type": "array"
},
"upgrades": {
"type": "array"
},
"previous_utc": {
"type": [
"string",
"null"
]
},
"results": {
"type": "array",
"items": {
"type": "object",
"required": [
"chain",
"target",
"kind"
],
"properties": {
"chain": {
"type": "string"
},
"target": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"eoa",
"safe",
"timelock",
"contract",
"unreadable",
"native",
"pda",
"squads",
"immutable",
"program"
]
},
"holder": {
"type": [
"string",
"null"
]
},
"path": {
"type": [
"string",
"null"
]
},
"threshold": {
"type": [
"integer",
"null"
]
},
"owners": {
"type": [
"integer",
"null"
]
},
"delay_seconds": {
"type": [
"integer",
"null"
]
},
"impl": {
"type": [
"string",
"null"
]
},
"impl_via": {
"type": [
"string",
"null"
]
},
"modules": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"guard": {
"type": [
"string",
"null"
]
},
"modules_detail": {
"type": "array"
},
"guard_detail": {
"type": [
"object",
"null"
]
},
"disagree": {
"type": [
"string",
"null"
]
},
"acknowledged": {
"type": [
"string",
"null"
]
},
"changed": {
"type": [
"string",
"null"
]
},
"upgraded": {
"type": [
"string",
"null"
]
},
"since": {
"type": [
"string",
"null"
]
},
"shares_admin_with": {
"type": "array"
},
"nested": {
"type": "boolean"
},
"oracle_self": {
"type": "boolean"
},
"access_control": {
"description": "the admin was read through an enumerable AccessControl role, not owner() (observed as boolean on 2026-10-10; not enforced: a documentation entry)"
},
"adminKeyScore": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"alias_of": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"behind": {
"description": "a bare contract holder whose own owner() is the effective hand: address, kind, threshold, owners, delay_seconds (observed as object on 2026-10-10; not enforced: a documentation entry)"
},
"block_end": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"block_start": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"cluster": {
"description": "the cluster of the contract, when it shares an admin with others (observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"cluster_tvl_usd": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"config_authority": {
"description": "(observed as on 2026-10-10; not enforced: a documentation entry)"
},
"delegated_to": {
"description": "since 2026-10-10: the admin or holder is a plain key whose account carries an EIP-7702 delegation to this code (observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"deployed_at": {
"description": "Solana: the date of that slot (observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"deployed_slot": {
"description": "Solana: the slot of the last deployment of the program (ProgramData) (observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"effective": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"executors": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"exposure": {
"description": "the attacker's question: keys_to_move, react_hours, tvl_usd, money_per_key_usd, money_source (contract or protocol_tvl) (observed as object on 2026-10-10; not enforced: a documentation entry)"
},
"former_members_in_account": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"holder_tx_count": {
"description": "transactions sent by a plain-key holder: a hot operational key or one that never acted (observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"initiators": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"keys_lost_to_freeze": {
"description": "how many lost keys would freeze the multisig for ever (observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"keys_to_act": {
"description": "how many keys must collude for the multisig to act (observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"multisig": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"multisigScore": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"multisig_program": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"owners_detail": {
"description": "(observed as array on 2026-10-10; not enforced: a documentation entry)"
},
"owners_kinds": {
"description": "what the signers of a Safe or Squads multisig are, counts only (eoa, safe, timelock, contract, unread) (observed as object on 2026-10-10; not enforced: a documentation entry)"
},
"pending_admin": {
"description": "a new admin designated and not yet accepted (pendingOwner / pendingAdmin) (observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"pending_via": {
"description": "which getter named the pending admin (observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"posture": {
"description": "the whole finding in one sentence: who holds the admin, the delay, what is in flight (observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"programdata": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"self_delay_seconds": {
"description": "the contract's own timelock(), when it exposes one (observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"timelockScore": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"timelock_family": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"tvl_scope": {
"description": "retired or peripheral: the protocol TVL does not sit behind this contract (data/tvl_scope.json) (observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"via_program": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
},
"voters": {
"description": "(observed as number on 2026-10-10; not enforced: a documentation entry)"
},
"why": {
"description": "(observed as string on 2026-10-10; not enforced: a documentation entry)"
}
}
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/side_roles.json v1",
"type": "object",
"required": [
"generated_utc",
"asked",
"count",
"plain_key_roles",
"changes",
"by_target"
],
"properties": {
"generated_utc": {
"type": "string"
},
"asked": {
"type": "integer"
},
"count": {
"type": "integer"
},
"plain_key_roles": {
"type": "array",
"items": {
"type": "string"
}
},
"previous_run": {
"type": [
"string",
"null"
]
},
"stale": {
"type": "integer"
},
"unread_chains": {
"type": "array",
"items": {
"type": "string"
}
},
"changes": {
"type": "array",
"items": {
"type": "object",
"required": [
"target",
"role",
"change"
],
"properties": {
"target": {
"type": "string"
},
"role": {
"type": "string"
},
"change": {
"enum": [
"changed",
"added",
"cleared",
"announced"
]
},
"from": {
"type": [
"string",
"null"
]
},
"from_kind": {
"type": [
"string",
"null"
]
},
"to": {
"type": [
"string",
"null"
]
},
"to_kind": {
"type": [
"string",
"null"
]
},
"valid_at": {
"type": [
"integer",
"null"
]
},
"from_seconds": {
"type": [
"integer",
"null"
]
},
"to_seconds": {
"type": [
"integer",
"null"
]
},
"shorter": {
"type": [
"boolean",
"null"
]
}
}
}
},
"by_target": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [],
"properties": {
"roles": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"kind",
"holder"
],
"properties": {
"kind": {
"enum": [
"eoa",
"safe",
"timelock",
"contract",
"unread"
]
},
"holder": {
"type": "string"
},
"threshold": {
"type": "integer"
},
"owners": {
"type": "integer"
},
"delay_seconds": {
"type": "integer"
},
"self": {
"type": "boolean"
},
"stale": {
"type": "boolean"
},
"delegated_to": {
"type": "string",
"description": "an EIP-7702 delegation: the plain key runs the code of this address"
}
}
}
},
"pending": {
"type": "object",
"properties": {
"guardian": {
"type": "object"
},
"timelock": {
"type": "object"
}
}
}
}
}
},
"what": {
"type": "string"
},
"pending": {
"type": "array",
"items": {
"type": "string"
}
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "watch/tvl.json v1",
"type": "object",
"required": [
"generated_utc",
"source",
"matched",
"unmatched"
],
"properties": {
"generated_utc": {
"type": "string"
},
"source": {
"type": "string"
},
"chains": {
"type": "object"
},
"unmatched": {
"type": "array",
"items": {
"type": "string"
}
},
"unknown_slugs": {
"type": "array"
},
"matched": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": [
"name",
"tvl",
"scope"
],
"properties": {
"slug": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
},
"tvl": {
"type": "number"
},
"scope": {
"enum": [
"chain",
"total",
"chain-total"
]
},
"chain": {
"type": [
"string",
"null"
]
}
}
}
},
"no_figure": {
"type": "array",
"items": {
"type": "string"
}
}
}
}Not an audit, not investment advice. Back to the dashboard.