reeve 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +293 -3
  3. data/README.md +276 -11
  4. data/lib/generators/reeve/install/templates/create_audit_entries.rb.tt +52 -5
  5. data/lib/generators/reeve/install/templates/initializer.rb.tt +10 -0
  6. data/lib/generators/reeve/upgrade/templates/add_key_epochs_to_reeve_audit_entries.rb.tt +35 -0
  7. data/lib/generators/reeve/upgrade/templates/add_ledger_retention_to_reeve_audit_entries.rb.tt +37 -0
  8. data/lib/generators/reeve/upgrade/upgrade_generator.rb +19 -25
  9. data/lib/generators/reeve/upgrade/upgrade_notes.rb +62 -0
  10. data/lib/reeve/audit/chain/anchoring.rb +91 -0
  11. data/lib/reeve/audit/chain/canonical.rb +38 -7
  12. data/lib/reeve/audit/chain/checkpoint.rb +228 -0
  13. data/lib/reeve/audit/chain/checkpoint_check.rb +156 -0
  14. data/lib/reeve/audit/chain/entrypoints.rb +29 -0
  15. data/lib/reeve/audit/chain/epoch_rules.rb +159 -0
  16. data/lib/reeve/audit/chain/epochs.rb +83 -0
  17. data/lib/reeve/audit/chain/head.rb +55 -2
  18. data/lib/reeve/audit/chain/keyring.rb +6 -0
  19. data/lib/reeve/audit/chain/report.rb +232 -32
  20. data/lib/reeve/audit/chain/retention/archive.rb +98 -0
  21. data/lib/reeve/audit/chain/retention/archive_check.rb +179 -0
  22. data/lib/reeve/audit/chain/retention/check.rb +94 -0
  23. data/lib/reeve/audit/chain/retention/connection.rb +65 -0
  24. data/lib/reeve/audit/chain/retention/digest.rb +29 -0
  25. data/lib/reeve/audit/chain/retention/plan.rb +100 -0
  26. data/lib/reeve/audit/chain/retention/preflight.rb +48 -0
  27. data/lib/reeve/audit/chain/retention/purge.rb +155 -0
  28. data/lib/reeve/audit/chain/retention/purge_result.rb +134 -0
  29. data/lib/reeve/audit/chain/retention/records.rb +23 -0
  30. data/lib/reeve/audit/chain/rotation.rb +124 -0
  31. data/lib/reeve/audit/chain/schema.rb +59 -0
  32. data/lib/reeve/audit/chain/verification.rb +53 -10
  33. data/lib/reeve/audit/chain/verifier.rb +75 -22
  34. data/lib/reeve/audit/chain/walk.rb +24 -4
  35. data/lib/reeve/audit/chain.rb +61 -13
  36. data/lib/reeve/audit/chain_epoch.rb +24 -0
  37. data/lib/reeve/audit/chain_record.rb +14 -8
  38. data/lib/reeve/audit/file_anchor.rb +49 -0
  39. data/lib/reeve/audit/retention_record.rb +24 -0
  40. data/lib/reeve/audit.rb +30 -3
  41. data/lib/reeve/configuration.rb +25 -0
  42. data/lib/reeve/errors.rb +15 -0
  43. data/lib/reeve/railtie.rb +2 -1
  44. data/lib/reeve/tasks/descriptions.rb +22 -0
  45. data/lib/reeve/tasks/purge_options.rb +54 -0
  46. data/lib/reeve/tasks.rb +104 -7
  47. data/lib/reeve/testing/checks/contract_version.rb +2 -2
  48. data/lib/reeve/testing/checks/ledger_chain.rb +56 -2
  49. data/lib/reeve/testing/ledger.rb +19 -3
  50. data/lib/reeve/version.rb +1 -1
  51. metadata +28 -2
@@ -20,13 +20,25 @@ module Reeve
20
20
  # * **nothing to write or read** (no compliance tools, ledger not loaded) — skipped,
21
21
  # saying the chain was not verified. Never passed.
22
22
  #
23
+ # With `anchored: true` (specs/004 research R8) it also makes a checkpoint after writing,
24
+ # publishes it through config.ledger_anchor, reads the anchor back, and verifies against
25
+ # that checkpoint alone. It passes only when the chain is intact and anchored at it: what
26
+ # a host's CI can prove is that its anchor is configured and round-trips. Whether
27
+ # production's checkpoints match production is the scheduled verify_ledger's job. Without
28
+ # it, the anchor is never read.
29
+ #
23
30
  # Reeve::Checks::LedgerChain.new.call
31
+ # Reeve::Checks::LedgerChain.new(anchored: true).call
24
32
  class LedgerChain < Base
25
33
  NOT_VERIFIED = "the ledger chain was not verified"
34
+ NOT_ANCHORED = "expected the ledger to be anchored"
35
+ NOT_RETURNED = "the checkpoint it published did not come back from config.ledger_anchor"
26
36
 
27
- def initialize(tools: nil, principals: nil, arguments: {}, invoke: nil, ledger: nil)
37
+ def initialize(tools: nil, principals: nil, arguments: {}, invoke: nil, ledger: nil,
38
+ anchored: false)
28
39
  @tools = tools
29
40
  @principals = principals
41
+ @anchored = anchored
30
42
  super(tool: nil, arguments: arguments, invoke: invoke, ledger: ledger)
31
43
  end
32
44
 
@@ -37,12 +49,54 @@ module Reeve
37
49
 
38
50
  unwritten = write_entries
39
51
  return unwritten_entry(unwritten) if unwritten
52
+ return judge(ledger.verify_chain) unless @anchored
40
53
 
41
- judge(ledger.verify_chain)
54
+ anchored
42
55
  end
43
56
 
44
57
  private
45
58
 
59
+ def anchored
60
+ return not_anchored("no anchor is configured: set config.ledger_anchor") if anchor.nil?
61
+ unless ledger.respond_to?(:checkpoint!)
62
+ return skipped("this ledger cannot make a checkpoint")
63
+ end
64
+
65
+ checkpoint, failure = round_trip
66
+ return not_anchored(failure) if failure
67
+
68
+ judge_anchored(ledger.verify_chain(checkpoints: [checkpoint]), checkpoint)
69
+ end
70
+
71
+ # The checkpoint it made and read back from the anchor, or why it could not.
72
+ def round_trip
73
+ made = ledger.checkpoint!
74
+ return [nil, made.to_s.lines.first.chomp] unless made&.made? && made.published?
75
+ unless Array(anchor.checkpoints).include?(made.checkpoint.to_line)
76
+ return [nil,
77
+ NOT_RETURNED]
78
+ end
79
+
80
+ [made.checkpoint, nil]
81
+ end
82
+
83
+ def anchor
84
+ Reeve.config.ledger_anchor
85
+ end
86
+
87
+ def judge_anchored(verification, checkpoint)
88
+ return judge(verification) unless verification&.intact?
89
+ if verification.anchored_at&.head_sequence == checkpoint.head_sequence
90
+ return judge(verification)
91
+ end
92
+
93
+ not_anchored(verification.to_s.lines.first.chomp)
94
+ end
95
+
96
+ def not_anchored(reason)
97
+ failed("#{NOT_ANCHORED}, but #{reason}", coverage: :not_anchored)
98
+ end
99
+
46
100
  def subject
47
101
  @subject ||= Array(@tools || Testing.compliance_tools).first
48
102
  end
@@ -71,14 +71,30 @@ module Reeve
71
71
  # The chain's verification of the whole ledger (specs/002), or nil when the ledger is
72
72
  # not loaded. The kit's only way to the chain: it names Reeve::Audit::Chain here, at
73
73
  # call time, so `require "reeve/testing"` still loads with no ActiveRecord at all.
74
- def verify_chain
75
- return nil unless available? && Reeve::Audit.const_defined?(:Chain, false)
74
+ #
75
+ # Against `checkpoints` only, never the configured anchor's (specs/004 research R8): a
76
+ # test database is emptied between runs, and every checkpoint an earlier run left names
77
+ # a chain that is gone.
78
+ def verify_chain(checkpoints: [])
79
+ return nil unless chain?
80
+
81
+ Reeve::Audit::Chain.verify(entry_class: entry_class, checkpoints: checkpoints)
82
+ end
83
+
84
+ # A checkpoint of the ledger, published to config.ledger_anchor (specs/004), or nil when
85
+ # the ledger is not loaded.
86
+ def checkpoint!
87
+ return nil unless chain?
76
88
 
77
- Reeve::Audit::Chain.verify(entry_class: entry_class)
89
+ Reeve::Audit::Chain.checkpoint!(entry_class: entry_class)
78
90
  end
79
91
 
80
92
  private
81
93
 
94
+ def chain?
95
+ available? && Reeve::Audit.const_defined?(:Chain, false)
96
+ end
97
+
82
98
  def entry_class
83
99
  return @entry_class if defined?(@entry_class)
84
100
 
data/lib/reeve/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Reeve
4
- VERSION = "0.6.0"
4
+ VERSION = "0.8.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reeve
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Velazquez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-10 00:00:00.000000000 Z
11
+ date: 2026-10-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Reeve makes it safe for a Rails application to expose MCP (Model Context Protocol)
@@ -31,25 +31,49 @@ files:
31
31
  - lib/generators/reeve/install/templates/create_audit_entries.rb.tt
32
32
  - lib/generators/reeve/install/templates/initializer.rb.tt
33
33
  - lib/generators/reeve/upgrade/templates/add_contract_version_to_reeve_audit_entries.rb.tt
34
+ - lib/generators/reeve/upgrade/templates/add_key_epochs_to_reeve_audit_entries.rb.tt
34
35
  - lib/generators/reeve/upgrade/templates/add_ledger_chain_to_reeve_audit_entries.rb.tt
36
+ - lib/generators/reeve/upgrade/templates/add_ledger_retention_to_reeve_audit_entries.rb.tt
35
37
  - lib/generators/reeve/upgrade/upgrade_generator.rb
38
+ - lib/generators/reeve/upgrade/upgrade_notes.rb
36
39
  - lib/reeve.rb
37
40
  - lib/reeve/audit.rb
38
41
  - lib/reeve/audit/chain.rb
42
+ - lib/reeve/audit/chain/anchoring.rb
39
43
  - lib/reeve/audit/chain/canonical.rb
44
+ - lib/reeve/audit/chain/checkpoint.rb
45
+ - lib/reeve/audit/chain/checkpoint_check.rb
46
+ - lib/reeve/audit/chain/entrypoints.rb
47
+ - lib/reeve/audit/chain/epoch_rules.rb
48
+ - lib/reeve/audit/chain/epochs.rb
40
49
  - lib/reeve/audit/chain/head.rb
41
50
  - lib/reeve/audit/chain/keyring.rb
42
51
  - lib/reeve/audit/chain/normalizer.rb
43
52
  - lib/reeve/audit/chain/report.rb
53
+ - lib/reeve/audit/chain/retention/archive.rb
54
+ - lib/reeve/audit/chain/retention/archive_check.rb
55
+ - lib/reeve/audit/chain/retention/check.rb
56
+ - lib/reeve/audit/chain/retention/connection.rb
57
+ - lib/reeve/audit/chain/retention/digest.rb
58
+ - lib/reeve/audit/chain/retention/plan.rb
59
+ - lib/reeve/audit/chain/retention/preflight.rb
60
+ - lib/reeve/audit/chain/retention/purge.rb
61
+ - lib/reeve/audit/chain/retention/purge_result.rb
62
+ - lib/reeve/audit/chain/retention/records.rb
63
+ - lib/reeve/audit/chain/rotation.rb
64
+ - lib/reeve/audit/chain/schema.rb
44
65
  - lib/reeve/audit/chain/verification.rb
45
66
  - lib/reeve/audit/chain/verifier.rb
46
67
  - lib/reeve/audit/chain/walk.rb
68
+ - lib/reeve/audit/chain_epoch.rb
47
69
  - lib/reeve/audit/chain_record.rb
48
70
  - lib/reeve/audit/entry.rb
71
+ - lib/reeve/audit/file_anchor.rb
49
72
  - lib/reeve/audit/isolated_recorder.rb
50
73
  - lib/reeve/audit/query.rb
51
74
  - lib/reeve/audit/recorder.rb
52
75
  - lib/reeve/audit/redactor.rb
76
+ - lib/reeve/audit/retention_record.rb
53
77
  - lib/reeve/authorization.rb
54
78
  - lib/reeve/authorization/adapter.rb
55
79
  - lib/reeve/authorization/adapters/plain.rb
@@ -76,6 +100,8 @@ files:
76
100
  - lib/reeve/rspec.rb
77
101
  - lib/reeve/scope_result.rb
78
102
  - lib/reeve/tasks.rb
103
+ - lib/reeve/tasks/descriptions.rb
104
+ - lib/reeve/tasks/purge_options.rb
79
105
  - lib/reeve/testing.rb
80
106
  - lib/reeve/testing/assertions.rb
81
107
  - lib/reeve/testing/checks.rb