Skip to content

ReserveGrid OS v0.2.1

Choose a tag to compare

@LeavesJ LeavesJ released this 23 Feb 03:42

ReserveGrid OS v0.2.1 — Alignment

Scope

v0.2.1 is an alignment release ensuring reason codes, tier naming, and documentation are globally consistent across rg-protocol, pool-verifier, exports, dashboard, and website. No new features. Log rotation shipped in this release (see below). Auth/TLS and failover orchestration deferred to hardening release. Protocol version remains 2. Wire schema unchanged.

rg-protocol as single source of truth

Stringification moves to rg-protocol

  • Add VerdictReason::as_str() -> &'static str method returning canonical snake_case codes.
  • Add VerdictReason::all_codes() -> &'static [&'static str] for test enumeration.
  • Delete wire_reason_code_str() from pool-verifier main.rs.

Tier naming canonicalized

  • Fix PolicyContext.fee_tier comment: "low" | "mid" | "high" (was incorrectly "lo" | "mid" | "hi").
  • No code change needed: FeeTier::as_str() already returns correct values.

Protocol compatibility documented

  • Add PROTOCOL.md or doc comment block in rg-protocol explaining backward compatibility rules.
  • New fields use #[serde(default)] and are backward-compatible. Old clients ignore unknown fields.
  • Protocol version bump only for breaking changes.

pool-verifier alignment

Remove normalization hack

  • Delete normalize_reason_key() function from main.rs.
  • Stats aggregation uses reason_code directly. No silent rewriting of legacy strings.
  • Legacy NDJSON lines without reason_code field aggregate under "unknown" bucket: explicit, not normalized.
  • Stats aggregation keys off reason_code directly from rg-protocol. Dashboard and CSV exports consume these codes without transformation.

Mapping hardened with tests

  • Tests in rg-protocol verify every VerdictReason variant round-trips through serde and as_str().
  • VerdictReason::ALL constant (11 variants) iterated exhaustively — adding a variant without updating ALL fails the test.
  • VerdictReason::ALL_CODES constant checked against as_str() for each variant — no drift possible.

System errors at handler boundary

  • EvalResult (policy.rs) covers policy evaluation only. System errors are emitted at the TCP handler boundary in main.rs, outside the policy evaluator.
  • System errors emitted in main.rs handler code:
    • Policy load failure emits PolicyLoadError.
    • Mempool required but unavailable with no fallback emits MempoolBackendUnavailable.
    • Unexpected panic emits InternalError.
  • Degraded mode (unknown_mempool_as_high = true) proceeds with high tier assumption: not an error, deliberate feature.
  • Poisoned policy lock recovery extracts config synchronously (RwLockReadGuard dropped before any .await) and emits PolicyLoadError with continue — no double verdict.

Dashboard tier display

  • Dashboard shows tier source indicator:
    • "mid (measured)" when tier derived from actual mempool tx count.
    • "high (fallback)" when tier derived from unknown_mempool_as_high flag.
  • No new reason_code emitted. Observability only.

Documentation alignment

beta-runbook.md

  • Section 6.6: Remove fallback language. Stats use reason_code directly.
  • Add section on empty mempool semantics:
    • When mempool is empty, fee-based rules are vacuously satisfied (avg fee of zero txs is undefined, template passes).
    • Use reject_empty_templates = true to block empty blocks.
    • This is policy choice, not a bug.

Website samples (veldra-site repo)

  • index.html sample verdict: Fix to match actual TemplateVerdict struct.
  • reason_code: "avg_fee_below_minimum" (was invented MIN_AVG_FEE_TOO_LOW).
  • policy_context fields match actual PolicyContext.
  • policy/index.html sample contract: Same fix.
  • Reason codes table: Add note that policy_load_error, mempool_backend_unavailable, internal_error are system-level codes (not policy evaluation results).

Hardening (implemented)

  • Verdict log rotation: 50 MB cap per file, 5 rotations (verdicts.logverdicts.log.1 … .5). Checked before every append.
  • In-memory verdict buffer: 1000 entries max, oldest drained on overflow.
  • CSV export: default 1000 rows, max 50,000. NDJSON tail: default 2000 lines, max 50,000.
  • Mempool fetch timeout: 600ms hard timeout. HTTP client global timeout: 900ms.

Hardening backlog (documented, not implemented)

  • The following operator feedback is acknowledged and tracked for a future hardening release.

Auth and TLS

  • HTTP API authentication mechanism. TLS/HTTPS support for dashboard and exports.
  • Credential hygiene: remove hardcoded regtest credentials from scripts, add production warning.

Service health linkage

  • Health check coupling between template-manager, pool-verifier, and bitcoind.
  • Configurable fail-open vs fail-closed behavior on upstream failure.
  • Explicit error codes for upstream unavailability.

Test matrix

  • reason_mapping_exhaustive: Every VerdictReason has as_str() coverage.
  • eval_result_exhaustive: Every policy rejection path returns a valid VerdictReason variant via EvalResult.
  • tier_naming_consistent: FeeTier::as_str() returns "low", "mid", "high" only.
  • policy_context_tier_values: PolicyContext.fee_tier only contains canonical tier names.
  • stats_aggregation_no_normalization: Stats bucket keys match raw reason_code values.
  • all_codes_match_as_str: VerdictReason::ALL_CODES[i] == VerdictReason::ALL[i].as_str() for all i.

Files changed

rg-protocol

  • src/lib.rs: Add VerdictReason::as_str(), VerdictReason::all_codes(), fix tier comment.

pool-verifier

  • src/policy.rs: Delete local VerdictReason enum, add EvalResult struct, add #[cfg(test)] mod tests (6 tests).
  • src/main.rs: Delete wire_reason_code_str(), delete normalize_reason_key(), add tier_source to LoggedVerdict, add poisoned lock handler with PolicyLoadError, update CSV header.

docs

  • beta-runbook.md: Section 6.6 fix, empty mempool semantics section.

veldra-site (separate repo)

  • index.html: Fix sample verdict.
  • policy/index.html: Fix sample contract, add system errors note.