kairos-chain 3.45.0 → 3.47.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea7401710d6467287e35146c84d731c08733f184fac36be13e76de11d7af5433
4
- data.tar.gz: a823ba8bf1bb1157394ff04466cdcf96c0347eee50796816fe8c2fd1d188792c
3
+ metadata.gz: e43541279a3d104b85372574c94f6fdf2d96188fb7de83d5a4d84c3f470634d7
4
+ data.tar.gz: 21b58d11561dd6d4177a7aa3d3832bdfe4fbde80ff2da2af198662d655a6fc81
5
5
  SHA512:
6
- metadata.gz: a11ba74c10eb1c56ef15b57992755083b6d36bd4fef1ea73bf1cae64afc1f07ad26f8774ee703c3c98eefa25c8a422441c5c5af032b9ae523d7518c7dc50193d
7
- data.tar.gz: 26f95dd3245e9e39311349ef540954d200985a59bf9da38b3b08256d11ec50a445d60cc3f0c41538b6a0c0bfb2eb591ce742dc4523950d9ffe8f06471d22e50a
6
+ metadata.gz: 41bc6c95594567c2e80ed006ea2bd21e7926361bf3cfb14fdf5a6b0a4aac17b9dca3f65faf17be1a4e86b16ce3fb9285af121d747fcdeac66eca58857d4af0bd
7
+ data.tar.gz: 77a5ac53384830598e90b408b4c737eb8549c219dfdeb47c38feb5939c46829c2b53dc9744da1a9914033521ff1fdf126f8d941989fe62857316863a75790e5f
data/CHANGELOG.md CHANGED
@@ -4,6 +4,84 @@ All notable changes to the `kairos-chain` gem will be documented in this file.
4
4
 
5
5
  This project follows [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [3.47.0] - 2026-07-22
8
+
9
+ ### Synoptis — selective disclosure sdp-1 (AUD-L4 slice 1)
10
+
11
+ Implements AUD-L4 design v0.3 (SDP-1..5, FROZEN, review-converged) slice 1
12
+ in the synoptis SkillSet. All additive: sdp-1 builds on khab-1/map-1/rpr-1
13
+ exactly as each prior slice built on its predecessors; no pre-existing file
14
+ changed.
15
+
16
+ - `conventions/sdp-1.md`: content-addressed selective-disclosure convention —
17
+ hash-based salted field-level disclosure (content-blinding: field values
18
+ hidden, record digest / field names / count public; the ZK membership
19
+ family is disclosed as out of scope for a later convention). Field
20
+ commitments (one salted digest per field, total coverage, `sdp-1/field`
21
+ domain separation), convention-determined disclosure profile (closed
22
+ schema; predicate + opened set + currency, `format` always opened),
23
+ presentation (closed schema per shape), three predicates
24
+ (typed-existence / claimed-verdict / conforming-verdict with foreignness,
25
+ anterior-tolerance assessment, and sibling-aware tolerance-target
26
+ coherence inside the checked predicate), extent-bounded currency scan
27
+ (issuer rule, residues disclosed unconditionally), computational
28
+ hiding/soundness base stated (no trusted setup).
29
+ - `selective_disclosure.rb`: build/parse/verify for field commitments,
30
+ profile, presentation (refuse-not-coerce, canonical-serialization
31
+ equality everywhere); signature verification from the record DIGEST alone
32
+ via the unchanged map-1 §1.1 attestation signature — blinded verdicts
33
+ without record content; conforming-verdict refuses (never degrades) when
34
+ operator credential or rpr-1 §2.1 assessment material is missing;
35
+ currency scan with unestablished-vs-unretracted discipline.
36
+ - `bin/sdp_verify.rb`: offline verifier (commit / binding / presentation /
37
+ profile / currency / convention), exit-code discipline (0/1/2), strict
38
+ intake (delete_suffix, canonical numerals).
39
+ - `test/test_sdp_disclosure.rb`: 61 design-constraint assertions
40
+ (SDP-1..5), including crafted-record shape refusal, verdict-vocabulary
41
+ closure, sibling-target coherence (rpr-1 §2.1 pooling agreement),
42
+ hiding of withheld values across artifacts and error paths, and
43
+ khab/map/rpr non-interference (37/75/76/88 regression green).
44
+
45
+ Implementation review: 3 rounds, R3 4/6 APPROVE + executable-probe
46
+ personas 2/2, zero remaining P0/P1 (dispositions in docs/drafts/).
47
+
48
+ ## [3.46.0] - 2026-07-22
49
+
50
+ ### Synoptis — reproduction endorsement rpr-1 (AUD-L3 slice 1)
51
+
52
+ Implements AUD-L3 design v0.4 (RPR-1..5, review-converged) slice 1 in the
53
+ synoptis SkillSet. All additive: rpr-1 builds on map-1/khab-1 exactly as
54
+ map-1 built on khab-1; no pre-existing file changed.
55
+
56
+ - `conventions/rpr-1.md`: content-addressed reproduction-endorsement
57
+ convention — re-execution target (inputs/environment/pipeline/output
58
+ digests; the first three form the committed computation identification,
59
+ output excluded), target-bound result-free tolerance declaration
60
+ (bit-identity at rpr-1), reproduction endorsement carrying the adjudicated
61
+ verdict (reproduced / not-reproduced) and its adjudication mode
62
+ (hand / procedure), declaration-set assessment (order-independent, sibling
63
+ targets pooled by computation identification, residue disclosed), map-1 §3
64
+ retraction reused unchanged.
65
+ - `reproduction.rb`: build/parse/digest for target, tolerance, endorsement
66
+ (closed schemas, canonical-serialization equality, refuse-not-coerce);
67
+ endorsement signing/verification via the unchanged map-1 §1.1 attestation
68
+ signature (ChainCredential); foreignness conformance check (RPR-4);
69
+ declaration-set assessment reporting invoked commitment (earliest
70
+ anterior), posterior commitments, digest-tiebroken rank, distinct-digest
71
+ multiplicity, and unresolved-equivalence residue.
72
+ - `bin/rpr_verify.rb`: offline auditor verifier (target / tolerance /
73
+ endorsement / assess / convention). Strict intake (at most one trailing
74
+ LF via delete_suffix — CRLF refused), canonical base-10 numerals only,
75
+ exit-code discipline (0 verified/report, 1 rejected, 2 unresolvable),
76
+ RPR-1/RPR-4 condition disclosure in output.
77
+ - `test/test_rpr_reproduction.rb`: 37 design-constraint assertions
78
+ (RPR-1..5), including presentation-order independence of the assessment
79
+ and rank, CRLF/padding refusal, and map-1 non-interference (75/75
80
+ regression green).
81
+
82
+ Implementation review: 3 rounds, R3 5/5 APPROVE, zero remaining P0/P1
83
+ (executable-probe personas; dispositions in docs/drafts/).
84
+
7
85
  ## [3.45.0] - 2026-07-21
8
86
 
9
87
  ### Synoptis — mutual anchoring protocol MAP-1..4 (map-1, AUD-L2)
@@ -1,4 +1,4 @@
1
1
  module KairosMcp
2
- VERSION = "3.45.0"
2
+ VERSION = "3.47.0"
3
3
  CHANGELOG_URL = "https://github.com/masaomi/KairosChain_2026/blob/main/CHANGELOG.md"
4
4
  end
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # rpr_verify — offline auditor verifier for rpr-1 reproduction-endorsement
5
+ # artifacts (RPR-1..5, aud_l3_reproducibility_design v0.4).
6
+ #
7
+ # Verifies with EXACTLY the disclosed trust base: the artifacts named on the
8
+ # command line plus the shipped rpr-1 and map-1 convention definitions. No
9
+ # registry, no network, no operator cooperation. (Authentic chain views are
10
+ # the caller's inputs; their integrity is the khab-1/map-1 surfaces' business.)
11
+ #
12
+ # Usage:
13
+ # rpr_verify.rb target <target.json>
14
+ # rpr_verify.rb tolerance <tolerance.json> [target.json]
15
+ # with target.json: additionally checks the binding matches that target.
16
+ # rpr_verify.rb endorsement <endorsement.json> <endorser_credential.json> <signature-hex> [operator_credential.json]
17
+ # with operator credential: additionally checks foreignness (RPR-4).
18
+ # rpr_verify.rb assess <targets.json> <declarations.json> <invoked_tolerance_sha256> <endorsement_position>
19
+ # targets.json = JSON array of target record STRINGS
20
+ # declarations.json = JSON array of {"tolerance": "<record string>", "position": <int>}
21
+ # rpr_verify.rb convention
22
+ # prints the shipped rpr-1 convention digest.
23
+ #
24
+ # Exit status: 0 = VERIFIED/REPORT, 1 = REJECTED, 2 = usage / unresolvable.
25
+
26
+ require 'json'
27
+ require 'digest'
28
+ require_relative '../lib/synoptis/anchoring/entry'
29
+ require_relative '../lib/synoptis/anchoring/chain_credential'
30
+ require_relative '../lib/synoptis/anchoring/reproduction'
31
+
32
+ CRED = Synoptis::Anchoring::ChainCredential
33
+ REPRO = Synoptis::Anchoring::Reproduction
34
+
35
+ def die(msg)
36
+ warn "rpr_verify: #{msg}"
37
+ exit 2
38
+ end
39
+
40
+ def reject(msg)
41
+ puts "REJECTED: #{msg}"
42
+ exit 1
43
+ end
44
+
45
+ # Read a record artifact: at most one trailing newline is tolerated (file
46
+ # convenience); any other surrounding bytes stay in the string and fail the
47
+ # canonical check downstream (refuse-not-coerce — a padded artifact is not
48
+ # the canonical record).
49
+ def read_file(path)
50
+ # delete_suffix, not chomp: chomp("\n") also swallows "\r\n" and a bare
51
+ # trailing "\r", which would coerce CRLF artifacts into canonical form.
52
+ File.read(path).delete_suffix("\n")
53
+ rescue SystemCallError => e
54
+ die "cannot read #{path}: #{e.message}"
55
+ end
56
+
57
+ def load_json(path)
58
+ JSON.parse(File.read(path))
59
+ rescue JSON::ParserError, SystemCallError => e
60
+ die "cannot read #{path}: #{e.message}"
61
+ end
62
+
63
+ def load_json_hash(path, label)
64
+ parsed = load_json(path)
65
+ die "#{path}: #{label} must be a JSON object, got #{parsed.class}" unless parsed.is_a?(Hash)
66
+ parsed
67
+ end
68
+
69
+ # Canonical base-10 numerals only: leading zeros are refused, not
70
+ # reinterpreted (a non-canonical numeral is a malformed input, exit 2).
71
+ def strict_int(value, label)
72
+ die "#{label} must be a canonical base-10 integer, got #{value.inspect}" unless value.is_a?(String) && value.match?(/\A(0|[1-9]\d*)\z/)
73
+ Integer(value, 10)
74
+ end
75
+
76
+ HEX64 = /\A[a-f0-9]{64}\z/
77
+
78
+ mode = ARGV.shift
79
+
80
+ case mode
81
+ when 'target'
82
+ die 'usage: rpr_verify.rb target <target.json>' unless ARGV.size == 1
83
+ record = read_file(ARGV[0])
84
+ begin
85
+ REPRO.parse_target!(record)
86
+ rescue REPRO::ReproductionError => e
87
+ reject e.message
88
+ end
89
+ puts "VERIFIED: target digest #{REPRO.target_digest(record)}"
90
+ puts " computation identification: #{REPRO.computation_id(record).split('|').map { |h| h[0, 12] }.join('… | ')}… (output excluded, rpr-1 §1)"
91
+ puts 'NOTE: binding fixes referents, never validates them (RPR-2, MPR-4).'
92
+
93
+ when 'tolerance'
94
+ die 'usage: rpr_verify.rb tolerance <tolerance.json> [target.json]' unless (1..2).cover?(ARGV.size)
95
+ record = read_file(ARGV[0])
96
+ tol = begin
97
+ REPRO.parse_tolerance!(record)
98
+ rescue REPRO::ReproductionError => e
99
+ reject e.message
100
+ end
101
+ if ARGV[1]
102
+ target = read_file(ARGV[1])
103
+ begin
104
+ digest = REPRO.target_digest(target)
105
+ rescue REPRO::ReproductionError => e
106
+ reject e.message
107
+ end
108
+ reject "tolerance binds target #{tol['target_sha256'][0, 12]}…, not the supplied target #{digest[0, 12]}…" unless tol['target_sha256'] == digest
109
+ end
110
+ puts "VERIFIED: tolerance digest #{Digest::SHA256.hexdigest(record)} (kind #{tol['kind']}, target #{tol['target_sha256'][0, 12]}…)"
111
+ puts 'NOTE: anteriority to any endorsement is decided by committed order (MPR-8), not by this tool.'
112
+
113
+ when 'endorsement'
114
+ die 'usage: rpr_verify.rb endorsement <endorsement.json> <endorser_credential.json> <signature-hex> [operator_credential.json]' unless (3..4).cover?(ARGV.size)
115
+ record = read_file(ARGV[0])
116
+ cred = load_json_hash(ARGV[1], 'endorser credential')
117
+ ok = begin
118
+ REPRO.verify_endorsement(record, cred, ARGV[2])
119
+ rescue REPRO::ReproductionError, CRED::CredentialError => e
120
+ reject e.message
121
+ end
122
+ reject 'endorsement signature does not verify under the endorser credential' unless ok
123
+ e = REPRO.parse_endorsement!(record)
124
+ if ARGV[3]
125
+ operator = load_json_hash(ARGV[3], 'operator credential')
126
+ begin
127
+ reject 'endorser credential equals operator credential — same-party, not a conforming rpr-1 endorsement (RPR-4)' unless REPRO.foreign?(cred, operator)
128
+ rescue CRED::CredentialError => err
129
+ # An unresolvable operator credential is a caller-input problem, not a
130
+ # verdict about the endorsement (exit-code discipline: 2, not 1).
131
+ die "operator credential unresolvable: #{err.message}"
132
+ end
133
+ end
134
+ puts "VERIFIED: #{e['verdict']} endorsement of target #{e['target_sha256'][0, 12]}… under tolerance #{e['tolerance_sha256'][0, 12]}…"
135
+ puts " adjudication mode: #{e['adjudication_mode']}#{e['procedure_sha256'] ? " (procedure #{e['procedure_sha256'][0, 12]}…)" : ''}"
136
+ puts 'CONDITIONS (travel with every stronger reading, rpr-1 §3):'
137
+ puts ' - the verdict asserts reproduction or its failure, never correctness (RPR-1, MPR-6)'
138
+ puts ' - evidence of a committed claim, never proof a re-execution happened (RPR-4)'
139
+ puts ' - distinctness is not independence; a colluding pair can fabricate (RPR-4)'
140
+ puts ' - the named mode is a declaration, not a proof (map-1 §3 non-self-certifying)'
141
+ if ARGV[3]
142
+ puts ' - foreignness checked against the SUPPLIED operator credential only'
143
+ else
144
+ puts ' - foreignness (RPR-4 conformance condition) NOT assessed — no operator credential supplied'
145
+ end
146
+
147
+ when 'assess'
148
+ die 'usage: rpr_verify.rb assess <targets.json> <declarations.json> <invoked_tolerance_sha256> <endorsement_position>' unless ARGV.size == 4
149
+ targets = load_json(ARGV[0])
150
+ die "#{ARGV[0]}: expected a JSON array of target record strings" unless targets.is_a?(Array) && targets.all? { |t| t.is_a?(String) }
151
+ declarations = load_json(ARGV[1])
152
+ die "#{ARGV[1]}: expected a JSON array of {tolerance, position} objects" unless declarations.is_a?(Array)
153
+ invoked = ARGV[2]
154
+ die 'invoked_tolerance_sha256 must be 64-char lowercase hex' unless invoked.match?(HEX64)
155
+ position = strict_int(ARGV[3], 'endorsement_position')
156
+ begin
157
+ report = REPRO.assess_declarations(targets: targets, declarations: declarations,
158
+ invoked_tolerance_sha256: invoked, endorsement_position: position)
159
+ rescue REPRO::ReproductionError => e
160
+ reject e.message
161
+ end
162
+ puts JSON.pretty_generate(report)
163
+ puts 'NOTE: the assessment reports, the reader prices (RPR-3: exposure, not prevention).'
164
+ puts 'NOTE: a produced report always exits 0; the conformance bit is invoked_conforming in the report.'
165
+
166
+ when 'convention'
167
+ puts "rpr-1 convention sha256: #{REPRO.convention_sha256}"
168
+
169
+ else
170
+ die 'usage: rpr_verify.rb target|tolerance|endorsement|assess|convention ... (see header)'
171
+ end
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # sdp_verify — offline verifier for sdp-1 selective-disclosure artifacts
5
+ # (SDP-1..5, aud_l4_selective_disclosure_design v0.3).
6
+ #
7
+ # Verifies with EXACTLY the disclosed trust base: the artifacts named on the
8
+ # command line plus the shipped sdp-1, rpr-1, and map-1 convention
9
+ # definitions. No registry, no network, no producer cooperation. (Authentic
10
+ # chain/log views are the caller's inputs; their integrity is the
11
+ # khab-1/map-1 surfaces' business.)
12
+ #
13
+ # Usage:
14
+ # sdp_verify.rb commit <record.json> [salts.json]
15
+ # builds the sdp-1 field-commitments auxiliary for a canonical record;
16
+ # prints the auxiliary record and (fresh or supplied) salts as JSON.
17
+ # sdp_verify.rb binding <aux.json> <record.json> <salts.json>
18
+ # checks the SDP-2 binding: every field digest and the record digest
19
+ # recompute from record + salts.
20
+ # sdp_verify.rb presentation <presentation.json> [operator_credential.json] [assessment.json]
21
+ # verifies a presentation. conforming-verdict REQUIRES both extras;
22
+ # assessment.json = {"targets":[...record strings...],
23
+ # "declarations":[{"tolerance":"...","position":N}...],
24
+ # "endorsement_position":N}.
25
+ # sdp_verify.rb profile <presentation.json>
26
+ # prints the disclosure profile and what it does/does not show.
27
+ # sdp_verify.rb currency <entries.json> <carrier_entry_hash> <extent>
28
+ # runs the sdp-1 §5 retraction scan over a supplied entry view.
29
+ # sdp_verify.rb convention
30
+ # prints the shipped sdp-1 convention digest.
31
+ #
32
+ # Exit status: 0 = VERIFIED/REPORT, 1 = REJECTED, 2 = usage / unresolvable.
33
+
34
+ require 'json'
35
+ require 'digest'
36
+ require_relative '../lib/synoptis/anchoring/entry'
37
+ require_relative '../lib/synoptis/anchoring/chain_credential'
38
+ require_relative '../lib/synoptis/anchoring/reproduction'
39
+ require_relative '../lib/synoptis/anchoring/selective_disclosure'
40
+
41
+ SD = Synoptis::Anchoring::SelectiveDisclosure
42
+ CRED = Synoptis::Anchoring::ChainCredential
43
+
44
+ def die(msg)
45
+ warn "sdp_verify: #{msg}"
46
+ exit 2
47
+ end
48
+
49
+ def reject(msg)
50
+ puts "REJECTED: #{msg}"
51
+ exit 1
52
+ end
53
+
54
+ # Read a record artifact: at most one trailing newline is tolerated (file
55
+ # convenience); any other surrounding bytes stay in the string and fail the
56
+ # canonical check downstream (refuse-not-coerce). delete_suffix, not chomp:
57
+ # chomp also swallows CRLF, which would coerce a non-canonical artifact.
58
+ def read_file(path)
59
+ File.read(path).delete_suffix("\n")
60
+ rescue SystemCallError => e
61
+ die "cannot read #{path}: #{e.message}"
62
+ end
63
+
64
+ def load_json(path, label)
65
+ parsed = JSON.parse(File.read(path))
66
+ die "#{path}: #{label} must be a JSON object or array, got #{parsed.class}" unless parsed.is_a?(Hash) || parsed.is_a?(Array)
67
+ parsed
68
+ rescue JSON::ParserError, SystemCallError => e
69
+ die "cannot read #{path}: #{e.message}"
70
+ end
71
+
72
+ def load_json_hash(path, label)
73
+ parsed = load_json(path, label)
74
+ die "#{path}: #{label} must be a JSON object, got #{parsed.class}" unless parsed.is_a?(Hash)
75
+ parsed
76
+ end
77
+
78
+ # Canonical base-10 numerals only: leading zeros are refused, not
79
+ # reinterpreted (a non-canonical numeral is a malformed input, exit 2).
80
+ def strict_int(value, label)
81
+ die "#{label} must be a canonical base-10 integer, got #{value.inspect}" unless value.is_a?(String) && value.match?(/\A(0|[1-9]\d*)\z/)
82
+ Integer(value, 10)
83
+ end
84
+
85
+ HEX64 = /\A[a-f0-9]{64}\z/
86
+
87
+ mode = ARGV.shift
88
+
89
+ case mode
90
+ when 'commit'
91
+ die 'usage: sdp_verify.rb commit <record.json> [salts.json]' unless (1..2).cover?(ARGV.size)
92
+ record = read_file(ARGV[0])
93
+ salts = ARGV[1] ? load_json_hash(ARGV[1], 'salts') : nil
94
+ built = begin
95
+ SD.build_field_commitments(record, salts: salts)
96
+ rescue SD::DisclosureError => e
97
+ reject e.message
98
+ end
99
+ puts JSON.pretty_generate(built)
100
+ warn 'NOTE: keep the salts private; each opened salt discloses exactly its field (sdp-1 §6).'
101
+
102
+ when 'binding'
103
+ die 'usage: sdp_verify.rb binding <aux.json> <record.json> <salts.json>' unless ARGV.size == 3
104
+ aux = read_file(ARGV[0])
105
+ record = read_file(ARGV[1])
106
+ salts = load_json_hash(ARGV[2], 'salts')
107
+ ok = begin
108
+ SD.verify_field_commitments(aux, record, salts)
109
+ rescue SD::DisclosureError => e
110
+ reject e.message
111
+ end
112
+ reject 'auxiliary is NOT checkably bound to the record (SDP-2: a bound-by-assertion auxiliary is a re-authoring)' unless ok
113
+ puts "VERIFIED: auxiliary is checkably bound to record #{Digest::SHA256.hexdigest(record)[0, 12]}… (total coverage, SDP-2)"
114
+
115
+ when 'presentation'
116
+ die 'usage: sdp_verify.rb presentation <presentation.json> [operator_credential.json] [assessment.json]' unless (1..3).cover?(ARGV.size)
117
+ pres = read_file(ARGV[0])
118
+ operator = ARGV[1] ? load_json_hash(ARGV[1], 'operator credential') : nil
119
+ assessment = ARGV[2] ? load_json_hash(ARGV[2], 'assessment material') : nil
120
+ report = begin
121
+ SD.verify_presentation(pres, operator_credential: operator, assessment: assessment)
122
+ rescue SD::DisclosureError, CRED::CredentialError => e
123
+ reject e.message
124
+ end
125
+ unless report[:valid]
126
+ puts "REJECTED: presentation does not verify (predicate #{report[:predicate]})"
127
+ report[:failures].each { |f| puts " - #{f}" }
128
+ exit 1
129
+ end
130
+ puts "VERIFIED: #{report[:predicate]} over committed record #{report[:record_sha256][0, 12]}…"
131
+ report[:opened].each { |n, v| puts " opened #{n} = #{v.inspect}" }
132
+ report[:notes].each { |n| puts " NOTE: #{n}" }
133
+ puts 'NOTE: soundness = sha256 collision resistance + Ed25519, no setup; hiding is computational, content-only (SDP-5, sdp-1 §6).'
134
+
135
+ when 'profile'
136
+ die 'usage: sdp_verify.rb profile <presentation.json>' unless ARGV.size == 1
137
+ pres = read_file(ARGV[0])
138
+ p = begin
139
+ SD.parse_presentation!(pres)
140
+ rescue SD::DisclosureError => e
141
+ reject e.message
142
+ end
143
+ profile = p['profile']
144
+ puts "profile (meaning fixed by sdp-1 §2, never producer gloss — SDP-4):"
145
+ puts " predicate: #{profile['predicate']}"
146
+ puts " opened: #{profile['opened'].join(', ')}"
147
+ puts " withheld: every other field of the referenced record (values only; names and count are public)"
148
+ puts " currency: #{profile['currency']}"
149
+ puts ' shows: existence and the opened values of ONE committed record'
150
+ puts ' never: non-existence, uniqueness, sibling records, contrary verdicts, producer selection (SDP-3)'
151
+
152
+ when 'currency'
153
+ die 'usage: sdp_verify.rb currency <entries.json> <carrier_entry_hash> <extent>' unless ARGV.size == 3
154
+ entries = load_json(ARGV[0], 'entries')
155
+ die "#{ARGV[0]}: entries must be a JSON array" unless entries.is_a?(Array)
156
+ carrier = ARGV[1].to_s
157
+ die 'carrier_entry_hash must be 64-char lowercase hex' unless carrier.match?(HEX64)
158
+ extent = strict_int(ARGV[2], 'extent')
159
+ scan = begin
160
+ SD.scan_currency(entries: entries, carrier_entry_hash: carrier, extent: extent)
161
+ rescue SD::DisclosureError => e
162
+ reject e.message
163
+ end
164
+ puts "REPORT: #{scan[:status]} (scanned extent #{scan[:scanned_extent]})"
165
+ puts " hits (positions): #{scan[:hits].join(', ')}" if scan[:hits]&.any?
166
+ puts " NOTE: #{scan[:note]}"
167
+ exit(scan[:status] == 'retracted' ? 1 : 0)
168
+
169
+ when 'convention'
170
+ puts "sdp-1 convention sha256: #{SD.convention_sha256}"
171
+
172
+ else
173
+ die 'usage: sdp_verify.rb commit|binding|presentation|profile|currency|convention …'
174
+ end
@@ -0,0 +1,141 @@
1
+ # rpr-1 — KairosChain Reproduction Endorsement Convention, version 1
2
+
3
+ Status: committed verification convention (RPR-1..5 of
4
+ aud_l3_reproducibility_design v0.4). The SHA-256 over this file's raw bytes is
5
+ the convention integrity digest; every artifact that names `rpr-1` commits it
6
+ as its convention reference. A verifier holding a definition whose digest does
7
+ not match the committed one MUST treat the artifact's convention as
8
+ unresolvable and refuse to verify under it. Any change to this convention is a
9
+ new identifier (`rpr-2`, ...); this file is never edited in place.
10
+ rpr-1 builds on map-1 (credentials, attestation signatures, attestation types,
11
+ retraction) and khab-1 (committed identity, committed order) and modifies
12
+ neither: a map-1 artifact is valid with or without any rpr-1 artifact present.
13
+
14
+ All record literals below are canonical JSON (keys sorted recursively,
15
+ `JSON.generate` compact form). A record is valid only in its canonical
16
+ serialization with exactly the fields shown — a non-canonical or extra-field
17
+ record is not an rpr-1 artifact and is ignored as noise (map-1 §4 precedent).
18
+
19
+ ## 1. Re-execution target (RPR-2)
20
+
21
+ {"environment_sha256":"<64-hex>","format":"rpr-1/target",
22
+ "input_sha256":"<64-hex>","output_sha256":"<64-hex>",
23
+ "pipeline_sha256":"<64-hex>"}
24
+
25
+ The target commits both halves of the verdict's referent: what is re-run
26
+ (inputs, environment, pipeline version) and what the re-run output is judged
27
+ against (the committed output). The first three digests — `input_sha256`,
28
+ `environment_sha256`, `pipeline_sha256` — constitute the target's **committed
29
+ computation identification** (design §3(b)): the identification of what is
30
+ re-run, which two targets can share while naming different committed outputs.
31
+ `output_sha256` completes the target but does not participate in that
32
+ identification. The target digest is the SHA-256 of the target's canonical
33
+ JSON. What the digests are digests OF (file formats, environment pinning,
34
+ pipeline identity) is the referenced material's business; rpr-1 commits
35
+ references, it does not validate referents (MPR-4 asymmetry, RPR-2 limit).
36
+
37
+ ## 2. Tolerance declaration (RPR-3)
38
+
39
+ {"format":"rpr-1/tolerance","kind":"bit-identity","target_sha256":"<64-hex>"}
40
+
41
+ A tolerance declaration is bound to a named target and is result-free by
42
+ construction: the schema is closed and `kind` is the only expressive field, so
43
+ no outcome-referencing content can be expressed. At rpr-1 the only kind is
44
+ `bit-identity` (re-run output matches the committed output byte for byte);
45
+ richer tolerance grammars (numerical bounds, domain equivalence) are a new
46
+ convention — this narrowness is disclosed, not hidden. The declaration is
47
+ committed as an internal-chain record ahead of the endorsements it governs;
48
+ committed position provides decidable anteriority (MPR-8). One declaration has
49
+ exactly one digest (canonical serialization).
50
+
51
+ ### 2.1 Declaration-set assessment (RPR-3)
52
+
53
+ Conformance of an endorsement's tolerance is assessed against the record, not
54
+ the endorser's selection. Given target records, tolerance declarations with
55
+ their committed positions, and the endorsement's committed position, the
56
+ assessment reports:
57
+
58
+ - every anterior declaration bound to the endorsement's target;
59
+ - every anterior declaration bound to any sibling target sharing the same
60
+ committed computation identification (§1) — a menu spread across
61
+ near-duplicate targets is the same menu and is read as one set (at rpr-1,
62
+ sharing = equality of the three computation-identification digests);
63
+ - the multiplicity of that pooled set and the invoked declaration's place in
64
+ it;
65
+ - the unresolved residue: declarations whose `target_sha256` resolves to no
66
+ supplied target record (sharing undecidable there), disclosed rather than
67
+ silently dropped.
68
+
69
+ The assessment reports; the reader prices. A verdict judged under one of many
70
+ anterior declarations is conforming and wears the multiplicity openly; a
71
+ tolerance chosen after the result is non-conforming (its posteriority is
72
+ decidable from committed order). The assessment is a function of the committed
73
+ record alone, never of presentation order: a declaration committed at several
74
+ positions is one declaration (one digest) with several commitments; the
75
+ invoked declaration conforms iff any of its commitments is anterior,
76
+ represented by the earliest, with posterior commitments disclosed alongside;
77
+ multiplicity counts distinct declarations, and the invoked declaration's
78
+ place is reported as its rank in the pooled anterior set ordered by earliest
79
+ anterior position, with equal positions ordered by declaration digest — so
80
+ every reported quantity, ranks included, is decidable from the committed
81
+ record alone.
82
+
83
+ ## 3. Reproduction endorsement (RPR-1, RPR-4)
84
+
85
+ hand-adjudicated:
86
+
87
+ {"adjudication_mode":"hand","format":"rpr-1/endorsement",
88
+ "target_sha256":"<64-hex>","tolerance_sha256":"<64-hex>",
89
+ "verdict":"reproduced"}
90
+
91
+ procedure-adjudicated:
92
+
93
+ {"adjudication_mode":"procedure","format":"rpr-1/endorsement",
94
+ "procedure_sha256":"<64-hex>","target_sha256":"<64-hex>",
95
+ "tolerance_sha256":"<64-hex>","verdict":"not-reproduced"}
96
+
97
+ `verdict` is `reproduced` or `not-reproduced`: the affirmative and the
98
+ negative verdict are the same kind of record under the same bindings, so the
99
+ record has a conforming place for failure as well as success (RPR-1).
100
+ `adjudication_mode` names the gate behind the verdict (RPR-4): `hand`, or
101
+ `procedure` with `procedure_sha256` naming the committed adjudication
102
+ procedure whose anterior adoption is the judgment act. `procedure_sha256` is
103
+ present exactly when the mode is `procedure` (closed schema per mode). The
104
+ named mode, like the attestation type itself, is a declaration and not a
105
+ proof (map-1 §3 non-self-certifying).
106
+
107
+ The endorsement is signed under the endorser's map-1 credential using the
108
+ map-1 §1.1 attestation signature, with the payload being the endorsement
109
+ record string. Verification needs only the credential, the record, and the
110
+ signature. The endorsement is carried with `attestation_type` =
111
+ `quality-endorsement` (map-1 §3, unchanged).
112
+
113
+ Foreignness (RPR-4) is a conformance condition: an endorsement whose issuer
114
+ credential digest equals the operator credential digest for the target's
115
+ chain is not a conforming rpr-1 endorsement, whatever else it may be as an
116
+ ordinary same-party claim. Distinctness is not independence: a colluding pair
117
+ can fabricate an endorsement, affirmative or negative, without any run having
118
+ occurred — the endorsement is evidence of a committed claim, never proof that
119
+ a re-execution happened (RPR-4 disclosed limit).
120
+
121
+ What a verified endorsement asserts is exactly: this endorser claimed, at a
122
+ committed moment, that the target was re-run and its output did or did not
123
+ match the target's committed output within the named tolerance — and nothing
124
+ else. No claim of correctness, validity, fitness, or significance (RPR-1,
125
+ MPR-6); absence of any endorsement proves nothing (MPR-9 non-production).
126
+
127
+ ## 4. Retraction (RPR-5)
128
+
129
+ A mistaken endorsement is withdrawn by the map-1 §3 `retraction` unchanged:
130
+ same issuer, unambiguous target, appended. A retracted endorsement still
131
+ stands as a committed record that the verdict was once claimed and later
132
+ taken back; nothing is unsaid by deletion and no anchored commitment's
133
+ coverage is subtracted (AHM-4).
134
+
135
+ ## 5. Verification procedure and trust base
136
+
137
+ All rpr-1 verification needs only: the artifacts named above, this
138
+ definition, the map-1 definition (for credentials, signatures, types, and
139
+ retraction), and authentic views of the logs/chains the artifacts reference.
140
+ No registry, no network, no operator cooperation for verification —
141
+ production of artifacts remains the endorser's act (MPR-4 asymmetry).