fix(api): make revision approval, signed action consumption and audit receipts transactional #40

Closed
opened 2026-10-14 03:11:07 +00:00 by PlasticDigits · 2 comments
Owner

Scope and source

Priority: P0. Gap coverage: GAP-005, GAP-007, GAP-016, GAP-017, GAP-024, GAP-042. Derived from GAP_1791945837.md, reviewed code commit f8dbe93df953a78b17cb3304b717dcf84c4cd517. Current e476a68da09645c2308cbf47b90211fcaffcb7af adds only the report, so implementation findings remain applicable. Report evidence is local reproduction/source inspection unless explicitly proposed; no production exploit or current live-readiness claim.

Coverage and sequencing: gap backlog tracker. Workstream 05. Dependencies: 02, 03.

Current codebase

Amendment hash checks precede a DB update that predicates only on draft status; title-only changes do not change body hash. Opening and analysis validate outside their mutation transactions. Signatures always get new UUIDs and replay creates duplicate drafts/comments/analysis. Comment caps use separate COUNT checks and analysis has no wallet quota. Timestamp subtraction can overflow. Stored hashes omit original signed payload bytes and proposal/revision linkage.

Why this implementation is needed

Concurrent edits can open unreviewed content, replay can spam records, quotas can race, and persisted records cannot independently prove what was signed.

Relevant files

Introduce complete monotonic revisions covering title/sections and future schedule/editor metadata. Inside one transactional service, verify actor/role/blacklist, expected revision/state, independence, quota and unique nonce/action consumption, then mutate and persist a receipt. Opening verifies the signed complete revision and performs its existing one-time freeze in that transaction; analysis references the reviewed revision. Return prior receipts for safe response-loss retries. Require ordered timestamps, bounded issue time and positive checked TTL with injectable clock. Store exact signed bytes, scheme/domain, action/revision links, content hashes, actor/opening and freeze metadata; retain immutable history and export verifiable receipts. Define versioned payload migration and explicit legacy-receipt limits.

Constraints and guardrails

Preserve standalone offchain advisory voting, no new smart contracts or archive-node dependency, BSC as a core electorate, raw 18-decimal integer balances and separate Terra/BSC address identities. Keep Legal fail-closed transactional UI and the proven DEX Terra/Bridge EVM connectors; no browser BSC balance RPC, seed/key prompt or embedded admin/model credential. API must retain a restricted role separate from the ledger writer. Freeze at actual opening, never at draft creation or a client-supplied height. Agents own design, implementation, review and evidence under repository guidance; do not shift approvals/tests to the human or invent PASS evidence.

Acceptance criteria

One of concurrent stale revisions succeeds; title-only changes invalidate approval. No analysis is appended after opening and quota limits hold under concurrency. Replayed accepted actions cannot create extra state/signature rows. Timestamp extrema neither panic nor bypass TTL. Independent verification reconstructs accepted Terra/BSC actions from persisted data alone.

Test plan: supported paths, failure paths and concurrency

Full ADR-36 wallet-format and EIP-191 register→draft→amend→comment→analysis→open→vote lifecycles. Barrier-controlled amend/amend, amend/open, analyze/open, quota-at-cap and duplicate-open/action races. Wrong actor/revision/hash/status, author analysis, blacklisted actors, expired/skewed/reversed/extreme times, release arithmetic, lost responses, DB rollback and process restart. Verify original versus superseded revisions and immutable exported receipts.

Test plan: attack, hack and abuse vectors

Replay across purposes/chains/proposals; nonce reuse with altered bytes; forged signer/domain, cross-scheme blobs, extreme timestamps and signature malleability; parallel spam bypassing caps; receipt tampering, actor privilege changes at commit and opening unreviewed title/editor/schedule. Assert no partial mutation or unverifiable successful action.

Verification and completion criteria

Attach concurrency barriers/results and independent receipt-verifier output including tamper failures. Run Rust unit/debug and release timestamp regressions plus isolated DB integration tests for both signature schemes; document protocol/backfill compatibility without claiming old hash-only rows are fully verifiable.

Record commit and pinned toolchain, commands/results and sanitized artifacts. Relevant Rust changes require cargo test --workspace --lib plus isolated Postgres integration (explicit guarded LEDGER_TEST_DATABASE_URL); frontend changes require npm test and production build plus relevant browser cases. Run secret scanning for new fixtures/config. Exercise fresh/upgrade migrations when schema/grants change; report skipped/live-only checks explicitly.

<!-- voting-gap-1791945837:05 --> ## Scope and source **Priority:** P0. **Gap coverage:** GAP-005, GAP-007, GAP-016, GAP-017, GAP-024, GAP-042. Derived from [GAP_1791945837.md](https://git.cl8y.com/code/voting/src/branch/main/gaps/GAP_1791945837.md), reviewed code commit `f8dbe93df953a78b17cb3304b717dcf84c4cd517`. Current `e476a68da09645c2308cbf47b90211fcaffcb7af` adds only the report, so implementation findings remain applicable. Report evidence is local reproduction/source inspection unless explicitly proposed; no production exploit or current live-readiness claim. Coverage and sequencing: [gap backlog tracker](https://git.cl8y.com/code/voting/issues/35). Workstream **05**. Dependencies: [02](https://git.cl8y.com/code/voting/issues/37), [03](https://git.cl8y.com/code/voting/issues/38). ## Current codebase Amendment hash checks precede a DB update that predicates only on draft status; title-only changes do not change body hash. Opening and analysis validate outside their mutation transactions. Signatures always get new UUIDs and replay creates duplicate drafts/comments/analysis. Comment caps use separate COUNT checks and analysis has no wallet quota. Timestamp subtraction can overflow. Stored hashes omit original signed payload bytes and proposal/revision linkage. ## Why this implementation is needed Concurrent edits can open unreviewed content, replay can spam records, quotas can race, and persisted records cannot independently prove what was signed. ## Relevant files - [`operator-voting/src/api.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/api.rs) - [`operator-voting/src/db.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/db.rs) - [`operator-voting/src/payload.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/payload.rs) - [`operator-voting/src/crypto/adr36.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/crypto/adr36.rs) - [`operator-voting/src/crypto/eip191.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/crypto/eip191.rs) - [`ledger/migrations/20260825000002_voting.sql`](https://git.cl8y.com/code/voting/src/branch/main/ledger/migrations/20260825000002_voting.sql) - [`ledger/migrations/20260830000002_draft_lifecycle.sql`](https://git.cl8y.com/code/voting/src/branch/main/ledger/migrations/20260830000002_draft_lifecycle.sql) - [`operator-voting/tests/api_flow.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/tests/api_flow.rs) - [`docs/OPERATOR_VOTING.md`](https://git.cl8y.com/code/voting/src/branch/main/docs/OPERATOR_VOTING.md) ## Recommended solution Introduce complete monotonic revisions covering title/sections and future schedule/editor metadata. Inside one transactional service, verify actor/role/blacklist, expected revision/state, independence, quota and unique nonce/action consumption, then mutate and persist a receipt. Opening verifies the signed complete revision and performs its existing one-time freeze in that transaction; analysis references the reviewed revision. Return prior receipts for safe response-loss retries. Require ordered timestamps, bounded issue time and positive checked TTL with injectable clock. Store exact signed bytes, scheme/domain, action/revision links, content hashes, actor/opening and freeze metadata; retain immutable history and export verifiable receipts. Define versioned payload migration and explicit legacy-receipt limits. ## Constraints and guardrails Preserve standalone offchain advisory voting, no new smart contracts or archive-node dependency, BSC as a core electorate, raw 18-decimal integer balances and separate Terra/BSC address identities. Keep Legal fail-closed transactional UI and the proven DEX Terra/Bridge EVM connectors; no browser BSC balance RPC, seed/key prompt or embedded admin/model credential. API must retain a restricted role separate from the ledger writer. Freeze at actual opening, never at draft creation or a client-supplied height. Agents own design, implementation, review and evidence under repository guidance; do not shift approvals/tests to the human or invent PASS evidence. ## Acceptance criteria One of concurrent stale revisions succeeds; title-only changes invalidate approval. No analysis is appended after opening and quota limits hold under concurrency. Replayed accepted actions cannot create extra state/signature rows. Timestamp extrema neither panic nor bypass TTL. Independent verification reconstructs accepted Terra/BSC actions from persisted data alone. ## Test plan: supported paths, failure paths and concurrency Full ADR-36 wallet-format and EIP-191 register→draft→amend→comment→analysis→open→vote lifecycles. Barrier-controlled amend/amend, amend/open, analyze/open, quota-at-cap and duplicate-open/action races. Wrong actor/revision/hash/status, author analysis, blacklisted actors, expired/skewed/reversed/extreme times, release arithmetic, lost responses, DB rollback and process restart. Verify original versus superseded revisions and immutable exported receipts. ## Test plan: attack, hack and abuse vectors Replay across purposes/chains/proposals; nonce reuse with altered bytes; forged signer/domain, cross-scheme blobs, extreme timestamps and signature malleability; parallel spam bypassing caps; receipt tampering, actor privilege changes at commit and opening unreviewed title/editor/schedule. Assert no partial mutation or unverifiable successful action. ## Verification and completion criteria Attach concurrency barriers/results and independent receipt-verifier output including tamper failures. Run Rust unit/debug and release timestamp regressions plus isolated DB integration tests for both signature schemes; document protocol/backfill compatibility without claiming old hash-only rows are fully verifiable. Record commit and pinned toolchain, commands/results and sanitized artifacts. Relevant Rust changes require `cargo test --workspace --lib` plus isolated Postgres integration (explicit guarded LEDGER_TEST_DATABASE_URL); frontend changes require `npm test` and production build plus relevant browser cases. Run secret scanning for new fixtures/config. Exercise fresh/upgrade migrations when schema/grants change; report skipped/live-only checks explicitly.
Author
Owner

Implementation status after main merge PR #53, commit cd07f00f32f04ad02f8b760ed34588fd9645919e:

  • Transactional revisions, signed action consumption and verifiable receipts implemented with regression tests and crosslinked invariants/agent guidance.
  • Combined Rust1.88.0, isolated PostgreSQL, frontend/build/browser, OpenAPI and secret checks passed; see evidence.
  • Live release verification is still in progress. The first Coolify attempts failed before healthy acceptance. PR #54 fixes native-RPC entrypoint wiring, frontend image mode, liveness probes and pooled schema selection, with additional process/fault/privilege tests.

Services are paused while existing storage is audited and the corrected pinned release is prepared. No production wallet transaction or hardware-wallet/Legal QA is claimed. This is an interim report; the issue remains open and the final comment will map the original criteria to actual live results and any unverified cases.

Implementation status after main merge [PR #53](https://git.cl8y.com/code/voting/pulls/53), commit `cd07f00f32f04ad02f8b760ed34588fd9645919e`: - [x] Transactional revisions, signed action consumption and verifiable receipts implemented with regression tests and crosslinked invariants/agent guidance. - [x] Combined Rust1.88.0, isolated PostgreSQL, frontend/build/browser, OpenAPI and secret checks passed; see [evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-issues-36-43.md). - [ ] Live release verification is still in progress. The first Coolify attempts failed before healthy acceptance. [PR #54](https://git.cl8y.com/code/voting/pulls/54) fixes native-RPC entrypoint wiring, frontend image mode, liveness probes and pooled schema selection, with additional process/fault/privilege tests. Services are paused while existing storage is audited and the corrected pinned release is prepared. No production wallet transaction or hardware-wallet/Legal QA is claimed. This is an interim report; the issue remains open and the final comment will map the original criteria to actual live results and any unverified cases.
Author
Owner

Implemented transactional revision approval, replay-safe signed actions and independently verifiable receipts. Changes are in PR #53, #54, #55, #56, and the throughput/test followup #59. PR #59 is merged with passing CI. Ledger/API/frontend deployments finished and are running healthy at final code 5cd9363307dc1607de952ac3243193c1a16304c7. Final live evidence.

Original acceptance and test-plan checklist:

  • One concurrent stale amendment succeeds; title-only changes advance the complete revision and invalidate old approval. Opening checks the signed revision/title/hash inside the transaction. Barrier-controlled amend/amend, amend/open, analyze/open and duplicate-open/action races pass on both chains.
  • Analysis cannot append after opening and requires an independent committee actor reviewing the current revision. Parallel analysis admits 5 of 7 requests; concurrent comments leave exactly cap 20. Shared authenticated database quotas hold across application instances; unsigned identities cannot charge another wallet and accepted retries are free.
  • Exact accepted retries return the original persisted response without extra state/signatures. Altered bytes reusing a nonce conflict. Full ADR-36 wallet-format and EIP-191 register/draft/amend/comment/analysis/open/vote lifecycles pass through production middleware and the restricted SQL pool. Wrong actor/revision/hash/status, self-analysis, blacklist and fresh-nonce duplicate votes reject without partial state.
  • Ordered timestamps, issue skew, expiry and checked positive TTL reject extreme/reversed values in debug and optimized release. A controlled transaction wait past expiry rolls back signatures/proposal/revision/receipt. Lost-response and actual API SIGKILL/restart retries retain one accepted action after expiry; prepared revoked actors reject after restart. Policy configuration is immutable per process and changes require restart.
  • Independent offline verification reconstructs both-chain accepted actions from exact persisted bytes, original signatures/public keys, scheme/domain and action/proposal/revision links. Original receipts remain verifiable after supersession. Tampered bytes/title/sections/HTML/IDs/revision/time fail; the CLI reports verified wallet signature and signed action/content links. Genuine high-s, wrong network, cross-scheme, purpose and proposal-target attacks reject without records.
  • Opening freezes once at actual admission, ignores client heights, checks both canonical checkpoints/intent progress, and retains immutable weights/content/history. The throughput followup preserves 32-block bounds and 2/17 lag policy; actual selected end governs cursor and baseline certification, future baselines stay pending, and final same-source hash/head checks follow accounting SQL before commit. Moving-head, post-SQL fault and real-process catch-up regressions pass; independent review found no financial/atomic blocker.
  • Version 2 migration/shared signing vectors, legacy/backfill limits, invariants and agent guidance are crosslinked in OPERATOR_VOTING.md and AGENTS_TRANSACTIONAL_VOTING.md. Standalone advisory voting, core BSC, raw balances, separate identities, established Legal/wallet flows and server-owned balance reads remain intact; no new contract/archive dependency or secret prompt was introduced.
  • Pinned Rust 1.88.0 CARGO_BUILD_JOBS=2 scripts/verify-release.sh release13 passes 74 unit/supervisor tests, 121 integrations (66 ledger), 245 schema validations and 146 frontend tests, production build, both nginx checks and Gitleaks. Machine-readable results, combined report, and lifecycle evidence record commands, barriers, receipt/tamper and migration results. The late supervised database-recovery exit fixture is corrected: at most one nonzero restart, then fresh both-chain/intent progress and sustained readiness; four fresh repeats pass.
  • PR #59 CI/main merge and exact-code Coolify deployment complete at 5cd9363307dc1607de952ac3243193c1a16304c7. All three services are running healthy; smoke 13, four canonical fixtures and runtime configuration checks pass. Loki contains no post-activation ledger warnings; the direct Coolify rpc_missing_result warning at 13:56:21 precedes activation at 13:58:22. Final artifact.
  • Accepted final private readiness windows 240+240+600 are all green (1080 observations; maximum Terra lag 2/BSC 17). Two later public 45-check windows are all green (90 consecutive observations). Both original unfunded Terra/BSC receipts verify independently; exact retries including after signed expiry return the same response. Final artifact.

Operational observation: the first public window returned 44/45 successful checks, with one 503 at 14:00:58 followed by 200 on the next check. Its cause remains unexplained and is retained in the evidence. Subsequent accepted public/private windows pass with unchanged thresholds and actual observed tips; no unhealthy observation is represented as healthy.

Evidence limits: funded-holder/committee live proposal/vote, hardware/mobile wallet and live human Legal QA are unexecuted broader variants. Hash-only legacy receipts cannot recover original signed bytes; frozen heights/power remain operator ledger evidence. Future schedule/editor fields must join the complete signed revision if introduced. The signed lifecycle, concurrency, replay, timestamp and independent-verification criteria are satisfied by the evidence above. The recorded 503 shows no failure of a signed action or persisted-record invariant.

Final evidence is published through PR #60. All mandatory body/comment criteria are satisfied by the checklist above; closing this issue.

Implemented transactional revision approval, replay-safe signed actions and independently verifiable receipts. Changes are in [PR #53](https://git.cl8y.com/code/voting/pulls/53), [#54](https://git.cl8y.com/code/voting/pulls/54), [#55](https://git.cl8y.com/code/voting/pulls/55), [#56](https://git.cl8y.com/code/voting/pulls/56), and the throughput/test followup [#59](https://git.cl8y.com/code/voting/pulls/59). PR #59 is merged with passing CI. Ledger/API/frontend deployments finished and are running healthy at final code `5cd9363307dc1607de952ac3243193c1a16304c7`. [Final live evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-final-live.json). Original acceptance and test-plan checklist: - [x] One concurrent stale amendment succeeds; title-only changes advance the complete revision and invalidate old approval. Opening checks the signed revision/title/hash inside the transaction. Barrier-controlled amend/amend, amend/open, analyze/open and duplicate-open/action races pass on both chains. - [x] Analysis cannot append after opening and requires an independent committee actor reviewing the current revision. Parallel analysis admits 5 of 7 requests; concurrent comments leave exactly cap 20. Shared authenticated database quotas hold across application instances; unsigned identities cannot charge another wallet and accepted retries are free. - [x] Exact accepted retries return the original persisted response without extra state/signatures. Altered bytes reusing a nonce conflict. Full ADR-36 wallet-format and EIP-191 register/draft/amend/comment/analysis/open/vote lifecycles pass through production middleware and the restricted SQL pool. Wrong actor/revision/hash/status, self-analysis, blacklist and fresh-nonce duplicate votes reject without partial state. - [x] Ordered timestamps, issue skew, expiry and checked positive TTL reject extreme/reversed values in debug and optimized release. A controlled transaction wait past expiry rolls back signatures/proposal/revision/receipt. Lost-response and actual API SIGKILL/restart retries retain one accepted action after expiry; prepared revoked actors reject after restart. Policy configuration is immutable per process and changes require restart. - [x] Independent offline verification reconstructs both-chain accepted actions from exact persisted bytes, original signatures/public keys, scheme/domain and action/proposal/revision links. Original receipts remain verifiable after supersession. Tampered bytes/title/sections/HTML/IDs/revision/time fail; the CLI reports `verified wallet signature and signed action/content links`. Genuine high-s, wrong network, cross-scheme, purpose and proposal-target attacks reject without records. - [x] Opening freezes once at actual admission, ignores client heights, checks both canonical checkpoints/intent progress, and retains immutable weights/content/history. The throughput followup preserves 32-block bounds and 2/17 lag policy; actual selected end governs cursor and baseline certification, future baselines stay pending, and final same-source hash/head checks follow accounting SQL before commit. Moving-head, post-SQL fault and real-process catch-up regressions pass; independent review found no financial/atomic blocker. - [x] Version 2 migration/shared signing vectors, legacy/backfill limits, invariants and agent guidance are crosslinked in [OPERATOR_VOTING.md](https://git.cl8y.com/code/voting/src/branch/main/docs/OPERATOR_VOTING.md) and [AGENTS_TRANSACTIONAL_VOTING.md](https://git.cl8y.com/code/voting/src/branch/main/skills/AGENTS_TRANSACTIONAL_VOTING.md). Standalone advisory voting, core BSC, raw balances, separate identities, established Legal/wallet flows and server-owned balance reads remain intact; no new contract/archive dependency or secret prompt was introduced. - [x] Pinned Rust 1.88.0 `CARGO_BUILD_JOBS=2 scripts/verify-release.sh` release13 passes 74 unit/supervisor tests, 121 integrations (66 ledger), 245 schema validations and 146 frontend tests, production build, both nginx checks and Gitleaks. [Machine-readable results](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-release13.json), [combined report](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-issues-36-43.md), and [lifecycle evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-lifecycle-40-42.md) record commands, barriers, receipt/tamper and migration results. The late supervised database-recovery exit fixture is corrected: at most one nonzero restart, then fresh both-chain/intent progress and sustained readiness; four fresh repeats pass. - [x] PR #59 CI/main merge and exact-code Coolify deployment complete at `5cd9363307dc1607de952ac3243193c1a16304c7`. All three services are running healthy; smoke 13, four canonical fixtures and runtime configuration checks pass. Loki contains no post-activation ledger warnings; the direct Coolify rpc_missing_result warning at 13:56:21 precedes activation at 13:58:22. [Final artifact](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-final-live.json). - [x] Accepted final private readiness windows 240+240+600 are all green (1080 observations; maximum Terra lag 2/BSC 17). Two later public 45-check windows are all green (90 consecutive observations). Both original unfunded Terra/BSC receipts verify independently; exact retries including after signed expiry return the same response. [Final artifact](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-final-live.json). Operational observation: the first public window returned 44/45 successful checks, with one 503 at 14:00:58 followed by 200 on the next check. Its cause remains unexplained and is retained in the evidence. Subsequent accepted public/private windows pass with unchanged thresholds and actual observed tips; no unhealthy observation is represented as healthy. Evidence limits: funded-holder/committee live proposal/vote, hardware/mobile wallet and live human Legal QA are unexecuted broader variants. Hash-only legacy receipts cannot recover original signed bytes; frozen heights/power remain operator ledger evidence. Future schedule/editor fields must join the complete signed revision if introduced. The signed lifecycle, concurrency, replay, timestamp and independent-verification criteria are satisfied by the evidence above. The recorded 503 shows no failure of a signed action or persisted-record invariant. Final evidence is published through [PR #60](https://git.cl8y.com/code/voting/pulls/60). All mandatory body/comment criteria are satisfied by the checklist above; closing this issue.
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/voting#40
No description provided.