k9o 1.0.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.

Potentially problematic release.


This version of k9o might be problematic. Click here for more details.

data/lib/k9o/vault.rb ADDED
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'uri'
5
+
6
+ module K9o
7
+ # `k9o.kibo.vault.secrets.*` — the §14 typed secrets surface
8
+ # (`services/gateway/src/routes/vault-secrets.ts`). Full family: list / get /
9
+ # create / reveal_challenge / reveal / update / rotate / move / delete.
10
+ #
11
+ # Contract live-verified against the real staging gateway (not just the
12
+ # route's OpenAPI schema, which is wrong/incomplete in several places noted
13
+ # below) by the `@k9o/kibo` TS reference build — this wrapper mirrors that
14
+ # verified behavior exactly:
15
+ #
16
+ # - Every operation on a NAMED secret (everything except `list`) REQUIRES
17
+ # `region` on the query string. Omitting it fails with
18
+ # K9O_INVALID_FIELD_VALUE ("this request requires an authoritative
19
+ # region"). NOT documented in the route's OpenAPI schema. `list` is the
20
+ # one exception (verified it does NOT need `region`).
21
+ # - The wire `value` field is base64 in BOTH directions, contradicting the
22
+ # OpenAPI's plain-language "the plaintext to seal" description (reads
23
+ # like it should be raw). A raw non-base64 `value` on create/update is
24
+ # rejected as unparseable. `create`/`update` take plaintext as a normal
25
+ # Ruby String and base64-encode it here; `reveal` base64-decodes the
26
+ # wire `value` back to plaintext before returning it — callers never
27
+ # see base64 on either side.
28
+ # - `update`/`rotate`/`move` responses are LIVE-VERIFIED NARROWER than (or
29
+ # entirely undocumented vs.) the OpenAPI's claimed `SecretSummary` — see
30
+ # the comment on each method below.
31
+ #
32
+ # `data` is returned as the raw wire Hash (camelCase string keys), matching
33
+ # this package's existing convention (Projects/Policies/Studio/etc. all
34
+ # return `envelope.data` untransformed — there is no generic
35
+ # camelCase<->snake_case response transform anywhere in this gem); `reveal`
36
+ # is the one exception, where the `value` key is base64-decoded in place
37
+ # (mirroring the TS reference's own `value`-only transform).
38
+ class VaultSecrets
39
+ def initialize(config)
40
+ @config = config
41
+ end
42
+
43
+ # List secrets (metadata only — never lists ciphertext).
44
+ # `GET /@<project_slug>/vault/secrets`.
45
+ # The one secrets operation that does NOT need `region` (live-verified).
46
+ def list(prefix: nil, cursor: nil, limit: nil)
47
+ slug = require_project_slug
48
+ q = {}
49
+ q['prefix'] = prefix if prefix
50
+ q['cursor'] = cursor if cursor
51
+ q['limit'] = limit if limit
52
+ HttpTransport.request(
53
+ config: @config, method: 'GET',
54
+ path: UrlGrammar.project_path(slug, 'vault/secrets'),
55
+ query: q
56
+ )
57
+ end
58
+
59
+ # Read a secret's metadata (never its value — use `reveal_challenge` +
60
+ # `reveal` for that).
61
+ # `GET /@<project_slug>/vault/secrets/<name>?region=<region>`.
62
+ #
63
+ # KNOWN LIVE BUG (staging, 2026-09-05): this call currently fails with
64
+ # K9O_REVEAL_CHALLENGE_REQUIRED on every attempt — the gateway does not
65
+ # honor its own documented "no X-K9O-Reveal-Challenge header => return
66
+ # metadata" behavior; it demands a challenge unconditionally. Implemented
67
+ # per the documented contract anyway (this is a backend defect to flag,
68
+ # not something to design around) — `list(prefix: exact_name)` is the
69
+ # current working alternative for a single secret's metadata until the
70
+ # gateway side is fixed.
71
+ def get(name, region:)
72
+ slug = require_project_slug
73
+ HttpTransport.request(
74
+ config: @config, method: 'GET',
75
+ path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
76
+ query: { 'region' => region }
77
+ )
78
+ end
79
+
80
+ # Create a secret under the caller's own auth.
81
+ # `POST /@<project_slug>/vault/secrets/<name>?region=<region>` → the full
82
+ # `sec_<base62>` summary.
83
+ #
84
+ # `region` scopes the vault WRITE itself (residency guard) and is
85
+ # REQUIRED — live-verified against staging, not documented in the route's
86
+ # OpenAPI schema.
87
+ #
88
+ # `visibility` (`{'kind' => 'private'}` or
89
+ # `{'kind' => 'shared', 'principals' => [...], 'roles' => [...]}`) and
90
+ # `metadata` (`Hash[String, String]`) are passed through verbatim — their
91
+ # own field names are already single words, so no wire-casing applies.
92
+ def create(name:, value:, region:, visibility: nil, metadata: nil)
93
+ slug = require_project_slug
94
+ body = { 'value' => Base64.strict_encode64(value) }
95
+ body['visibility'] = visibility if visibility
96
+ body['metadata'] = metadata if metadata
97
+ HttpTransport.request(
98
+ config: @config, method: 'POST',
99
+ path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
100
+ query: { 'region' => region },
101
+ body: body
102
+ )
103
+ end
104
+
105
+ # Mint a single-use reveal challenge — present it as
106
+ # `X-K9O-Reveal-Challenge` on the immediately-following `reveal` call.
107
+ # `POST .../<name>?op=reveal-challenge&region=<region>`.
108
+ def reveal_challenge(name, region:)
109
+ slug = require_project_slug
110
+ HttpTransport.request(
111
+ config: @config, method: 'POST',
112
+ path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
113
+ query: { 'op' => 'reveal-challenge', 'region' => region }
114
+ )
115
+ end
116
+
117
+ # Reveal a secret's plaintext value using a single-use challenge from
118
+ # `reveal_challenge`. `GET .../<name>?region=<region>` with
119
+ # `X-K9O-Reveal-Challenge`. Base64-decodes the wire's `value` back to
120
+ # plaintext (live-verified: the reveal response's `value` is base64,
121
+ # symmetric with `create`'s encode) before returning it to the caller.
122
+ def reveal(name, region:, challenge:)
123
+ slug = require_project_slug
124
+ envelope = HttpTransport.request(
125
+ config: @config, method: 'GET',
126
+ path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
127
+ query: { 'region' => region },
128
+ headers: { 'X-K9O-Reveal-Challenge' => challenge }
129
+ )
130
+ data = envelope.data
131
+ if data.is_a?(Hash) && data.key?('value')
132
+ data = data.merge('value' => Base64.decode64(data['value']))
133
+ end
134
+ Envelope.new(data: data, meta: envelope.meta)
135
+ end
136
+
137
+ # Update a secret's value (bumps version) — owner-only.
138
+ # `PUT .../<name>?region=<region>`.
139
+ #
140
+ # LIVE-VERIFIED NARROWER than documented: the gateway returns only
141
+ # `{id, name, version}` on a real PUT, not the full summary the OpenAPI
142
+ # schema claims — call `get` afterward if the full summary is needed.
143
+ def update(name, region:, value:, metadata: nil)
144
+ slug = require_project_slug
145
+ body = { 'value' => Base64.strict_encode64(value) }
146
+ body['metadata'] = metadata if metadata
147
+ HttpTransport.request(
148
+ config: @config, method: 'PUT',
149
+ path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
150
+ query: { 'region' => region },
151
+ body: body
152
+ )
153
+ end
154
+
155
+ # Rotate a secret's wrapping key (re-wraps under a fresh `kid`; content
156
+ # unchanged) — owner-only. `POST .../<name>?op=rotate&region=<region>`.
157
+ #
158
+ # LIVE-VERIFIED undocumented shape: `{id, name, version, rotatedAt,
159
+ # ledgerSequenceNumber}` — the route's OpenAPI schema doesn't document
160
+ # this response at all.
161
+ def rotate(name, region:)
162
+ slug = require_project_slug
163
+ HttpTransport.request(
164
+ config: @config, method: 'POST',
165
+ path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
166
+ query: { 'op' => 'rotate', 'region' => region }
167
+ )
168
+ end
169
+
170
+ # Move (rename) a secret's hierarchical path — owner-only.
171
+ # `POST .../<name>?op=move&region=<region>` with `{newPath}`.
172
+ #
173
+ # LIVE-VERIFIED undocumented shape: `{id, name, event}` (observed
174
+ # `event: "SECRET_PATH_CHANGED"`; treat as a plain String, not a closed
175
+ # enum — only one sample was observed).
176
+ def move(name, region:, new_path:)
177
+ slug = require_project_slug
178
+ HttpTransport.request(
179
+ config: @config, method: 'POST',
180
+ path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
181
+ query: { 'op' => 'move', 'region' => region },
182
+ body: { 'newPath' => new_path }
183
+ )
184
+ end
185
+
186
+ # Retire a secret — owner-only. `DELETE .../<name>?region=<region>` →
187
+ # `204 No Content` (HttpTransport already maps that to `data: nil`).
188
+ def delete(name, region:)
189
+ slug = require_project_slug
190
+ HttpTransport.request(
191
+ config: @config, method: 'DELETE',
192
+ path: UrlGrammar.project_path(slug, "vault/secrets/#{enc(name)}"),
193
+ query: { 'region' => region }
194
+ )
195
+ end
196
+
197
+ private
198
+
199
+ def require_project_slug
200
+ Studio.require_project_slug(@config, 'kibo.vault.secrets')
201
+ end
202
+
203
+ def enc(s)
204
+ ::URI.encode_www_form_component(s)
205
+ end
206
+ end
207
+
208
+ # `k9o.kibo.vault.*` — the Kibo vault surface (member-plane, project-scoped).
209
+ # Currently just `secrets` (ch08 §14); a `zk_deks` accessor
210
+ # (ch08 §11.3.2 F.5, `POST vault/zk-deks` / `GET vault/zk-deks/<id>`) would
211
+ # nest here alongside it if/when that surface is built for Ruby — out of
212
+ # scope for this wrapper, which only covers the vault-secrets audit gap.
213
+ class KiboVault
214
+ attr_reader :secrets
215
+
216
+ def initialize(config)
217
+ @secrets = VaultSecrets.new(config)
218
+ end
219
+ end
220
+
221
+ # `k9o.kibo.*` — per-client namespace root for the Kibo vault REST surface.
222
+ # Distinct from the module-level `K9o::Kibo` (native ZK `encrypt`/`decrypt`
223
+ # FFI bindings, ch08 §11.3.1/§11.3.3.6, called directly as
224
+ # `K9o::Kibo.encrypt`/`.decrypt` since that surface needs no per-client HTTP
225
+ # config): this class exists so `client.kibo.vault.secrets.*` is reachable
226
+ # per client instance, mirroring the TS reference's
227
+ # `client.kibo.vault.{zk_deks, secrets}` shape.
228
+ class KiboNamespace
229
+ attr_reader :vault
230
+
231
+ def initialize(config)
232
+ @vault = KiboVault.new(config)
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module K9o
4
+ VERSION = '1.0.0'
5
+ end
data/lib/k9o.rb ADDED
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # k9o — Kilimanjaro platform SDK for Ruby.
4
+ # Idiomatic: snake_case methods + raise K9o::KError +
5
+ # Net::HTTP transport.
6
+ #
7
+ # k9o = K9o.create_client(
8
+ # base_url: "https://acme.api.k9o.io",
9
+ # api_key: ENV["K9O_API_KEY"],
10
+ # org_slug: "acme",
11
+ # project_slug: "member-portal",
12
+ # )
13
+ #
14
+ # begin
15
+ # response = k9o.from("posts").select.limit(10).execute
16
+ # response.data.each { |row| puts row }
17
+ #
18
+ # session = k9o.gate.sign_in(email: "u@x.com", password: "...")
19
+ # puts "signed in: #{session.data['user']['id']}"
20
+ # rescue K9o::KError => e
21
+ # warn "[#{e.code}] #{e.message}: #{e.recommendation}"
22
+ # raise
23
+ # end
24
+
25
+ require_relative 'k9o/version'
26
+ require_relative 'k9o/kerror'
27
+ require_relative 'k9o/envelope'
28
+ require_relative 'k9o/url_grammar'
29
+ require_relative 'k9o/region'
30
+ require_relative 'k9o/http_transport'
31
+ require_relative 'k9o/data'
32
+ require_relative 'k9o/projects'
33
+ require_relative 'k9o/gate'
34
+ require_relative 'k9o/studio'
35
+ require_relative 'k9o/compute'
36
+ require_relative 'k9o/ai'
37
+ require_relative 'k9o/storage'
38
+ require_relative 'k9o/realtime'
39
+ require_relative 'k9o/migrations'
40
+ require_relative 'k9o/functions'
41
+ require_relative 'k9o/policies'
42
+ require_relative 'k9o/audit'
43
+ require_relative 'k9o/orgs'
44
+ require_relative 'k9o/me'
45
+ require_relative 'k9o/org'
46
+ # kibo ZK BYOK/HYOK crypto — self-guards its native binding (LoadError-safe), so
47
+ # requiring k9o never breaks on a platform without the kibo native lib.
48
+ require_relative 'k9o/kibo'
49
+ # kibo vault REST surface (client.kibo.vault.secrets) — ch08 §14.
50
+ require_relative 'k9o/vault'
51
+ # Barbican guardrail SDK — self-guards its native binding (LoadError-safe), so
52
+ # requiring k9o never breaks on a platform without the barbican native lib.
53
+ require_relative 'k9o/barbican'
54
+ require_relative 'k9o/client'
data/sig/k9o/ai.rbs ADDED
@@ -0,0 +1,16 @@
1
+ # RBS signatures for the `client.ai` namespace (platform AI primitives).
2
+
3
+ module K9o
4
+ class Ai
5
+ # `input` is one string or an array of strings, sent exactly as given.
6
+ # `model` is omitted from the request body when nil (server default).
7
+ # Returns an Envelope whose `data` is
8
+ # { "embeddings" => Array[Array[Float]], "model" => String, "dimensions" => Integer }.
9
+ def initialize: (untyped config) -> void
10
+ def embed: (input: (String | Array[String]), ?model: String?) -> Envelope
11
+ end
12
+
13
+ class K9oClient
14
+ attr_reader ai: Ai
15
+ end
16
+ end
data/sig/k9o/gate.rbs ADDED
@@ -0,0 +1,11 @@
1
+ # RBS signatures for the `client.gate` namespace (additions).
2
+
3
+ module K9o
4
+ class Gate
5
+ # GET /@<slug>/gate/hosted. Unauthenticated (no credential sent).
6
+ # Returns an Envelope whose `data` is { "signInUrl" => String, "rpId" => String }.
7
+ # Raises KError (K9O_MISSING_PROJECT_CONTEXT) without a project slug; the
8
+ # uniform 404 K9O_PROJECT_NOT_FOUND for unknown org / deleted / non-Gate project.
9
+ def hosted: () -> Envelope
10
+ end
11
+ end
@@ -0,0 +1,138 @@
1
+ # RBS signatures for the migration control-plane type model (ch13 §2.2, Ruby
2
+ # TIER-3: documented + statically-checkable hash pass-through). These describe
3
+ # the FIXED control-plane Operation union + its sub-shapes so steep/typeprof
4
+ # can check migration authoring. The runtime carries plain Hashes; the wire
5
+ # JSON matches packages/core/src/migrations.ts (snake_case keys, `searchable`
6
+ # NESTED inside the `encrypted` object form).
7
+ #
8
+ # Closed-union note: RBS cannot express "exactly one key set", so the Operation
9
+ # variants are typed as distinct single-key hash shapes and the closed-union
10
+ # property is enforced at wire + server validation (true for every tier).
11
+
12
+ module K9o
13
+ # The `encrypted` union: the bare `true` shorthand, or the object form with a
14
+ # NESTED `searchable` flag.
15
+ type column_encryption = { store: ("inline" | "vault"), ?searchable: bool }
16
+ type encrypted = (true | column_encryption)
17
+
18
+ type fk_action = ("CASCADE" | "RESTRICT" | "SET NULL" | "NO ACTION")
19
+
20
+ type column_references = {
21
+ collection: String,
22
+ on: String,
23
+ ?on_delete: fk_action,
24
+ ?on_update: fk_action,
25
+ }
26
+
27
+ # `type` stays a String (parser-owned vocabulary; not an enum).
28
+ type column_spec = {
29
+ name: String,
30
+ type: String,
31
+ ?primary_key: bool,
32
+ ?not_null: bool,
33
+ ?unique: bool,
34
+ ?default: String,
35
+ ?references: column_references,
36
+ ?encrypted: encrypted,
37
+ ?blind_indexed: bool,
38
+ }
39
+
40
+ type index_spec = {
41
+ ?name: String,
42
+ columns: Array[String],
43
+ ?unique: bool,
44
+ }
45
+
46
+ # KEY-TAGGED Operation union — EXACTLY these 6 (FROZEN at v2; rename_column is
47
+ # the pending 7th, intentionally absent). Each variant is a single-key hash.
48
+ type create_table_op = { create_table: { name: String, columns: Array[column_spec], ?indexes: Array[index_spec] } }
49
+ type drop_table_op = { drop_table: String }
50
+ type add_column_op = { add_column: { table: String, column: column_spec } }
51
+ type drop_column_op = { drop_column: { table: String, column: String } }
52
+ type alter_table_op = { alter_table: { name: String, ?add_columns: Array[column_spec], ?drop_columns: Array[String] } }
53
+ type create_index_op = { create_index: { table: String, index: index_spec } }
54
+ type drop_index_op = { drop_index: { name: String } }
55
+
56
+ type operation = (create_table_op | drop_table_op | add_column_op | drop_column_op | alter_table_op | create_index_op | drop_index_op)
57
+
58
+ type action_rule = { ?rule: untyped }
59
+
60
+ type relationship_spec = {
61
+ collection: String,
62
+ on: String,
63
+ as: String,
64
+ ?type: ("inner" | "left"),
65
+ }
66
+
67
+ type collection_policy = {
68
+ ?references: Array[relationship_spec],
69
+ ?actions: { ?select: action_rule, ?insert: action_rule, ?update: action_rule, ?delete: action_rule },
70
+ }
71
+
72
+ type storage_bucket_policy = {
73
+ ?public: bool,
74
+ ?actions: { ?read: action_rule, ?upload: action_rule, ?delete: action_rule, ?list: action_rule },
75
+ }
76
+
77
+ type migration_policy = {
78
+ ?collections: Hash[String, collection_policy],
79
+ ?storage: { ?buckets: Hash[String, storage_bucket_policy] },
80
+ }
81
+
82
+ type migration = {
83
+ version: Integer,
84
+ slug: String,
85
+ up: Array[operation],
86
+ down: Array[operation],
87
+ ?policy: migration_policy,
88
+ }
89
+
90
+ type applied_migration = {
91
+ version: Integer,
92
+ slug: String,
93
+ checksum: String,
94
+ appliedAt: String,
95
+ }
96
+
97
+ type pending_migration = {
98
+ pendingId: String,
99
+ version: Integer,
100
+ slug: String,
101
+ checksum: String,
102
+ submittedAt: String,
103
+ submittedBy: String,
104
+ }
105
+
106
+ class Migrations
107
+ @config: untyped
108
+ def initialize: (untyped config) -> void
109
+ def apply: (Array[migration] migrations) -> untyped
110
+ def list: () -> untyped
111
+ def get: (untyped version) -> untyped
112
+ def queue: (Array[migration] migrations) -> untyped
113
+ def list_pending: () -> untyped
114
+ def apply_pending: (String pending_id) -> untyped
115
+ def reject_pending: (String pending_id, ?reason: String?) -> untyped
116
+ def delete_pending: (String pending_id) -> untyped
117
+
118
+ private
119
+ def require_project_slug: () -> String
120
+ def enc: (untyped s) -> String
121
+
122
+ # Typed keyword-arg constructors for the control-plane Operation union.
123
+ module Build
124
+ def self?.column: (name: String, type: String, ?primary_key: bool?, ?not_null: bool?, ?unique: bool?, ?default: String?, ?references: column_references?, ?encrypted: encrypted?, ?blind_indexed: bool?) -> column_spec
125
+ def self?.column_encryption: (store: ("inline" | "vault"), ?searchable: bool?) -> column_encryption
126
+ def self?.references: (collection: String, on: String, ?on_delete: fk_action?, ?on_update: fk_action?) -> column_references
127
+ def self?.index: (columns: Array[String], ?name: String?, ?unique: bool?) -> index_spec
128
+ def self?.create_table: (name: String, columns: Array[column_spec], ?indexes: Array[index_spec]?) -> create_table_op
129
+ def self?.drop_table: (String name) -> drop_table_op
130
+ def self?.add_column: (table: String, column: column_spec) -> add_column_op
131
+ def self?.drop_column: (table: String, column: String) -> drop_column_op
132
+ def self?.alter_table: (name: String, ?add_columns: Array[column_spec]?, ?drop_columns: Array[String]?) -> alter_table_op
133
+ def self?.create_index: (table: String, index: index_spec) -> create_index_op
134
+ def self?.drop_index: (name: String) -> drop_index_op
135
+ def self?.migration: (version: Integer, slug: String, up: Array[operation], down: Array[operation], ?policy: migration_policy?) -> migration
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,19 @@
1
+ # RBS signatures for regional host resolution (K9o::Region).
2
+
3
+ module K9o
4
+ module Region
5
+ REGION_FREE_ORG_HOST: Regexp
6
+ REGION_CODE: Regexp
7
+
8
+ # The regional origin `location` may name for `host`, or nil.
9
+ def self.origin_from_location: (String host, String location, String base) -> String?
10
+ # The origin requests go to: cached regional origin, else the configured base URL. Never raises.
11
+ def self.resolve_origin: (HttpClientConfig config) -> String
12
+ def self.probe: (HttpClientConfig config) -> [String, bool]
13
+ end
14
+
15
+ class HttpClientConfig
16
+ attr_reader region_mutex: Thread::Mutex
17
+ attr_accessor regional_origin: String?
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ # RBS signatures for the `client.storage` namespace (buckets and objects).
2
+
3
+ module K9o
4
+ module Multipart
5
+ type part = { name: String, value: untyped, ?filename: String, ?content_type: String }
6
+ def self.encode: (Array[part] parts) -> [String, String]
7
+ end
8
+
9
+ class Storage
10
+ MAX_EXPIRES_IN: Integer
11
+
12
+ def initialize: (untyped config) -> void
13
+ def list_buckets: () -> Envelope
14
+ def get_bucket: (String name) -> Envelope
15
+ def create_bucket: (String name, ?public: bool?, ?file_size_limit: Integer?, ?allowed_mime_types: Array[String]?,
16
+ ?versioning: bool?, ?delete_after_days: Integer?, ?byos_binding_id: String?) -> Envelope
17
+ # `patch` keys: snake_case or wire camelCase; an explicit nil clears a limit.
18
+ def update_bucket: (String name, Hash[(String | Symbol), untyped] patch) -> Envelope
19
+ def delete_bucket: (String name, ?empty: bool) -> Envelope
20
+ def from: (String bucket) -> BucketHandle
21
+
22
+ class BucketHandle
23
+ def initialize: (untyped config, String bucket) -> void
24
+ # `data` is a String (sent as its bytes) or an IO-like responding to #read.
25
+ def upload: (String path, (String | _Reader) data, ?content_type: String?, ?upsert: bool,
26
+ ?metadata: Hash[String, String]?) -> Envelope
27
+ # `expires_in` is required: Integer 1..604800 (K9O_INVALID_FIELD_VALUE otherwise).
28
+ def create_signed_url: (String path, expires_in: Integer, ?download: bool?) -> Envelope
29
+ def list: (?prefix: String?) -> Envelope
30
+ def remove: (String path) -> Envelope
31
+ end
32
+
33
+ interface _Reader
34
+ def read: () -> String
35
+ end
36
+ end
37
+
38
+ class K9oClient
39
+ attr_reader storage: Storage
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: k9o
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Kilimanjaro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-10-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.20'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.20'
27
+ description: Idiomatic Ruby SDK — create_client + data DSL + projects + KError. snake_case
28
+ + raise K9o::KError.
29
+ email:
30
+ - engineering@kilimanjaro.dev
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - CHANGELOG.md
36
+ - LICENSE
37
+ - NOTICE
38
+ - README.md
39
+ - k9o.gemspec
40
+ - lib/k9o.rb
41
+ - lib/k9o/ai.rb
42
+ - lib/k9o/audit.rb
43
+ - lib/k9o/barbican.rb
44
+ - lib/k9o/client.rb
45
+ - lib/k9o/compute.rb
46
+ - lib/k9o/data.rb
47
+ - lib/k9o/envelope.rb
48
+ - lib/k9o/functions.rb
49
+ - lib/k9o/gate.rb
50
+ - lib/k9o/http_transport.rb
51
+ - lib/k9o/kerror.rb
52
+ - lib/k9o/kibo.rb
53
+ - lib/k9o/me.rb
54
+ - lib/k9o/migrations.rb
55
+ - lib/k9o/multipart.rb
56
+ - lib/k9o/org.rb
57
+ - lib/k9o/orgs.rb
58
+ - lib/k9o/policies.rb
59
+ - lib/k9o/projects.rb
60
+ - lib/k9o/realtime.rb
61
+ - lib/k9o/region.rb
62
+ - lib/k9o/storage.rb
63
+ - lib/k9o/studio.rb
64
+ - lib/k9o/url_grammar.rb
65
+ - lib/k9o/vault.rb
66
+ - lib/k9o/version.rb
67
+ - sig/k9o/ai.rbs
68
+ - sig/k9o/gate.rbs
69
+ - sig/k9o/migrations.rbs
70
+ - sig/k9o/region.rbs
71
+ - sig/k9o/storage.rbs
72
+ homepage: https://kilimanjaro.dev
73
+ licenses:
74
+ - Apache-2.0
75
+ metadata:
76
+ homepage_uri: https://kilimanjaro.dev
77
+ source_code_uri: https://github.com/k9o-dev/sdk-ruby
78
+ changelog_uri: https://github.com/k9o-dev/sdk-ruby/blob/main/CHANGELOG.md
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: 3.2.0
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubygems_version: 3.5.22
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Kilimanjaro platform SDK for Ruby
98
+ test_files: []