ReserveGrid OS v0.2.1
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 strmethod returning canonical snake_case codes. - Add
VerdictReason::all_codes() -> &'static [&'static str]for test enumeration. - Delete
wire_reason_code_str()frompool-verifiermain.rs.
Tier naming canonicalized
- Fix
PolicyContext.fee_tiercomment:"low" | "mid" | "high"(was incorrectly"lo" | "mid" | "hi"). - No code change needed:
FeeTier::as_str()already returns correct values.
Protocol compatibility documented
- Add
PROTOCOL.mdor doc comment block inrg-protocolexplaining 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 frommain.rs. - Stats aggregation uses
reason_codedirectly. No silent rewriting of legacy strings. - Legacy NDJSON lines without
reason_codefield 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
serdeandas_str(). VerdictReason::ALLconstant (11 variants) iterated exhaustively — adding a variant without updating ALL fails the test.VerdictReason::ALL_CODESconstant checked againstas_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 inmain.rs, outside the policy evaluator.- System errors emitted in
main.rshandler code:- Policy load failure emits
PolicyLoadError. - Mempool required but unavailable with no fallback emits
MempoolBackendUnavailable. - Unexpected panic emits
InternalError.
- Policy load failure emits
- Degraded mode (
unknown_mempool_as_high = true) proceeds with high tier assumption: not an error, deliberate feature. - Poisoned policy lock recovery extracts config synchronously (
RwLockReadGuarddropped before any.await) and emitsPolicyLoadErrorwithcontinue— 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 fromunknown_mempool_as_highflag.
- No new
reason_codeemitted. Observability only.
Documentation alignment
beta-runbook.md
- Section 6.6: Remove fallback language. Stats use
reason_codedirectly. - 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 = trueto block empty blocks. - This is policy choice, not a bug.
Website samples (veldra-site repo)
index.htmlsample verdict: Fix to match actualTemplateVerdictstruct.reason_code:"avg_fee_below_minimum"(was inventedMIN_AVG_FEE_TOO_LOW).policy_contextfields match actualPolicyContext.policy/index.htmlsample contract: Same fix.- Reason codes table: Add note that
policy_load_error,mempool_backend_unavailable,internal_errorare system-level codes (not policy evaluation results).
Hardening (implemented)
- Verdict log rotation: 50 MB cap per file, 5 rotations (
verdicts.log→verdicts.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: EveryVerdictReasonhasas_str()coverage.eval_result_exhaustive: Every policy rejection path returns a validVerdictReasonvariant viaEvalResult.tier_naming_consistent:FeeTier::as_str()returns"low","mid","high"only.policy_context_tier_values:PolicyContext.fee_tieronly contains canonical tier names.stats_aggregation_no_normalization: Stats bucket keys match rawreason_codevalues.all_codes_match_as_str:VerdictReason::ALL_CODES[i] == VerdictReason::ALL[i].as_str()for alli.
Files changed
rg-protocol
src/lib.rs: AddVerdictReason::as_str(),VerdictReason::all_codes(), fix tier comment.
pool-verifier
src/policy.rs: Delete localVerdictReasonenum, addEvalResultstruct, add#[cfg(test)]mod tests (6 tests).src/main.rs: Deletewire_reason_code_str(), deletenormalize_reason_key(), addtier_sourcetoLoggedVerdict, add poisoned lock handler withPolicyLoadError, 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.