feat(ops): H-3 — align oracle poll interval and silence alert with window staleness budget #24

Closed
opened 2026-08-08 04:40:21 +00:00 by PlasticDigits · 10 comments
PlasticDigits commented 2026-08-08 04:40:21 +00:00 (Migrated from gitlab.com)

Summary

Close H-3 from audits/INTERNAL_KIMIK3_1786162831.md: fix oracle poll / window staleness / silence-alert misalignment so one missed tick cannot immediately halt swaps, and so liveness alerts fire before (or with) the window staleness cutoff — not hours later.

Audit ref: H-3
Related: C-3 (confirm-before-liveness — silence should key off confirmed updates once C-3 lands)


Current codebase

Knob Where Default today
Window max oracle age ust1-common DEFAULT_MAX_ORACLE_AGE_SECS 21600 (6h)
Oracle service poll oracle-service POLL_INTERVAL_SECS 21600 (6h)
Liveness silence ORACLE_MAX_SILENCE_SECS 28800 (8h)
On-chain throttle ust1-common min update interval 30 min (most polls are no-ops when within band)

Problem: Poll interval equals the entire staleness budget ⇒ zero margin for a failed tick. Silence alert default is 2h after the window already rejects swaps. Combined with C-3 false success, “silence” is also semantically muddy until confirmations exist.


Why this is needed

  1. Availability: a single RPC/LCD/policy failure at the 6h boundary halts all window swaps until the next successful update.
  2. Operability: paging 2h after users are already bricked is too late.
  3. Throttle means frequent polls are cheap (skip broadcast); lowering poll default does not imply 48 on-chain txs/day.

Constraints / guardrails

  1. Default POLL_INTERVAL_SECS ≤ 1 hour (recommend 3600 or less); still respect on-chain throttle / policy skips.
  2. Default ORACLE_MAX_SILENCE_SECS ≤ max_oracle_age + poll (e.g. 21600+3600) — alert must not be looser than “window already dead + one poll”.
  3. Prefer alert threshold ≤ window max_oracle_age so ops are paged at or before user impact (document chosen formula).
  4. Do not change on-chain throttle, daily cap, or monotonicity in this issue.
  5. After C-3, silence must mean confirmed on-chain updates — coordinate defaults/docs; this issue can land first with clear comments.
  6. Env overrides remain supported; change defaults + docs + any example Render/env snippets.
  7. Avoid tight loops that burn LCD/RPC quota; 1h default is the audit recommendation ceiling.

Relevant files

  • oracle-service/src/config.rs — defaults + validation if needed
  • oracle-service/src/main.rs — alert text if thresholds change meaning
  • smartcontracts-terraclassic/packages/ust1-common/src/lib.rs — DEFAULT_MAX_ORACLE_AGE_SECS (usually leave; document relationship)
  • README.md (oracle-service liveness section)
  • docs/DEPLOYMENT.md — env table / recommended production values
  • Config unit tests in oracle-service

  1. Set default poll to 3600 (or ≤3600).
  2. Set default ORACLE_MAX_SILENCE_SECS to a value ≤ window max age (recommend 21600) or max_age + poll per product choice — must be ≤ current 28800 and not later than user impact.
  3. Document the inequality: poll < max_oracle_age and silence ≤ max_oracle_age (+ small grace) in DEPLOYMENT.
  4. Add config validation warning or hard check if poll_interval >= max_oracle_age equivalent is mis-set when both are known (service may not know on-chain max age — document operator responsibility).
  5. Cross-link C-3: once confirmations exist, silence tracks confirmed updates only.

Acceptance criteria

  • Default poll ≤ 1h.
  • Default silence threshold no longer fires 2h after the 6h window cutoff (new default ≤ 6h, or documented max_age+poll with poll≪max_age).
  • README + DEPLOYMENT updated with recommended production relationship.
  • Existing tests updated; no on-chain contract changes required unless docs-only constants referenced.
  • Policy skip / throttle behavior unchanged (frequent polls still mostly no-op).

Test plan (all paths)

  1. Defaults: config load without env → new poll/silence defaults asserted in unit tests.
  2. Env override: custom POLL_INTERVAL_SECS / ORACLE_MAX_SILENCE_SECS still honored.
  3. Alert boundary: liveness tracker should_alert at new threshold (existing unit tests adjusted).
  4. Tick no-op: equal rate / policy reject still skip broadcast (integration or unit).
  5. Docs: examples match new defaults.

Test plan (attack / hack / abuse vectors)

  1. Ops misconfig: set poll=21600 and silence=86400 — document as footgun; optional startup warn if silence > poll * N or silence absurdly high.
  2. Alert fatigue: too-low silence with throttle-heavy skips — after C-3, “success” is confirmed update; until then, clarify that policy skips are not silence breakers (already true).
  3. RPC griefing: faster poll increases load — keep ≤1h default, not seconds.
  4. False security: shipping H-3 without C-3 still allows false success — issue description must note dependency; do not claim full liveness correctness until C-3 merges.

Verification criteria

  • Unit tests encode new defaults.
  • Staging/oracle service restarted with new defaults; logs show poll interval.
  • H-3 closed when defaults+docs land; link C-3 for confirmation semantics.
## Summary Close **H-3** from [`audits/INTERNAL_KIMIK3_1786162831.md`](../audits/INTERNAL_KIMIK3_1786162831.md): fix oracle poll / window staleness / silence-alert misalignment so one missed tick cannot immediately halt swaps, and so liveness alerts fire **before** (or with) the window staleness cutoff — not hours later. **Audit ref:** H-3 **Related:** C-3 (confirm-before-liveness — silence should key off confirmed updates once C-3 lands) --- ## Current codebase | Knob | Where | Default today | |------|-------|---------------| | Window max oracle age | `ust1-common` `DEFAULT_MAX_ORACLE_AGE_SECS` | **21600** (6h) | | Oracle service poll | `oracle-service` `POLL_INTERVAL_SECS` | **21600** (6h) | | Liveness silence | `ORACLE_MAX_SILENCE_SECS` | **28800** (8h) | | On-chain throttle | `ust1-common` min update interval | **30 min** (most polls are no-ops when within band) | **Problem:** Poll interval equals the entire staleness budget ⇒ zero margin for a failed tick. Silence alert default is **2h after** the window already rejects swaps. Combined with C-3 false success, “silence” is also semantically muddy until confirmations exist. --- ## Why this is needed 1. Availability: a single RPC/LCD/policy failure at the 6h boundary halts all window swaps until the next successful update. 2. Operability: paging 2h after users are already bricked is too late. 3. Throttle means frequent polls are cheap (skip broadcast); lowering poll default does not imply 48 on-chain txs/day. --- ## Constraints / guardrails 1. Default `POLL_INTERVAL_SECS` ≤ **1 hour** (recommend 3600 or less); still respect on-chain throttle / policy skips. 2. Default `ORACLE_MAX_SILENCE_SECS` ≤ `max_oracle_age + poll` (e.g. 21600+3600) — alert must not be looser than “window already dead + one poll”. 3. Prefer alert threshold **≤** window `max_oracle_age` so ops are paged at or before user impact (document chosen formula). 4. Do not change on-chain throttle, daily cap, or monotonicity in this issue. 5. After C-3, silence must mean confirmed on-chain updates — coordinate defaults/docs; this issue can land first with clear comments. 6. Env overrides remain supported; change **defaults** + docs + any example Render/env snippets. 7. Avoid tight loops that burn LCD/RPC quota; 1h default is the audit recommendation ceiling. --- ## Relevant files - `oracle-service/src/config.rs` — defaults + validation if needed - `oracle-service/src/main.rs` — alert text if thresholds change meaning - `smartcontracts-terraclassic/packages/ust1-common/src/lib.rs` — `DEFAULT_MAX_ORACLE_AGE_SECS` (usually leave; document relationship) - `README.md` (oracle-service liveness section) - `docs/DEPLOYMENT.md` — env table / recommended production values - Config unit tests in `oracle-service` --- ## Recommended direction 1. Set default poll to **3600** (or ≤3600). 2. Set default `ORACLE_MAX_SILENCE_SECS` to a value ≤ window max age (recommend **21600**) or `max_age + poll` per product choice — **must be ≤ current 28800 and not later than user impact**. 3. Document the inequality: `poll < max_oracle_age` and `silence ≤ max_oracle_age (+ small grace)` in DEPLOYMENT. 4. Add config validation warning or hard check if `poll_interval >= max_oracle_age` equivalent is mis-set when both are known (service may not know on-chain max age — document operator responsibility). 5. Cross-link C-3: once confirmations exist, silence tracks confirmed updates only. --- ## Acceptance criteria - [ ] Default poll ≤ 1h. - [ ] Default silence threshold no longer fires 2h *after* the 6h window cutoff (new default ≤ 6h, or documented `max_age+poll` with poll≪max_age). - [ ] README + DEPLOYMENT updated with recommended production relationship. - [ ] Existing tests updated; no on-chain contract changes required unless docs-only constants referenced. - [ ] Policy skip / throttle behavior unchanged (frequent polls still mostly no-op). --- ## Test plan (all paths) 1. **Defaults:** config load without env → new poll/silence defaults asserted in unit tests. 2. **Env override:** custom `POLL_INTERVAL_SECS` / `ORACLE_MAX_SILENCE_SECS` still honored. 3. **Alert boundary:** liveness tracker `should_alert` at new threshold (existing unit tests adjusted). 4. **Tick no-op:** equal rate / policy reject still skip broadcast (integration or unit). 5. **Docs:** examples match new defaults. --- ## Test plan (attack / hack / abuse vectors) 1. **Ops misconfig:** set poll=21600 and silence=86400 — document as footgun; optional startup warn if `silence > poll * N` or silence absurdly high. 2. **Alert fatigue:** too-low silence with throttle-heavy skips — after C-3, “success” is confirmed update; until then, clarify that policy skips are not silence breakers (already true). 3. **RPC griefing:** faster poll increases load — keep ≤1h default, not seconds. 4. **False security:** shipping H-3 without C-3 still allows false success — issue description must note dependency; do not claim full liveness correctness until C-3 merges. --- ## Verification criteria - Unit tests encode new defaults. - Staging/oracle service restarted with new defaults; logs show poll interval. - H-3 closed when defaults+docs land; link C-3 for confirmation semantics.
PlasticDigits commented 2026-08-08 04:51:17 +00:00 (Migrated from gitlab.com)

mentioned in commit fae6e8aebe

mentioned in commit fae6e8aebec917dfd875af3eb85fa44b3287fb7c
PlasticDigits commented 2026-08-08 04:51:32 +00:00 (Migrated from gitlab.com)

mentioned in merge request !36

mentioned in merge request !36
PlasticDigits commented 2026-08-08 04:51:57 +00:00 (Migrated from gitlab.com)

mentioned in merge request !37

mentioned in merge request !37
PlasticDigits commented 2026-08-08 04:52:24 +00:00 (Migrated from gitlab.com)

Implemented in !37 (fix/issue-24-oracle-poll-silence).

Acceptance criteria

  • Default poll ≤ 1h → DEFAULT_POLL_INTERVAL_SECS = 3600
  • Default silence no longer fires 2h after 6h cutoff → DEFAULT_ORACLE_MAX_SILENCE_SECS = 21600
  • README + DEPLOYMENT updated with production relationship (poll < max_age, silence ≤ max_age, grace ≤ max_age+poll)
  • Tests updated (cargo test -p ust1-oracle-service — 19 passed, twice); no on-chain contract logic changes
  • Policy skip / throttle behavior unchanged

Also done

  • Documented INV-ORACLE-OPS-POLL-001 / INV-ORACLE-OPS-SILENCE-001
  • Startup ORACLE_OPS_TIMING_MISCONFIG + verify_oracle_operator_env.sh advisories
  • Agent skill skills/oracle-ops-poll-silence + cross-links

Not done here (by design / ops)

  • C-3 confirm-before-liveness (#23) — silence still CheckTx-keyed
  • Staging restart / SIEM drain verification (ops)
  • On-chain throttle / daily / mono (explicitly out of scope)

MR: https://gitlab.com/PlasticDigits/ust1-window/-/merge_requests/4

Implemented in !37 (`fix/issue-24-oracle-poll-silence`). ### Acceptance criteria - [x] Default poll ≤ 1h → `DEFAULT_POLL_INTERVAL_SECS = 3600` - [x] Default silence no longer fires 2h after 6h cutoff → `DEFAULT_ORACLE_MAX_SILENCE_SECS = 21600` - [x] README + DEPLOYMENT updated with production relationship (`poll < max_age`, `silence ≤ max_age`, grace ≤ `max_age+poll`) - [x] Tests updated (`cargo test -p ust1-oracle-service` — 19 passed, twice); no on-chain contract logic changes - [x] Policy skip / throttle behavior unchanged ### Also done - Documented **INV-ORACLE-OPS-POLL-001** / **INV-ORACLE-OPS-SILENCE-001** - Startup `ORACLE_OPS_TIMING_MISCONFIG` + `verify_oracle_operator_env.sh` advisories - Agent skill `skills/oracle-ops-poll-silence` + cross-links ### Not done here (by design / ops) - C-3 confirm-before-liveness (#23) — silence still CheckTx-keyed - Staging restart / SIEM drain verification (ops) - On-chain throttle / daily / mono (explicitly out of scope) MR: https://gitlab.com/PlasticDigits/ust1-window/-/merge_requests/4
PlasticDigits commented 2026-08-08 04:59:59 +00:00 (Migrated from gitlab.com)

mentioned in merge request !44

mentioned in merge request !44
PlasticDigits commented 2026-08-08 05:10:47 +00:00 (Migrated from gitlab.com)

MR !37 verification (H-3 / #24)

Verdict: PASS_WITH_NOTES — merging (prefer before !35).

AC

  • Default poll = 3600s (≤ 1h)
  • Default silence = 21600s (≤ window max age; no longer 28800)
  • README + DEPLOYMENT + skill updated
  • Tests updated; no on-chain logic changes
  • Throttle / policy skip unchanged

Tests (local)

cargo test -p ust1-oracle-service — 19/19 passed
verify_oracle_operator_env.sh — clean on defaults; advisories on legacy 21600/28800

Problems / follow-ups

  1. Staging restart log verification still manual (poll_interval_secs=3600 / silence 21600).
  2. Silence still tracks CheckTx until C-3 (!35) lands — documented, not a blocker for H-3.
  3. Prior pipelines only ran secret_detection.
## MR !37 verification (H-3 / #24) **Verdict: PASS_WITH_NOTES — merging (prefer before !35).** ### AC - [x] Default poll = 3600s (≤ 1h) - [x] Default silence = 21600s (≤ window max age; no longer 28800) - [x] README + DEPLOYMENT + skill updated - [x] Tests updated; no on-chain logic changes - [x] Throttle / policy skip unchanged ### Tests (local) `cargo test -p ust1-oracle-service` — **19/19 passed** `verify_oracle_operator_env.sh` — clean on defaults; advisories on legacy 21600/28800 ### Problems / follow-ups 1. Staging restart log verification still manual (`poll_interval_secs=3600` / silence `21600`). 2. Silence still tracks CheckTx until C-3 (!35) lands — documented, not a blocker for H-3. 3. Prior pipelines only ran `secret_detection`.
PlasticDigits commented 2026-08-08 05:11:04 +00:00 (Migrated from gitlab.com)

mentioned in commit 6ffa7324ae

mentioned in commit 6ffa7324aeb17f350a573674417ae473ea85eecc
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-08 05:11:05 +00:00
PlasticDigits commented 2026-08-08 06:01:29 +00:00 (Migrated from gitlab.com)

mentioned in issue #27

mentioned in issue #27
PlasticDigits commented 2026-08-16 12:58:09 +00:00 (Migrated from gitlab.com)

mentioned in issue #32

mentioned in issue #32
PlasticDigits commented 2026-08-16 13:26:57 +00:00 (Migrated from gitlab.com)

mentioned in merge request !46

mentioned in merge request !46
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/ust1-window#24
No description provided.