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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b831eba22baf94d43c7592c5ff2dbf12ca928dcea034b713837bf5253e691f99
4
- data.tar.gz: 65c6cd548075ccfccf85d3330548fe51280d39f7c767f6de96c8d64fa5afd799
3
+ metadata.gz: 754ac017b9725fc33b9efa2b60bbcb8e8e73cbcfdd239b5c5f9f0fc5ede94cbc
4
+ data.tar.gz: 4f93f73953d9dd89728bbd66256d69dabf5aa66242b3ec15b4ce782bdcfa29f0
5
5
  SHA512:
6
- metadata.gz: df294d0d060010e7d589e972ab12164c9cc1451bfd069693d5296bafb9512e95c985d83f4cb849a9d5064f76f0c89b57fbad2245d517f4cc759ce00ebae0f446
7
- data.tar.gz: 81a7d541ebf5afc58b938f2b6d2b22c0f38c3c32d69705c2b6cd4074425292a0defa98a4adc74834659bef418a78539b2f7f2a90d48eb26df1f6961c1e41c978
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
@@ -1,4 +1,4 @@
1
1
  module KairosMcp
2
- VERSION = "3.0.0"
2
+ VERSION = "3.1.1"
3
3
  CHANGELOG_URL = "https://github.com/masaomi/KairosChain_2026/blob/main/CHANGELOG.md"
4
4
  end
@@ -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: meeting_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: GrantManager::GRANT_CREATION_COOLDOWN -
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
- GRANT_CREATION_COOLDOWN = 300 # 5 minutes
7
+ DEFAULT_GRANT_CREATION_COOLDOWN = 300 # 5 minutes
8
8
  MAX_GRANTS_PER_IP_PER_HOUR = 5
9
9
 
10
- def initialize(pg_pool:, plan_registry:)
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]) < GRANT_CREATION_COOLDOWN
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
- @grant_manager = GrantManager.new(pg_pool: @pg_pool, plan_registry: @plan_registry)
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.0.0
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-21 00:00:00.000000000 Z
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.3.26
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