kairos-chain 3.0.0 → 3.1.1
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/CHANGELOG.md +41 -0
- data/lib/kairos_mcp/version.rb +1 -1
- data/templates/skillsets/mmp/lib/mmp/place_client.rb +2 -0
- data/templates/skillsets/mmp/tools/meeting_acquire_skill.rb +2 -2
- data/templates/skillsets/mmp/tools/meeting_browse.rb +1 -1
- data/templates/skillsets/mmp/tools/meeting_connect.rb +3 -1
- data/templates/skillsets/mmp/tools/meeting_deposit.rb +1 -1
- data/templates/skillsets/mmp/tools/meeting_federate.rb +4 -4
- data/templates/skillsets/mmp/tools/meeting_get_skill_details.rb +1 -1
- data/templates/skillsets/service_grant/lib/service_grant/access_checker.rb +1 -1
- data/templates/skillsets/service_grant/lib/service_grant/access_gate.rb +1 -0
- data/templates/skillsets/service_grant/lib/service_grant/grant_manager.rb +9 -5
- data/templates/skillsets/service_grant/lib/service_grant.rb +3 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 754ac017b9725fc33b9efa2b60bbcb8e8e73cbcfdd239b5c5f9f0fc5ede94cbc
|
|
4
|
+
data.tar.gz: 4f93f73953d9dd89728bbd66256d69dabf5aa66242b3ec15b4ce782bdcfa29f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d3f3b1f2ec4b18ae428e3287e9225e5282abdefb811129d972c99c14107a5026e128b2cbd102c65e3b5e662dc7c7fbb9e3d72498cb43c50167d7bf7835ae93f
|
|
7
|
+
data.tar.gz: 73a4c69bb24acfcf147f8f1b505f1601c6c2264a5121d49ce83df3a84551e5e8aea707abc8e0fb4ba7b6569dd1fcaa8c79df52e3b9f52f32f35d4f9b9c4c10e5
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,45 @@ 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.1.1] - 2026-03-23
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **HTTPS support for all MMP HTTP calls**: `Net::HTTP.new` does not enable SSL by default. All MMP PlaceClient and meeting_* tools failed with "Connection reset by peer" when connecting to TLS-enabled Meeting Place servers. Added `http.use_ssl = (uri.scheme == 'https')` to all HTTP calls.
|
|
12
|
+
- **Caddy DNS resolvers**: Ubuntu 24.04 uses systemd-resolved (127.0.0.53) which is inaccessible from Docker containers. Added external DNS resolvers (8.8.8.8, 1.1.1.1) to Caddy container.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **Domain**: `meeting.kairoschain.io` → `meeting.genomicschain.io`
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## [3.1.0] - 2026-03-22
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **Docker Production Deployment**: Complete Docker setup for Meeting Place server on EC2
|
|
25
|
+
- `docker-compose.prod.yml` with Caddy TLS reverse proxy (`meeting.kairoschain.io`)
|
|
26
|
+
- Network isolation: `frontend` (Caddy + app) / `backend` (app + PG)
|
|
27
|
+
- Security headers (HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy)
|
|
28
|
+
- EC2 setup script (Amazon Linux 2023, Docker Compose via dnf)
|
|
29
|
+
- Service Grant DB migrations in entrypoint
|
|
30
|
+
- Volume upgrade: automatic SkillSet backfill from template on existing volumes
|
|
31
|
+
- **Configurable Grant Creation Cooldown** (`grant_creation_cooldown`): Config option in `service_grant.yml` (default: 300s, set to 0 to disable). Future: trust-based cooldown where `cooldown = base * (1.0 - trust_score)`
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **AccessGate owner bypass**: Admin/owner tokens (from `--init-admin`) were blocked by Service Grant with "pubkey_hash missing from auth context". Owner role now bypasses Service Grant checks — admin tokens are system management, not service consumers
|
|
36
|
+
- **GrantManager record_with_retry kwargs**: `record_grant_event` passed bare kwargs to `record_with_retry(event, attempt:)`, leaving the positional `event` parameter empty → `ArgumentError`. Fixed with explicit `{}` braces. Caused 500 errors on Place API endpoints
|
|
37
|
+
- **meeting_connect session_token**: `connect_relay` saved the MMP introduce handshake token (`/meeting/v1/introduce`) instead of the Place register token (`/place/v1/register`). The MMP token lacks `pubkey_hash` in the session store, causing all Place API write operations (deposit, acquire) to fail with 403 "Cannot resolve identity"
|
|
38
|
+
|
|
39
|
+
### Review
|
|
40
|
+
|
|
41
|
+
- Docker deployment: 2 rounds × 3 LLMs (Claude Agent Team, Cursor Composer-2, Cursor GPT-5.4), converged at Round 2
|
|
42
|
+
- Service Grant bugfixes: 1 round × 3 LLMs, 3/3 APPROVE
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
7
46
|
## [3.0.0] - 2026-03-21
|
|
8
47
|
|
|
9
48
|
### Added
|
|
@@ -476,6 +515,8 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
476
515
|
- Skill promotion with Persona Assembly
|
|
477
516
|
- Tool guide and metadata system
|
|
478
517
|
|
|
518
|
+
[3.1.1]: https://github.com/masaomi/KairosChain_2026/compare/v3.1.0...v3.1.1
|
|
519
|
+
[3.1.0]: https://github.com/masaomi/KairosChain_2026/compare/v3.0.0...v3.1.0
|
|
479
520
|
[3.0.0]: https://github.com/masaomi/KairosChain_2026/compare/v2.10.1...v3.0.0
|
|
480
521
|
[2.10.1]: https://github.com/masaomi/KairosChain_2026/compare/v2.10.0...v2.10.1
|
|
481
522
|
[2.10.0]: https://github.com/masaomi/KairosChain_2026/compare/v2.9.0...v2.10.0
|
data/lib/kairos_mcp/version.rb
CHANGED
|
@@ -127,6 +127,7 @@ module MMP
|
|
|
127
127
|
uri = URI.parse("#{@place_url}#{path}")
|
|
128
128
|
uri.query = URI.encode_www_form(params) unless params.empty?
|
|
129
129
|
http = Net::HTTP.new(uri.host, uri.port); http.open_timeout = @timeout; http.read_timeout = @timeout
|
|
130
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
130
131
|
req = Net::HTTP::Get.new(uri)
|
|
131
132
|
req['Authorization'] = "Bearer #{@bearer_token}" if @bearer_token
|
|
132
133
|
response = http.request(req)
|
|
@@ -139,6 +140,7 @@ module MMP
|
|
|
139
140
|
def post(path, body)
|
|
140
141
|
uri = URI.parse("#{@place_url}#{path}")
|
|
141
142
|
http = Net::HTTP.new(uri.host, uri.port); http.open_timeout = @timeout; http.read_timeout = @timeout
|
|
143
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
142
144
|
req = Net::HTTP::Post.new(uri.path)
|
|
143
145
|
req['Content-Type'] = 'application/json'
|
|
144
146
|
req['Authorization'] = "Bearer #{@bearer_token}" if @bearer_token
|
|
@@ -215,7 +215,7 @@ module KairosMcp
|
|
|
215
215
|
path = "/place/v1/skill_content/#{URI.encode_www_form_component(skill_id)}"
|
|
216
216
|
path += "?owner=#{URI.encode_www_form_component(owner_agent_id)}" if owner_agent_id
|
|
217
217
|
uri = URI.parse("#{url}#{path}")
|
|
218
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
218
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
219
219
|
http.open_timeout = 5; http.read_timeout = 10
|
|
220
220
|
req = Net::HTTP::Get.new(uri)
|
|
221
221
|
req['Authorization'] = "Bearer #{bearer_token}" if bearer_token
|
|
@@ -254,7 +254,7 @@ module KairosMcp
|
|
|
254
254
|
|
|
255
255
|
def get_skill_direct(endpoint, skill_id, bearer_token: nil)
|
|
256
256
|
uri = URI.parse("#{endpoint}/meeting/v1/skill_content")
|
|
257
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
257
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
258
258
|
http.open_timeout = 5; http.read_timeout = 10
|
|
259
259
|
req = Net::HTTP::Post.new(uri.path)
|
|
260
260
|
req['Content-Type'] = 'application/json'
|
|
@@ -99,7 +99,7 @@ module KairosMcp
|
|
|
99
99
|
def browse_place(url, token, params)
|
|
100
100
|
query = URI.encode_www_form(params)
|
|
101
101
|
uri = URI.parse("#{url}/place/v1/board/browse?#{query}")
|
|
102
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
102
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
103
103
|
http.open_timeout = 5; http.read_timeout = 10
|
|
104
104
|
req = Net::HTTP::Get.new(uri)
|
|
105
105
|
req['Authorization'] = "Bearer #{token}" if token
|
|
@@ -155,7 +155,8 @@ module KairosMcp
|
|
|
155
155
|
result = {
|
|
156
156
|
status: 'connected', mode: 'relay', url: url, relay_mode: true,
|
|
157
157
|
identity_verified: verified,
|
|
158
|
-
session_token:
|
|
158
|
+
session_token: session_token,
|
|
159
|
+
meeting_session_token: meeting_session_token,
|
|
159
160
|
meeting_place: { url: url, name: place_info['name'] || place_info[:name] || 'Meeting Place' },
|
|
160
161
|
self_agent_id: agent_id,
|
|
161
162
|
your_skills: skill_counts,
|
|
@@ -249,6 +250,7 @@ module KairosMcp
|
|
|
249
250
|
def http_post(url, body)
|
|
250
251
|
uri = URI.parse(url)
|
|
251
252
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
253
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
252
254
|
req = Net::HTTP::Post.new(uri.path)
|
|
253
255
|
req['Content-Type'] = 'application/json'
|
|
254
256
|
req.body = JSON.generate(body)
|
|
@@ -173,7 +173,7 @@ module KairosMcp
|
|
|
173
173
|
|
|
174
174
|
def deposit_to_place(url, token, skill)
|
|
175
175
|
uri = URI.parse("#{url}/place/v1/deposit")
|
|
176
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
176
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
177
177
|
http.open_timeout = 5; http.read_timeout = 15
|
|
178
178
|
req = Net::HTTP::Post.new(uri.path)
|
|
179
179
|
req['Content-Type'] = 'application/json'
|
|
@@ -170,7 +170,7 @@ module KairosMcp
|
|
|
170
170
|
|
|
171
171
|
query = URI.encode_www_form(params)
|
|
172
172
|
uri = URI.parse("#{url}/place/v1/board/browse?#{query}")
|
|
173
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
173
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
174
174
|
http.open_timeout = 5; http.read_timeout = 10
|
|
175
175
|
req = Net::HTTP::Get.new(uri)
|
|
176
176
|
req['Authorization'] = "Bearer #{token}"
|
|
@@ -198,7 +198,7 @@ module KairosMcp
|
|
|
198
198
|
path = "/place/v1/skill_content/#{URI.encode_www_form_component(skill_id)}"
|
|
199
199
|
path += "?owner=#{URI.encode_www_form_component(owner_agent_id)}" if owner_agent_id
|
|
200
200
|
uri = URI.parse("#{url}#{path}")
|
|
201
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
201
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
202
202
|
http.open_timeout = 5; http.read_timeout = 10
|
|
203
203
|
req = Net::HTTP::Get.new(uri)
|
|
204
204
|
req['Authorization'] = "Bearer #{token}"
|
|
@@ -224,7 +224,7 @@ module KairosMcp
|
|
|
224
224
|
# Returns nil on failure (caller should fallback to URL).
|
|
225
225
|
def resolve_place_id(url)
|
|
226
226
|
uri = URI.parse("#{url}/place/v1/info")
|
|
227
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
227
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
228
228
|
http.open_timeout = 3; http.read_timeout = 5
|
|
229
229
|
req = Net::HTTP::Get.new(uri)
|
|
230
230
|
response = http.request(req)
|
|
@@ -262,7 +262,7 @@ module KairosMcp
|
|
|
262
262
|
# POST skill to target Place with provenance
|
|
263
263
|
def deposit_to_target(url, token, skill)
|
|
264
264
|
uri = URI.parse("#{url}/place/v1/deposit")
|
|
265
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
265
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
266
266
|
http.open_timeout = 5; http.read_timeout = 15
|
|
267
267
|
req = Net::HTTP::Post.new(uri.path)
|
|
268
268
|
req['Content-Type'] = 'application/json'
|
|
@@ -118,7 +118,7 @@ module KairosMcp
|
|
|
118
118
|
|
|
119
119
|
def get_details_direct(endpoint, skill_id, bearer_token: nil)
|
|
120
120
|
uri = URI.parse("#{endpoint}/meeting/v1/skill_details?skill_id=#{URI.encode_www_form_component(skill_id)}")
|
|
121
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
121
|
+
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = (uri.scheme == 'https')
|
|
122
122
|
http.open_timeout = 3; http.read_timeout = 5
|
|
123
123
|
req = Net::HTTP::Get.new(uri)
|
|
124
124
|
req['Authorization'] = "Bearer #{bearer_token}" if bearer_token
|
|
@@ -43,7 +43,7 @@ module ServiceGrant
|
|
|
43
43
|
if @grant_manager.in_cooldown?(grant) && write_action?(service, action)
|
|
44
44
|
raise AccessDeniedError.new(:cooldown, service: service, action: action,
|
|
45
45
|
message: "New grant in cooldown period. Read-only actions only.",
|
|
46
|
-
cooldown_remaining:
|
|
46
|
+
cooldown_remaining: @grant_manager.cooldown -
|
|
47
47
|
(Time.now - grant[:first_seen_at]).to_i)
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -22,6 +22,7 @@ module ServiceGrant
|
|
|
22
22
|
return unless user_ctx # STDIO mode -- permissive
|
|
23
23
|
|
|
24
24
|
return if user_ctx[:local_dev] # local dev mode -- permissive
|
|
25
|
+
return if user_ctx[:role] == 'owner' # admin/owner bypasses service grant checks
|
|
25
26
|
|
|
26
27
|
pubkey_hash = user_ctx[:pubkey_hash]
|
|
27
28
|
|
|
@@ -4,12 +4,15 @@ require 'time'
|
|
|
4
4
|
|
|
5
5
|
module ServiceGrant
|
|
6
6
|
class GrantManager
|
|
7
|
-
|
|
7
|
+
DEFAULT_GRANT_CREATION_COOLDOWN = 300 # 5 minutes
|
|
8
8
|
MAX_GRANTS_PER_IP_PER_HOUR = 5
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
attr_reader :plan_registry, :cooldown
|
|
11
|
+
|
|
12
|
+
def initialize(pg_pool:, plan_registry:, cooldown: nil)
|
|
11
13
|
@pg = pg_pool
|
|
12
14
|
@plans = plan_registry
|
|
15
|
+
@cooldown = cooldown || DEFAULT_GRANT_CREATION_COOLDOWN
|
|
13
16
|
@ip_tracker = IpRateTracker.new(
|
|
14
17
|
max: MAX_GRANTS_PER_IP_PER_HOUR, window: 3600, pg_pool: pg_pool
|
|
15
18
|
)
|
|
@@ -133,8 +136,9 @@ module ServiceGrant
|
|
|
133
136
|
end
|
|
134
137
|
|
|
135
138
|
def in_cooldown?(grant)
|
|
139
|
+
return false if @cooldown <= 0
|
|
136
140
|
return false unless grant[:first_seen_at]
|
|
137
|
-
(Time.now - grant[:first_seen_at]) <
|
|
141
|
+
(Time.now - grant[:first_seen_at]) < @cooldown
|
|
138
142
|
end
|
|
139
143
|
|
|
140
144
|
def grants_with_unknown_plans(plan_registry)
|
|
@@ -177,11 +181,11 @@ module ServiceGrant
|
|
|
177
181
|
MAX_RECORDING_RETRIES = 3
|
|
178
182
|
|
|
179
183
|
def record_grant_event(pubkey_hash, service, action, details = {})
|
|
180
|
-
record_with_retry(
|
|
184
|
+
record_with_retry({
|
|
181
185
|
type: 'service_grant_event', layer: 'L1',
|
|
182
186
|
pubkey_hash: pubkey_hash, service: service,
|
|
183
187
|
action: action, details: details, timestamp: Time.now.iso8601
|
|
184
|
-
)
|
|
188
|
+
})
|
|
185
189
|
end
|
|
186
190
|
|
|
187
191
|
# Record with retry (non-blocking, up to MAX_RECORDING_RETRIES attempts).
|
|
@@ -47,7 +47,9 @@ module ServiceGrant
|
|
|
47
47
|
|
|
48
48
|
# 3. IP resolution + domain objects
|
|
49
49
|
@ip_resolver = ClientIpResolver.new(config['ip_resolution'] || {})
|
|
50
|
-
|
|
50
|
+
cooldown = config['grant_creation_cooldown']&.to_i
|
|
51
|
+
@grant_manager = GrantManager.new(pg_pool: @pg_pool, plan_registry: @plan_registry,
|
|
52
|
+
cooldown: cooldown)
|
|
51
53
|
@usage_tracker = UsageTracker.new(pg_pool: @pg_pool, plan_registry: @plan_registry,
|
|
52
54
|
cycle_manager: @cycle_manager)
|
|
53
55
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kairos-chain
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Masaomi Hatakeyama
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|
|
@@ -389,7 +389,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
389
389
|
- !ruby/object:Gem::Version
|
|
390
390
|
version: '0'
|
|
391
391
|
requirements: []
|
|
392
|
-
rubygems_version: 3.
|
|
392
|
+
rubygems_version: 3.5.22
|
|
393
393
|
signing_key:
|
|
394
394
|
specification_version: 4
|
|
395
395
|
summary: KairosChain - Self-referential MCP server for auditable skill self-management
|