{
  "protocol": "Kerne Protocol",
  "chain": "base",
  "chainId": 8453,
  "blockNumber": 50062153,
  "timestamp": "2026-08-16T20:54:14.389Z",
  "overall": "warning",
  "summary": {
    "solvencyState": "live-vault-degraded",
    "solvencyStateNote": "'live-vault-degraded' denotes ONLY the deprecated v1 KerneVault leg (phantom kLP supply, excluded from backing and holding no outstanding obligation), not the solvency of kUSD. Outstanding kUSD is backed 1:1 by USDC in the live PSM; the hourly EOA-signed Proof of Reserves at /api/por/signed is the authoritative solvency attestation. See summary.offChainAttestation and the solvency_ratio trigger for the live reads.",
    "solvencyRatioBps": null,
    "solvencyRatioPct": null,
    "vaultPaused": false,
    "circuitBreakerActive": null,
    "softAlertActive": null,
    "insolventNow": false,
    "insolventDurationSec": 0,
    "gracePeriodSec": 14400,
    "offChainAttestation": {
      "source": "/api/por/signed",
      "authoritative": true,
      "reachable": true,
      "verified": true,
      "fresh": true,
      "stalenessSeconds": 3882,
      "staleThresholdSeconds": 7800
    }
  },
  "reserves": {
    "vaultAssetsETH": 0.04991987960114005,
    "kUSDSupply": 1144.707154
  },
  "triggers": {
    "onChain": [
      {
        "name": "solvency_ratio",
        "description": "Vault total assets divided by total share supply, in basis points (10000 = 100%). The v1 KerneVault is in a documented degraded state pending v2 redeploy (phantom kLP supply does not reconcile with backing), so the on-chain vault ratio is not a meaningful measure of protocol solvency in this window. Outstanding kUSD is USDC-backed 1:1 in KUSDPSM (not vault-backed); the PSM mint flow (USDC to kUSD) is the live deposit path.",
        "source": "KerneVault.sol:getSolvencyRatio",
        "current": null,
        "limit": null,
        "status": "ok",
        "unit": "bps"
      },
      {
        "name": "psm_solvency_check_disabled",
        "description": "TRUE on the live mint PSM. KUSDPSM._checkSolvency reads getSolvencyRatio() on the vault named in psm_solvency_gate_vault and reverts ProtocolInsolvency below psm_solvency_gate_threshold; when this flag is set that check is skipped on both the mint and the redeem path. WHY IT IS SET: the gate couples the PSM to the vault book, and those are separate books. kUSD minted here is backed 1:1 by the USDC held in this PSM, not by the vault, so a vault-side reading can halt or permit PSM mints for reasons that have nothing to do with whether the kUSD being minted is backed. WHAT WOULD HAPPEN IF IT WERE CLEARED TODAY: see psm_solvency_gate_effective, which is the more useful number. COMPENSATING CONTROLS, all live and readable here: the depeg gate is enforced (psm_depeg_check_disabled is false, with the Chainlink USDC/USD feed wired and a staleness window published live in this payload as psm_chainlink_staleness_max); PSM backing is published every hour as psm_solvency_ratio in the signature-bound attestation at /api/por/signed; per-stable concentration caps and the mint cap still apply; and the retired mint PSM has had its kUSD MINTER_ROLE revoked, so its copy of this flag cannot mint anything.",
        "source": "KUSDPSM.sol:solvencyCheckDisabled",
        "current": true,
        "limit": false,
        "status": "warning"
      },
      {
        "name": "psm_solvency_gate_effective",
        "description": "Whether the solvency gate would MEASURE anything if it were re-enabled right now. It reads getSolvencyRatio() on the vault below, and KerneVault.sol returns the sentinel 20000 (200%) whenever that vault has zero liabilities. The vault currently has zero shares outstanding, so the gate would read a constant, clear the threshold, and tell a depositor nothing. This is disclosed rather than presented as safety: an enabled gate over an empty vault is decoration, and the honest reading is that PSM backing is evidenced by the hourly signed attestation, not by this flag in either position.",
        "source": "KerneVault.sol:getSolvencyRatio (liabilities == 0 sentinel)",
        "current": "sentinel-only",
        "limit": "measures-real-liabilities",
        "status": "warning"
      },
      {
        "name": "psm_solvency_check_disabled_retired_psm",
        "description": "Same flag on the retired mint PSM 0x07eBb486e11BD217e6085eb5ab663e4517595993. Also true, and inert: its kUSD MINTER_ROLE was revoked on 2026-07-10, so it cannot mint regardless of gate state. It still custodies the reserve backing kUSD minted before that cutover, which is why it is reported rather than dropped.",
        "source": "KUSDPSM.sol:solvencyCheckDisabled",
        "current": true,
        "limit": false,
        "status": "ok"
      },
      {
        "name": "psm_depeg_check_disabled",
        "description": "The sibling opt-out for the stable-side depeg gate, and it is FALSE, meaning the depeg circuit breaker is enforced on the live mint PSM. The gate fails closed on an unset oracle (KRN-26-PSM-DEPEG-FAIL-OPEN, 2026-06-10), and the Chainlink USDC/USD feed 0x7e860098F58bBFC8648a4311b374B1D669a2bc6B is wired, with a staleness window published live in this payload as psm_chainlink_staleness_max. Reported next to the solvency flag so the two are never conflated.",
        "source": "KUSDPSM.sol:depegCheckDisabled",
        "current": false,
        "limit": false,
        "status": "ok"
      },
      {
        "name": "psm_solvency_gate_vault",
        "description": "The vault address KUSDPSM._checkSolvency would read if the gate were enabled. Published so the gate can be judged rather than assumed: which contract it asks, and therefore which book it is actually gating on.",
        "source": "KUSDPSM.sol:vault",
        "current": "0x8ccc56b5624e2fdb592f6609d81f4c3798e3292b",
        "limit": "0x8ccc56B5624e2FDB592F6609d81F4c3798e3292B",
        "status": "ok"
      },
      {
        "name": "psm_solvency_gate_threshold",
        "description": "The minimum solvency ratio in basis points the PSM gate would require (10100 = 101%). Enforced only while psm_solvency_check_disabled is false.",
        "source": "KUSDPSM.sol:minSolvencyThreshold",
        "current": 10100,
        "limit": 10100,
        "status": "ok",
        "unit": "bps"
      },
      {
        "name": "red_halt_threshold",
        "description": "Below this solvency ratio the vault auto-pauses after the grace period. Reads unknown when the on-chain call does not return.",
        "source": "KerneVault.sol:CRITICAL_CR_THRESHOLD",
        "current": null,
        "limit": null,
        "status": "unknown",
        "unit": "bps"
      },
      {
        "name": "yellow_alert_threshold",
        "description": "Below this solvency ratio operators receive a soft alert. Reads unknown when the on-chain call does not return.",
        "source": "KerneVault.sol:WARNING_CR_THRESHOLD",
        "current": null,
        "limit": null,
        "status": "unknown",
        "unit": "bps"
      },
      {
        "name": "safe_recovery_threshold",
        "description": "Solvency ratio required to clear an active circuit breaker. Reads unknown when the on-chain call does not return.",
        "source": "KerneVault.sol:SAFE_CR_THRESHOLD",
        "current": null,
        "limit": null,
        "status": "unknown",
        "unit": "bps"
      },
      {
        "name": "insolvency_grace_period",
        "description": "Continuous insolvency duration before the vault auto-pauses.",
        "source": "KerneVault.sol:GRACE_PERIOD",
        "current": 0,
        "limit": 14400,
        "status": "ok",
        "unit": "seconds"
      },
      {
        "name": "cr_circuit_breaker_active",
        "description": "Whether the on-chain circuit breaker is currently engaged. Reads unknown when the on-chain call does not return, which is NOT the same as not engaged.",
        "source": "KerneVault.sol:crCircuitBreakerActive",
        "current": null,
        "limit": false,
        "status": "unknown"
      },
      {
        "name": "cr_soft_alert_active",
        "description": "Whether the soft alert flag is currently raised. Reads unknown when the on-chain call does not return, which is NOT the same as not raised.",
        "source": "KerneVault.sol:crSoftAlertActive",
        "current": null,
        "limit": false,
        "status": "unknown"
      },
      {
        "name": "vault_paused",
        "description": "Whether deposits and withdrawals are currently halted. Reads unknown when the on-chain call does not return.",
        "source": "KerneVault.sol:paused",
        "current": false,
        "limit": false,
        "status": "ok"
      },
      {
        "name": "withdrawal_cooldown",
        "description": "Time between requestWithdrawal and claimWithdrawal. Reads unknown when the on-chain call does not return.",
        "source": "KerneVault.sol:withdrawalCooldown",
        "current": 604800,
        "limit": 604800,
        "status": "ok",
        "unit": "seconds"
      },
      {
        "name": "off_chain_update_cooldown",
        "description": "Minimum interval between Strategist off-chain asset reports. Reads unknown when the on-chain call does not return.",
        "source": "KerneVault.sol:offChainUpdateCooldown",
        "current": null,
        "limit": null,
        "status": "unknown",
        "unit": "seconds"
      },
      {
        "name": "time_since_last_offchain_report",
        "description": "Seconds since the last on-chain Strategist off-chain-asset mirror write (KerneVault.updateOffChainAssets). During the documented pre-launch / degraded-vault window the Strategist wallet is intentionally held gas-minimal and this redundant on-chain mirror is paused. The authoritative off-chain asset attestation is the hourly EOA-signed Proof of Reserves at /api/por/signed (verified server-side; see the signed_por_freshness trigger). This mirror trigger therefore reads ok while that signed attestation is provably fresh, and falls back to warning if it cannot be confirmed fresh. It returns to its full 6h warning / 24h critical schedule once vault v2 ships and the on-chain mirror resumes.",
        "source": "KerneVault.sol:lastReportedTimestamp",
        "current": 3272851,
        "limit": 21600,
        "status": "ok",
        "unit": "seconds"
      },
      {
        "name": "signed_por_freshness",
        "description": "Seconds since the BOUND (signed) timestamp of the latest hourly EOA-signed Proof of Reserves, verified server-side: the signature must recover to the expected Strategist EOA AND the signed numbers and timestamp must rehash to the attestation hash (reproduce at /api/por/signed). This is the authoritative off-chain asset-freshness signal while the redundant on-chain mirror write is intentionally paused; the overall status keys off this attestation, not the paused mirror. It reads ok when fresh and verified, warning when the attestation is reachable but stale or unverifiable (a genuinely silent bot), critical when stale beyond 24h, and unknown when the publisher is momentarily unreachable.",
        "source": "frontend/src/lib/por-attestation.ts:verifySignedPoR",
        "current": 3882,
        "limit": 7800,
        "status": "ok",
        "unit": "seconds"
      },
      {
        "name": "max_liquidation_per_hour_bps",
        "description": "Cap on liquidation volume per hour as fraction of TVL, in basis points. Reads unknown when the on-chain call does not return.",
        "source": "KerneVault.sol:maxLiquidationPerHourBps",
        "current": null,
        "limit": null,
        "status": "unknown",
        "unit": "bps"
      }
    ],
    "offChain": [
      {
        "name": "risk_engine_critical_health_score",
        "description": "Health score below this triggers Emergency Unwind.",
        "source": "bot/sentinel/risk_engine.py:critical_health_score",
        "current": 60,
        "limit": 60,
        "status": "ok"
      },
      {
        "name": "risk_engine_min_health_score",
        "description": "Minimum acceptable health score for normal operations.",
        "source": "bot/sentinel/risk_engine.py:min_health_score",
        "current": 80,
        "limit": 80,
        "status": "ok"
      },
      {
        "name": "risk_engine_delta_limit",
        "description": "Net delta limit before health score is penalized. Volatility-adjusted at runtime.",
        "source": "bot/sentinel/risk_engine.py:delta_limit",
        "current": 0.02,
        "limit": 0.02,
        "status": "ok",
        "unit": "fraction"
      },
      {
        "name": "risk_engine_min_liquidation_distance",
        "description": "Minimum buffer between current price and liquidation price.",
        "source": "bot/sentinel/risk_engine.py:min_liquidation_distance",
        "current": 0.3,
        "limit": 0.3,
        "status": "ok",
        "unit": "fraction"
      },
      {
        "name": "risk_engine_max_drawdown_pct",
        "description": "Daily drawdown which, sustained for 3 consecutive cycles, triggers Emergency Unwind.",
        "source": "bot/sentinel/risk_engine.py:max_drawdown_pct",
        "current": 0.02,
        "limit": 0.02,
        "status": "ok",
        "unit": "fraction"
      },
      {
        "name": "risk_engine_max_drawdown_breach_cycles",
        "description": "Consecutive cycles the 2% drawdown must persist before Emergency Unwind fires (hysteresis against single-cycle noise).",
        "source": "bot/sentinel/risk_engine.py:max_drawdown_breach_cycles",
        "current": 3,
        "limit": 3,
        "status": "ok",
        "unit": "count"
      },
      {
        "name": "risk_engine_daily_loss_floor_usd",
        "description": "Floor for the TVL-scaled daily loss limit. Effective limit = max(this, 0.1% of NAV).",
        "source": "bot/sentinel/risk_engine.py:daily_loss_limit_usd",
        "current": 50000,
        "limit": 50000,
        "status": "ok",
        "unit": "USD"
      },
      {
        "name": "risk_engine_hourly_loss_floor_usd",
        "description": "Floor for the TVL-scaled hourly loss limit. Effective limit = max(this, 0.02% of NAV).",
        "source": "bot/sentinel/risk_engine.py:hourly_loss_limit_usd",
        "current": 10000,
        "limit": 10000,
        "status": "ok",
        "unit": "USD"
      },
      {
        "name": "sentinel_rebalance_threshold",
        "description": "Delta deviation above which the sentinel flags for rebalance.",
        "source": "bot/sentinel_monitor.py:REBALANCE_THRESHOLD",
        "current": 0.05,
        "limit": 0.05,
        "status": "ok",
        "unit": "fraction"
      },
      {
        "name": "sentinel_critical_cr",
        "description": "Sentinel-side CR threshold below which flash-rebalance fires. Distinct from on-chain solvency thresholds.",
        "source": "bot/sentinel_monitor.py:CRITICAL_CR",
        "current": 1.3,
        "limit": 1.3,
        "status": "ok",
        "unit": "ratio"
      },
      {
        "name": "sentinel_depeg_threshold",
        "description": "LST/ETH ratio deviation above which a depeg warning fires.",
        "source": "bot/sentinel_monitor.py:DEPEG_THRESHOLD",
        "current": 0.02,
        "limit": 0.02,
        "status": "ok",
        "unit": "fraction"
      },
      {
        "name": "sentinel_negative_funding_threshold",
        "description": "Hourly funding rate below which an alert fires. Does not by itself trigger unwind.",
        "source": "bot/sentinel_monitor.py:NEGATIVE_FUNDING_THRESHOLD",
        "current": -0.0001,
        "limit": -0.0001,
        "status": "ok",
        "unit": "rate"
      },
      {
        "name": "sentinel_depeg_consecutive_breaches",
        "description": "Consecutive depeg breach count required before Emergency Unwind fires.",
        "source": "bot/sentinel/risk_engine.py:depeg_state.breach_count",
        "current": 3,
        "limit": 3,
        "status": "ok"
      },
      {
        "name": "allocator_max_venue_concentration",
        "description": "Maximum allocator weight on any single venue.",
        "source": "bot/neural/constraint_projection.py:DEFAULT_MAX_VENUE_CONCENTRATION",
        "current": 0.6,
        "limit": 0.6,
        "status": "ok",
        "unit": "fraction"
      }
    ],
    "psm": [
      {
        "name": "psm_chainlink_staleness_max",
        "description": "PSM oracle stale window for USDC, read live from the live mint PSM. Above this all PSM operations revert with OraclePriceStale. This is a per-stable mapping (KUSDPSM.maxOracleDelay), and the 1h DEFAULT_MAX_ORACLE_DELAY in the source governs ONLY when that mapping is unset for the stable. It is set here, so the 1h figure is not the live window. Until 2026-07-28 this endpoint published that 1h constant as though it were live, understating the real window by a factor of 26 and making the risk read safer than the contract; the value is now read on chain every request. Null means the read failed and the window is unknown, which is never filled in from the constant. Reproduce: cast call <psm> \"maxOracleDelay(address)(uint256)\" 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 --rpc-url https://mainnet.base.org",
        "source": "KUSDPSM.sol:maxOracleDelay (mapping, not DEFAULT_MAX_ORACLE_DELAY)",
        "current": 172800,
        "limit": 172800,
        "status": "ok",
        "unit": "seconds"
      },
      {
        "name": "psm_chainlink_staleness_max_retired_psm",
        "description": "The same window on the retired mint PSM (kUSD MINTER_ROLE revoked 2026-07-10, USDC reserve retained and still redeemable). Published separately because a redeemer drawing on that reserve is governed by its own setting, not the live mint PSM's.",
        "source": "KUSDPSM.sol:maxOracleDelay (mapping, not DEFAULT_MAX_ORACLE_DELAY)",
        "current": 93600,
        "limit": 93600,
        "status": "ok",
        "unit": "seconds"
      },
      {
        "name": "psm_chainlink_staleness_source_default",
        "description": "The 1h fallback compiled into KUSDPSM.sol, published for contrast so a reader can see why the two figures differ and check which one governs. It applies only when maxOracleDelay is unset for the stable being swapped. It is NOT the live window while the mapping is set.",
        "source": "KUSDPSM.sol:DEFAULT_MAX_ORACLE_DELAY",
        "current": 3600,
        "limit": 3600,
        "status": "ok",
        "unit": "seconds"
      }
    ]
  },
  "gaps": [
    {
      "selector": "0xb4a8be82",
      "name": "maxLiquidationPerHourBps",
      "reason": "deployed bytecode does not expose this getter (source-vs-deployed drift); contract redeploy required to verify on-chain"
    },
    {
      "selector": "0xd0cb6fc8",
      "name": "crCircuitBreakerActive",
      "reason": "deployed bytecode does not expose this getter (source-vs-deployed drift); contract redeploy required to verify on-chain"
    },
    {
      "selector": "0xe4a22389",
      "name": "SAFE_CR_THRESHOLD",
      "reason": "deployed bytecode does not expose this getter (source-vs-deployed drift); contract redeploy required to verify on-chain"
    },
    {
      "selector": "0x6c48dbc8",
      "name": "offChainUpdateCooldown",
      "reason": "deployed bytecode does not expose this getter (source-vs-deployed drift); contract redeploy required to verify on-chain"
    },
    {
      "selector": "0x13dbb102",
      "name": "WARNING_CR_THRESHOLD",
      "reason": "deployed bytecode does not expose this getter (source-vs-deployed drift); contract redeploy required to verify on-chain"
    },
    {
      "selector": "0xc53226de",
      "name": "CRITICAL_CR_THRESHOLD",
      "reason": "deployed bytecode does not expose this getter (source-vs-deployed drift); contract redeploy required to verify on-chain"
    },
    {
      "selector": "0xae81feed",
      "name": "crSoftAlertActive",
      "reason": "deployed bytecode does not expose this getter (source-vs-deployed drift); contract redeploy required to verify on-chain"
    }
  ],
  "gapsNote": "Items in `gaps` are read-only view-function getters that the deployed bytecode does not expose. They reflect source-vs-deployed drift, not security vulnerabilities. The dependent triggers fall back to documented threshold values; pause state, circuit-breaker booleans, and the live solvency read remain authoritative because they are sourced from on-chain functions that are exposed.",
  "specNotYetWired": [
    "130/115/105% layered collateral buffer (currently the contract enforces 99/100/101)",
    "Staged hedge reduction at 72h/120h consecutive negative funding",
    "Hard $5M margin cap per CEX account (currently 60% allocator concentration cap)",
    "10% of vault TVL held as liquid collateral on Base for instant withdrawals. Scope note: this is a vault-TVL withdrawal-buffer policy and has never applied to the USDC backing held in the PSM contracts, which is not deployed into any strategy. Implementation is partial and this number is not a hard contract constraint",
    "Time-based hedge venue health monitor (>5 min unreachable triggers automated halt)",
    "KerneOracleRouter dual-feed Pyth+Chainlink (200/500 bps tolerance/circuit-breaker) is deployed on Base at 0x5fAeaB501A33468775DDF929C714890eE7e984bc and Safe-administered, but is NOT wired into the mint path; its only configured asset is WETH and USDC has no feed on it. This line previously said it was not yet deployed, which was wrong. PSM uses single-feed Chainlink today, with a staleness window of 172800s read live from the PSM. This line previously said 1h, which is the source fallback rather than the configured value; see psm_chainlink_staleness_max"
  ],
  "methodology": "On-chain reads use a 4-RPC fallback chain. The reported on-chain values are reproducible by any caller via cast call against the same contract. Off-chain Sentinel constants are mirrored here for verifiability against bot/sentinel/risk_engine.py and bot/sentinel_monitor.py; the threshold-constant test suite asserts they match the canonical Python source.",
  "docs": "https://kerne.fi/docs/exit-triggers-and-emergency-runbook"
}