kairos-chain 3.57.0 → 3.58.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 +4 -4
- data/lib/kairos_mcp/version.rb +1 -1
- data/templates/skillsets/llm_client/config/llm_client.yml +3 -6
- data/templates/skillsets/synoptis/lib/synoptis/proof_envelope.rb +27 -30
- data/templates/skillsets/synoptis/lib/synoptis/tool_helpers.rb +30 -7
- data/templates/skillsets/synoptis/lib/synoptis/verifier.rb +15 -29
- data/templates/skillsets/synoptis/test/test_agent_identity.rb +129 -0
- data/templates/skillsets/synoptis/tools/attestation_verify.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d24490106b19ca11e1e04e37a6d406ff8a191563957bb7c029fecf191593237
|
|
4
|
+
data.tar.gz: 0bcf20859249713fdafe723477a1fda62da79061b09bd4e7d76efcc5d78f0af2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d438c2e198ac55990b65a7e124878de1fda2fce6312bd5bfbad0256795b9990addd4ba9b44d3d5b81b572990351f726a3cea7e8ca1e8b6e1a76e6b74f749adc8
|
|
7
|
+
data.tar.gz: 10184876b0611bbb2590a36c5a7a36a9e3f9e4fc1c1ad3aab43c7df3bc53961516feb9dbd2900a649239ee02b4f942d973148cabbccb8d3010d041fc710918b4
|
data/lib/kairos_mcp/version.rb
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# local — Local model via OpenAI-compatible API (Ollama, vLLM)
|
|
11
11
|
|
|
12
12
|
provider: claude_code
|
|
13
|
-
model: claude-opus-
|
|
13
|
+
model: claude-opus-4-6
|
|
14
14
|
# api_key_env not needed for claude_code provider — uses Claude Code subscription
|
|
15
15
|
default_max_tokens: 4096
|
|
16
16
|
default_temperature: 0.7
|
|
@@ -41,11 +41,8 @@ timeout_seconds: 600
|
|
|
41
41
|
|
|
42
42
|
# Cost estimates (USD per 1M tokens, for llm_status display)
|
|
43
43
|
cost_estimates:
|
|
44
|
-
claude-opus-5: { input: 5.0, output: 25.0 }
|
|
45
|
-
claude-fable-5: { input: 10.0, output: 50.0 }
|
|
46
|
-
claude-opus-4-6: { input: 5.0, output: 25.0 }
|
|
47
|
-
claude-sonnet-5: { input: 3.0, output: 15.0 }
|
|
48
44
|
claude-sonnet-4-6: { input: 3.0, output: 15.0 }
|
|
49
|
-
claude-haiku-4-5: { input:
|
|
45
|
+
claude-haiku-4-5: { input: 0.80, output: 4.0 }
|
|
46
|
+
claude-opus-4-6: { input: 15.0, output: 75.0 }
|
|
50
47
|
gpt-4o: { input: 2.50, output: 10.0 }
|
|
51
48
|
gpt-4o-mini: { input: 0.15, output: 0.60 }
|
|
@@ -9,30 +9,34 @@ module Synoptis
|
|
|
9
9
|
class ProofEnvelope
|
|
10
10
|
PROOF_VERSION = '1.0.0'
|
|
11
11
|
|
|
12
|
-
# 1.1.0 adds
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# signed under 1.0.0 keeps verifying against the same
|
|
16
|
-
# over — adding fields unconditionally would have
|
|
17
|
-
# signature.
|
|
12
|
+
# 1.1.0 adds one identity field to the canonical form: submitter_pubkey, the
|
|
13
|
+
# party that handed over the content, as distinct from attester_id, which
|
|
14
|
+
# names the anchor that recorded it. The version selects the canonical field
|
|
15
|
+
# set, so an envelope signed under 1.0.0 keeps verifying against the same
|
|
16
|
+
# bytes it was signed over — adding fields unconditionally would have
|
|
17
|
+
# invalidated every existing signature.
|
|
18
|
+
#
|
|
19
|
+
# It deliberately does NOT carry attester_pubkey. An earlier revision put the
|
|
20
|
+
# signing key in the canonical form so a reader could verify from the record
|
|
21
|
+
# alone; the version field is itself a field of the record, so a forger could
|
|
22
|
+
# write version 1.1.0, name any attester, sign with their own key, record that
|
|
23
|
+
# key alongside, and obtain valid: true from a key-less verification. Ruling of
|
|
24
|
+
# 2026-08-02 (option A2): the key is never taken from the record. Verification
|
|
25
|
+
# needs a key supplied by the caller, which is where authenticity has to come
|
|
26
|
+
# from anyway — binding a key to a named attester requires a trusted key list
|
|
27
|
+
# this layer does not have.
|
|
18
28
|
PROOF_VERSION_WITH_IDENTITY = '1.1.0'
|
|
19
29
|
VERSIONS_CARRYING_IDENTITY = [PROOF_VERSION_WITH_IDENTITY].freeze
|
|
20
30
|
|
|
21
31
|
attr_reader :proof_id, :version, :attester_id, :subject_ref, :claim,
|
|
22
32
|
:evidence, :merkle_root, :signature, :timestamp, :ttl,
|
|
23
|
-
:actor_user_id, :actor_role, :metadata, :submitter_pubkey
|
|
24
|
-
:attester_pubkey
|
|
33
|
+
:actor_user_id, :actor_role, :metadata, :submitter_pubkey
|
|
25
34
|
|
|
26
35
|
def initialize(attrs = {})
|
|
27
36
|
attrs = attrs.transform_keys(&:to_sym) if attrs.is_a?(Hash)
|
|
28
37
|
@proof_id = attrs[:proof_id] || SecureRandom.uuid
|
|
29
38
|
@submitter_pubkey = attrs[:submitter_pubkey]
|
|
30
|
-
@
|
|
31
|
-
# Either identity field selects the version. Keying off one of them let a
|
|
32
|
-
# caller hand over the attester key in good faith and get an envelope that
|
|
33
|
-
# stores it outside the signature.
|
|
34
|
-
@version = attrs[:version] ||
|
|
35
|
-
((@submitter_pubkey || @attester_pubkey) ? PROOF_VERSION_WITH_IDENTITY : PROOF_VERSION)
|
|
39
|
+
@version = attrs[:version] || (@submitter_pubkey ? PROOF_VERSION_WITH_IDENTITY : PROOF_VERSION)
|
|
36
40
|
@attester_id = attrs[:attester_id]
|
|
37
41
|
@subject_ref = attrs[:subject_ref]
|
|
38
42
|
@claim = attrs[:claim]
|
|
@@ -61,8 +65,7 @@ module Synoptis
|
|
|
61
65
|
actor_user_id: @actor_user_id,
|
|
62
66
|
actor_role: @actor_role,
|
|
63
67
|
metadata: @metadata,
|
|
64
|
-
submitter_pubkey: @submitter_pubkey
|
|
65
|
-
attester_pubkey: @attester_pubkey
|
|
68
|
+
submitter_pubkey: @submitter_pubkey
|
|
66
69
|
}
|
|
67
70
|
end
|
|
68
71
|
|
|
@@ -87,10 +90,7 @@ module Synoptis
|
|
|
87
90
|
timestamp: @timestamp,
|
|
88
91
|
ttl: @ttl
|
|
89
92
|
}
|
|
90
|
-
if carries_identity_fields?
|
|
91
|
-
canonical[:attester_pubkey] = @attester_pubkey
|
|
92
|
-
canonical[:submitter_pubkey] = @submitter_pubkey
|
|
93
|
-
end
|
|
93
|
+
canonical[:submitter_pubkey] = @submitter_pubkey if carries_identity_fields?
|
|
94
94
|
JSON.generate(canonical, sort_keys: true)
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -102,9 +102,8 @@ module Synoptis
|
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
# An identity field present at a version whose canonical form does not carry
|
|
105
|
-
# it is a value sitting outside the signature
|
|
106
|
-
#
|
|
107
|
-
# the field the Verifier reaches for when no key is supplied — unguarded.
|
|
105
|
+
# it is a value sitting outside the signature — displayed as if it were part
|
|
106
|
+
# of the record while nothing binds it to the signed bytes.
|
|
108
107
|
#
|
|
109
108
|
# Deserialisation must not raise on it, because from_h is the read path and
|
|
110
109
|
# a stored or third-party record of this shape would otherwise take down
|
|
@@ -113,7 +112,7 @@ module Synoptis
|
|
|
113
112
|
def identity_outside_signature?
|
|
114
113
|
return false if carries_identity_fields?
|
|
115
114
|
|
|
116
|
-
|
|
115
|
+
!@submitter_pubkey.nil?
|
|
117
116
|
end
|
|
118
117
|
|
|
119
118
|
def content_hash
|
|
@@ -130,15 +129,13 @@ module Synoptis
|
|
|
130
129
|
|
|
131
130
|
# Accepts an MMP::Crypto instance (which holds the private key internally).
|
|
132
131
|
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
# needs a trusted key list, which this layer does not have.
|
|
132
|
+
# Signing does not record the signing key. A key recorded by the signer is a
|
|
133
|
+
# self-report: it tells a reader which key to check against only if the reader
|
|
134
|
+
# already trusts whoever wrote the record, which is the thing being checked.
|
|
135
|
+
# The verifying key travels out of band (see PROOF_VERSION_WITH_IDENTITY).
|
|
138
136
|
def sign!(crypto)
|
|
139
137
|
return unless crypto
|
|
140
138
|
|
|
141
|
-
@attester_pubkey = crypto.export_public_key if carries_identity_fields?
|
|
142
139
|
@signature = crypto.sign(canonical_json)
|
|
143
140
|
end
|
|
144
141
|
|
|
@@ -4,7 +4,14 @@ require 'yaml'
|
|
|
4
4
|
require 'fileutils'
|
|
5
5
|
|
|
6
6
|
module Synoptis
|
|
7
|
+
# Raised when this instance cannot name the agent performing a recorded action.
|
|
8
|
+
# A distinct class so callers can tell "we do not know who this is" apart from
|
|
9
|
+
# the MMP loading failures that produce it; see ToolHelpers#resolve_agent_id.
|
|
10
|
+
class UnattributableAgentError < StandardError; end
|
|
11
|
+
|
|
7
12
|
module ToolHelpers
|
|
13
|
+
UNATTRIBUTABLE_MESSAGE =
|
|
14
|
+
'agent identity unavailable; refusing to record an action that could not be attributed'
|
|
8
15
|
def synoptis_data_dir
|
|
9
16
|
dir = File.join(KairosMcp.data_dir, 'synoptis_data')
|
|
10
17
|
FileUtils.mkdir_p(dir)
|
|
@@ -80,14 +87,30 @@ module Synoptis
|
|
|
80
87
|
# instance_id, not introduce: both yield the same identifier, but introduce
|
|
81
88
|
# also globs skills, scans skillsets and signs, so it raises for reasons
|
|
82
89
|
# unrelated to identity (a partially loaded MMP raises NameError on
|
|
83
|
-
# MMP::VERSION).
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
90
|
+
# MMP::VERSION).
|
|
91
|
+
#
|
|
92
|
+
# Raises rather than returning a placeholder. Two placeholders were tried and
|
|
93
|
+
# both collapsed identity: 'unknown' is non-empty, so every attributability
|
|
94
|
+
# check passed and every anchor in that state shared one attester id; nil then
|
|
95
|
+
# became '' at the comparison sites, and '' matches more records than 'unknown'
|
|
96
|
+
# did — RevocationManager authorises on revoker_id == attester_id, and
|
|
97
|
+
# ChallengeManager compares against envelope&.attester_id.to_s, which is ''
|
|
98
|
+
# for a proof that was not found (measured, 2026-08-02).
|
|
99
|
+
#
|
|
100
|
+
# The comparison sites are five and growing; a guard at each is a guard that a
|
|
101
|
+
# sixth caller will not have. Raising makes a blank identity unrepresentable at
|
|
102
|
+
# the one place it is produced. Callers do not rescue this: an operation that
|
|
103
|
+
# cannot name its actor must not proceed, and Protocol#handle already turns the
|
|
104
|
+
# exception into an error response carrying this message.
|
|
87
105
|
def resolve_agent_id
|
|
88
|
-
mmp_identity.instance_id
|
|
89
|
-
|
|
90
|
-
|
|
106
|
+
id = mmp_identity.instance_id
|
|
107
|
+
raise UnattributableAgentError, UNATTRIBUTABLE_MESSAGE if id.nil? || id.to_s.strip.empty?
|
|
108
|
+
|
|
109
|
+
id
|
|
110
|
+
rescue UnattributableAgentError
|
|
111
|
+
raise
|
|
112
|
+
rescue StandardError => e
|
|
113
|
+
raise UnattributableAgentError, "#{UNATTRIBUTABLE_MESSAGE} (#{e.class}: #{e.message})"
|
|
91
114
|
end
|
|
92
115
|
|
|
93
116
|
def resolve_actor_user_id
|
|
@@ -18,27 +18,24 @@ module Synoptis
|
|
|
18
18
|
errors << 'identity_outside_signature' if envelope.identity_outside_signature?
|
|
19
19
|
|
|
20
20
|
if envelope.signature
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
|
|
21
|
+
# The verifying key comes from the caller and only from the caller.
|
|
22
|
+
# Reading it out of the envelope would make the record self-certifying:
|
|
23
|
+
# the version field selects the canonical field set and is itself a field
|
|
24
|
+
# of the record, so a forger could label a record 1.1.0, name any
|
|
25
|
+
# attester, sign with their own key, record that key alongside, and have
|
|
26
|
+
# a key-less verification return valid: true (measured, 2026-08-02).
|
|
27
|
+
# Ruling of 2026-08-02 (option A2): no key is ever taken from the record.
|
|
28
|
+
#
|
|
29
|
+
# An empty string is treated as "no key supplied" rather than passed
|
|
30
|
+
# through: MCP clients routinely send '' for an omitted optional string,
|
|
31
|
+
# and handing that to the crypto layer would report invalid_signature —
|
|
32
|
+
# a claim about the signature, when nothing about the signature is known.
|
|
31
33
|
supplied = public_key.to_s.strip.empty? ? nil : public_key
|
|
32
34
|
|
|
33
|
-
if supplied
|
|
34
|
-
errors << '
|
|
35
|
+
if supplied
|
|
36
|
+
errors << 'invalid_signature' unless verify_signature(envelope, supplied)
|
|
35
37
|
else
|
|
36
|
-
|
|
37
|
-
if key
|
|
38
|
-
errors << 'invalid_signature' unless verify_signature(envelope, key)
|
|
39
|
-
else
|
|
40
|
-
errors << 'no_public_key_for_verification'
|
|
41
|
-
end
|
|
38
|
+
errors << 'no_public_key_for_verification'
|
|
42
39
|
end
|
|
43
40
|
elsif @require_signature
|
|
44
41
|
errors << 'missing_signature'
|
|
@@ -54,17 +51,6 @@ module Synoptis
|
|
|
54
51
|
|
|
55
52
|
private
|
|
56
53
|
|
|
57
|
-
# PEM text can differ in line endings and trailing whitespace while naming
|
|
58
|
-
# the same key, so compare the parsed keys rather than the strings.
|
|
59
|
-
def keys_equal?(one, other)
|
|
60
|
-
return one.to_s.strip == other.to_s.strip unless defined?(::MMP::Crypto)
|
|
61
|
-
|
|
62
|
-
crypto = MMP::Crypto.new(auto_generate: false)
|
|
63
|
-
crypto.import_public_key(one.to_s).to_pem == crypto.import_public_key(other.to_s).to_pem
|
|
64
|
-
rescue StandardError
|
|
65
|
-
one.to_s.strip == other.to_s.strip
|
|
66
|
-
end
|
|
67
|
-
|
|
68
54
|
def verify_signature(envelope, public_key)
|
|
69
55
|
return false unless defined?(::MMP::Crypto)
|
|
70
56
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ToolHelpers#resolve_agent_id — the one place a recorded action's actor is named.
|
|
4
|
+
#
|
|
5
|
+
# R4 (2026-08-01) found this method had no test at all: reverting it left every
|
|
6
|
+
# suite green. Two placeholder returns had already been tried and both collapsed
|
|
7
|
+
# identity, so the behaviour under test here is that there is no placeholder.
|
|
8
|
+
#
|
|
9
|
+
# 'unknown' non-empty, so every attributability check passed and all anchors
|
|
10
|
+
# in that state shared one attester id
|
|
11
|
+
# nil became '' at the comparison sites, and '' matches more records
|
|
12
|
+
# than 'unknown' did (a proof that is not found yields nil.to_s)
|
|
13
|
+
#
|
|
14
|
+
# Ruling of 2026-08-02: raise instead, so a blank identity is unrepresentable at
|
|
15
|
+
# the point of production rather than guarded at each of five comparison sites.
|
|
16
|
+
|
|
17
|
+
project_root = File.expand_path('../../../..', __dir__)
|
|
18
|
+
mmp_lib = File.join(project_root, 'KairosChain_mcp_server', 'templates', 'skillsets', 'mmp', 'lib')
|
|
19
|
+
$LOAD_PATH.unshift(mmp_lib) unless $LOAD_PATH.include?(mmp_lib)
|
|
20
|
+
synoptis_lib = File.expand_path('../lib', __dir__)
|
|
21
|
+
$LOAD_PATH.unshift(synoptis_lib) unless $LOAD_PATH.include?(synoptis_lib)
|
|
22
|
+
|
|
23
|
+
require 'synoptis/tool_helpers'
|
|
24
|
+
|
|
25
|
+
$pass = 0
|
|
26
|
+
$fail = 0
|
|
27
|
+
|
|
28
|
+
def assert(condition, message)
|
|
29
|
+
if condition
|
|
30
|
+
$pass += 1
|
|
31
|
+
puts " PASS: #{message}"
|
|
32
|
+
else
|
|
33
|
+
$fail += 1
|
|
34
|
+
puts " FAIL: #{message}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def section(title)
|
|
39
|
+
puts "\n#{'=' * 60}"
|
|
40
|
+
puts "SECTION: #{title}"
|
|
41
|
+
puts '=' * 60
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A host that yields whatever identity the case under test needs. Only
|
|
45
|
+
# mmp_identity is stubbed; resolve_agent_id itself is the shipped method.
|
|
46
|
+
class IdentityHost
|
|
47
|
+
include Synoptis::ToolHelpers
|
|
48
|
+
|
|
49
|
+
def initialize(&block)
|
|
50
|
+
@block = block
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def mmp_identity
|
|
54
|
+
@block.call
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def resolve(&block)
|
|
59
|
+
IdentityHost.new(&block).resolve_agent_id
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def raises_unattributable?(&block)
|
|
63
|
+
resolve(&block)
|
|
64
|
+
false
|
|
65
|
+
rescue Synoptis::UnattributableAgentError
|
|
66
|
+
true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# ============================================================
|
|
70
|
+
section '1. A resolvable identity is returned unchanged'
|
|
71
|
+
# ============================================================
|
|
72
|
+
|
|
73
|
+
id = resolve { Struct.new(:instance_id).new('anchor-instance-0001') }
|
|
74
|
+
assert id == 'anchor-instance-0001', 'a resolvable instance_id is returned as-is'
|
|
75
|
+
|
|
76
|
+
# ============================================================
|
|
77
|
+
section '2. No blank identity is ever produced'
|
|
78
|
+
# ============================================================
|
|
79
|
+
|
|
80
|
+
[[nil, 'nil'], ['', 'empty string'], [' ', 'whitespace only']].each do |value, label|
|
|
81
|
+
assert raises_unattributable? { Struct.new(:instance_id).new(value) },
|
|
82
|
+
"#{label} raises UnattributableAgentError instead of being returned"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
assert raises_unattributable? { raise NameError, 'uninitialized constant MMP::VERSION' },
|
|
86
|
+
'a partially loaded MMP raises UnattributableAgentError, not a placeholder'
|
|
87
|
+
|
|
88
|
+
assert raises_unattributable? { raise StandardError, 'registry unavailable' },
|
|
89
|
+
'any other failure to resolve raises UnattributableAgentError'
|
|
90
|
+
|
|
91
|
+
# ============================================================
|
|
92
|
+
section '3. The placeholders that collapsed identity are not reachable'
|
|
93
|
+
# ============================================================
|
|
94
|
+
|
|
95
|
+
# These are the two values the comparison sites treated as authority. Neither can
|
|
96
|
+
# come out of resolve_agent_id now; the assertions state which comparison each one
|
|
97
|
+
# used to satisfy, so a revision that reintroduces a placeholder fails here.
|
|
98
|
+
begin
|
|
99
|
+
produced = []
|
|
100
|
+
[nil, '', ' ', 'x'].each do |value|
|
|
101
|
+
produced << (resolve { Struct.new(:instance_id).new(value) } rescue :raised)
|
|
102
|
+
end
|
|
103
|
+
assert !produced.include?('unknown'),
|
|
104
|
+
"'unknown' is unreachable (RevocationManager authorises on revoker_id == attester_id)"
|
|
105
|
+
assert produced.none? { |p| p.is_a?(String) && p.strip.empty? },
|
|
106
|
+
"'' is unreachable (ChallengeManager compares against envelope&.attester_id.to_s)"
|
|
107
|
+
assert produced.count(:raised) == 3, 'exactly the three blank inputs raised'
|
|
108
|
+
assert produced.last == 'x', 'a non-blank identity still passes through'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# ============================================================
|
|
112
|
+
section '4. The error carries why, not just that'
|
|
113
|
+
# ============================================================
|
|
114
|
+
|
|
115
|
+
begin
|
|
116
|
+
resolve { raise NameError, 'uninitialized constant MMP::VERSION' }
|
|
117
|
+
rescue Synoptis::UnattributableAgentError => e
|
|
118
|
+
assert e.message.include?('refusing to record'), 'the message states the refusal'
|
|
119
|
+
assert e.message.include?('NameError'), 'the message keeps the underlying cause'
|
|
120
|
+
assert Synoptis::UnattributableAgentError < StandardError,
|
|
121
|
+
'the error is a StandardError so Protocol#handle turns it into an error response'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# ============================================================
|
|
125
|
+
puts "\n#{'=' * 60}"
|
|
126
|
+
puts "FINAL RESULTS: #{$pass} passed, #{$fail} failed"
|
|
127
|
+
puts '=' * 60
|
|
128
|
+
|
|
129
|
+
exit(1) if $fail > 0
|
|
@@ -32,7 +32,7 @@ module KairosMcp
|
|
|
32
32
|
type: 'object',
|
|
33
33
|
properties: {
|
|
34
34
|
proof_id: { type: 'string', description: 'The proof ID to verify' },
|
|
35
|
-
public_key: { type: 'string', description: '
|
|
35
|
+
public_key: { type: 'string', description: 'PEM public key of the attester, obtained out of band. Signature verification uses this key and only this key — a key recorded inside a proof is a self-report and is never trusted. Omitting it, or sending an empty string, yields no_public_key_for_verification and the proof is reported invalid.' }
|
|
36
36
|
},
|
|
37
37
|
required: %w[proof_id]
|
|
38
38
|
}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kairos-chain
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.58.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Masaomi Hatakeyama
|
|
@@ -760,6 +760,7 @@ files:
|
|
|
760
760
|
- templates/skillsets/synoptis/lib/synoptis/trust_scorer.rb
|
|
761
761
|
- templates/skillsets/synoptis/lib/synoptis/verifier.rb
|
|
762
762
|
- templates/skillsets/synoptis/skillset.json
|
|
763
|
+
- templates/skillsets/synoptis/test/test_agent_identity.rb
|
|
763
764
|
- templates/skillsets/synoptis/test/test_head_anchor.rb
|
|
764
765
|
- templates/skillsets/synoptis/test/test_l2_constitutive_slice1.rb
|
|
765
766
|
- templates/skillsets/synoptis/test/test_l2_constitutive_slice2.rb
|