k9o 1.4.0 → 1.5.2
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 +69 -1
- data/README.md +1273 -142
- data/k9o.gemspec +2 -2
- data/lib/k9o/analytics.rb +403 -0
- data/lib/k9o/approvals.rb +102 -8
- data/lib/k9o/audit.rb +150 -0
- data/lib/k9o/client.rb +18 -3
- data/lib/k9o/crypto.rb +471 -0
- data/lib/k9o/custody.rb +28 -0
- data/lib/k9o/data.rb +65 -11
- data/lib/k9o/ed25519.rb +182 -0
- data/lib/k9o/end_users.rb +93 -0
- data/lib/k9o/envelope.rb +20 -5
- data/lib/k9o/functions.rb +82 -3
- data/lib/k9o/gate.rb +46 -7
- data/lib/k9o/http_transport.rb +71 -14
- data/lib/k9o/jcs.rb +117 -0
- data/lib/k9o/kerror.rb +57 -5
- data/lib/k9o/keys.rb +131 -0
- data/lib/k9o/kibo_client_core.rb +385 -0
- data/lib/k9o/kibo_ffi_core.rb +178 -0
- data/lib/k9o/locate.rb +118 -0
- data/lib/k9o/me.rb +1 -1
- data/lib/k9o/metadata.rb +342 -0
- data/lib/k9o/moat.rb +443 -0
- data/lib/k9o/org.rb +4 -5
- data/lib/k9o/rampart.rb +273 -0
- data/lib/k9o/realtime.rb +261 -10
- data/lib/k9o/schedules.rb +109 -0
- data/lib/k9o/vault.rb +3 -4
- data/lib/k9o/vectors.rb +124 -0
- data/lib/k9o/version.rb +1 -1
- data/lib/k9o.rb +13 -0
- data/sig/k9o/approvals.rbs +25 -3
- data/sig/k9o/datasets.rbs +125 -0
- data/sig/k9o/gate.rbs +2 -0
- data/sig/k9o/kerror.rbs +24 -0
- data/sig/k9o/kibo_client_core.rbs +25 -0
- data/sig/k9o/metadata.rbs +54 -0
- data/sig/k9o/moat.rbs +29 -0
- data/sig/k9o/rampart.rbs +59 -0
- data/sig/k9o/realtime.rbs +34 -0
- data/sig/k9o/workload.rbs +64 -0
- metadata +27 -4
data/lib/k9o/vault.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'base64'
|
|
4
3
|
require 'uri'
|
|
5
4
|
|
|
6
5
|
module K9o
|
|
@@ -81,7 +80,7 @@ module K9o
|
|
|
81
80
|
# `metadata` (`Hash[String, String]`) are passed through verbatim.
|
|
82
81
|
def create(name:, value:, region: nil, visibility: nil, metadata: nil)
|
|
83
82
|
slug = require_project_slug
|
|
84
|
-
body = { 'value' =>
|
|
83
|
+
body = { 'value' => [value].pack('m0') }
|
|
85
84
|
body['visibility'] = visibility if visibility
|
|
86
85
|
body['metadata'] = metadata if metadata
|
|
87
86
|
HttpTransport.request(
|
|
@@ -119,7 +118,7 @@ module K9o
|
|
|
119
118
|
)
|
|
120
119
|
data = envelope.data
|
|
121
120
|
if data.is_a?(Hash) && data.key?('value')
|
|
122
|
-
data = data.merge('value' =>
|
|
121
|
+
data = data.merge('value' => data['value'].unpack1('m'))
|
|
123
122
|
end
|
|
124
123
|
Envelope.new(data: data, meta: envelope.meta)
|
|
125
124
|
end
|
|
@@ -132,7 +131,7 @@ module K9o
|
|
|
132
131
|
# schema claims — call `get` afterward if the full summary is needed.
|
|
133
132
|
def update(name, value:, region: nil, metadata: nil)
|
|
134
133
|
slug = require_project_slug
|
|
135
|
-
body = { 'value' =>
|
|
134
|
+
body = { 'value' => [value].pack('m0') }
|
|
136
135
|
body['metadata'] = metadata if metadata
|
|
137
136
|
HttpTransport.request(
|
|
138
137
|
config: @config, method: 'PUT',
|
data/lib/k9o/vectors.rb
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
# Vector collections: k9o.vectors.* store embeddings you computed yourself, next to the text and metadata they came
|
|
5
|
+
# from, and find the nearest ones.
|
|
6
|
+
#
|
|
7
|
+
# nearest(collection, query_vector, top_k:, min_score:, metric:, filters:) POST /@<slug>/vectors/<collection>/search
|
|
8
|
+
# upsert(collection, rows) POST /@<slug>/vectors/<collection>/upsert
|
|
9
|
+
#
|
|
10
|
+
# Both send the vector in the request body, so they take real embeddings of any common size (up to 16000 numbers).
|
|
11
|
+
# The collection name must start with +_+. The first upsert creates the collection and fixes its dimension; every
|
|
12
|
+
# later vector must have the same length. Searching a collection that was never upserted answers 404
|
|
13
|
+
# K9O_COLLECTION_NOT_FOUND.
|
|
14
|
+
class Vectors
|
|
15
|
+
METRICS = %w[cosine euclidean dot].freeze
|
|
16
|
+
MAX_VECTOR = 16_000
|
|
17
|
+
MAX_ROWS = 1000
|
|
18
|
+
# Snake_case spellings accepted for the camelCase wire keys of a row and of its +_source+.
|
|
19
|
+
ROW_ALIASES = { 'content_hash' => 'contentHash', 'subject_ids' => 'subjectIds' }.freeze
|
|
20
|
+
SOURCE_ALIASES = { 'chunk_index' => 'chunkIndex' }.freeze
|
|
21
|
+
|
|
22
|
+
def initialize(config)
|
|
23
|
+
@config = config
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The rows nearest to +query_vector+ (1 to 16000 numbers), closest first. +top_k+ is 1 to 1000 (the gateway
|
|
27
|
+
# defaults to 10); +min_score+ drops results scoring below it; +metric+ is cosine (default), euclidean or dot;
|
|
28
|
+
# +filters+ is a Hash of equality checks on +id+, +contentHash+, +source.<key>+ and +metadata.<key>+.
|
|
29
|
+
# +data+ is { 'results' => [...], 'collection' => name }.
|
|
30
|
+
def nearest(collection, query_vector, top_k: nil, min_score: nil, metric: nil, filters: nil)
|
|
31
|
+
route = path(collection, 'search')
|
|
32
|
+
check_vector(query_vector, 'query_vector')
|
|
33
|
+
body = { 'queryVector' => query_vector.to_a }
|
|
34
|
+
unless top_k.nil?
|
|
35
|
+
unless top_k.is_a?(Integer) && top_k >= 1 && top_k <= 1000
|
|
36
|
+
raise invalid('top_k must be a whole number from 1 to 1000', top_k: top_k)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
body['topK'] = top_k
|
|
40
|
+
end
|
|
41
|
+
unless min_score.nil?
|
|
42
|
+
raise invalid('min_score must be a number', min_score: min_score) unless finite?(min_score)
|
|
43
|
+
|
|
44
|
+
body['minScore'] = min_score
|
|
45
|
+
end
|
|
46
|
+
body['metric'] = ApprovalChecks.enum!(metric, METRICS, 'metric') unless metric.nil?
|
|
47
|
+
unless filters.nil?
|
|
48
|
+
raise invalid('filters must be a Hash of path to value') unless filters.is_a?(Hash)
|
|
49
|
+
|
|
50
|
+
body['filters'] = filters.transform_keys(&:to_s)
|
|
51
|
+
end
|
|
52
|
+
call(route, body)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Insert or overwrite 1 to 1000 rows in one request; the first upsert creates the collection. A row is a Hash
|
|
56
|
+
# with +vector+ and +_source+ ({ handle:, chunkIndex: }), and optionally +id+, +contentHash+, +text+, +metadata+
|
|
57
|
+
# and +subjectIds+ (one end-user id, ["eu_..."]; required unless the collection was declared shared). Keys may
|
|
58
|
+
# be strings or symbols, and +content_hash+, +subject_ids+ and +chunk_index+ are accepted for the camelCase
|
|
59
|
+
# names. A single row may be passed on its own, with or without braces. +data+ is { 'upserted' => n,
|
|
60
|
+
# 'collection' => name }.
|
|
61
|
+
def upsert(collection, rows = nil, **row)
|
|
62
|
+
route = path(collection, 'upsert')
|
|
63
|
+
rows = [row] if rows.nil? && !row.empty?
|
|
64
|
+
rows = [rows] if rows.is_a?(Hash)
|
|
65
|
+
unless rows.is_a?(Array) && rows.size >= 1 && rows.size <= MAX_ROWS
|
|
66
|
+
raise invalid("rows must be an array of 1 to #{MAX_ROWS} rows")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
wire = rows.each_with_index.map do |entry, index|
|
|
70
|
+
raise invalid("rows[#{index}] must be a Hash") unless entry.is_a?(Hash)
|
|
71
|
+
|
|
72
|
+
normalize_row(entry, index)
|
|
73
|
+
end
|
|
74
|
+
call(route, 'rows' => wire)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def normalize_row(entry, index)
|
|
80
|
+
row = entry.each_with_object({}) do |(key, value), out|
|
|
81
|
+
name = key.to_s
|
|
82
|
+
out[ROW_ALIASES.fetch(name, name)] = value
|
|
83
|
+
end
|
|
84
|
+
check_vector(row['vector'], "rows[#{index}].vector")
|
|
85
|
+
source = row['_source']
|
|
86
|
+
if source.is_a?(Hash)
|
|
87
|
+
row['_source'] = source.each_with_object({}) do |(key, value), out|
|
|
88
|
+
name = key.to_s
|
|
89
|
+
out[SOURCE_ALIASES.fetch(name, name)] = value
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
row['vector'] = row['vector'].to_a
|
|
93
|
+
row
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def check_vector(vector, name)
|
|
97
|
+
unless vector.is_a?(Array) && vector.size >= 1 && vector.size <= MAX_VECTOR
|
|
98
|
+
raise invalid("#{name} must be an array of 1 to #{MAX_VECTOR} numbers")
|
|
99
|
+
end
|
|
100
|
+
return if vector.all? { |n| finite?(n) }
|
|
101
|
+
|
|
102
|
+
raise invalid("#{name} must contain only finite numbers")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def finite?(value)
|
|
106
|
+
value.is_a?(Numeric) && !value.is_a?(Complex) && (!value.respond_to?(:finite?) || value.finite?)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def invalid(message, context = {})
|
|
110
|
+
ApprovalChecks.invalid(message, context)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def call(route, body)
|
|
114
|
+
HttpTransport.request(config: @config, method: 'POST', path: route, body: body)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def path(collection, tail)
|
|
118
|
+
raise invalid('a vector collection name must start with _', collection: collection) unless collection.is_a?(String) && collection.start_with?('_')
|
|
119
|
+
|
|
120
|
+
slug = Studio.require_project_slug(@config, 'vectors')
|
|
121
|
+
UrlGrammar.project_path(slug, "vectors/#{::URI.encode_www_form_component(collection).gsub('+', '%20')}/#{tail}")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
data/lib/k9o/version.rb
CHANGED
data/lib/k9o.rb
CHANGED
|
@@ -31,6 +31,7 @@ require_relative 'k9o/http_transport'
|
|
|
31
31
|
require_relative 'k9o/data'
|
|
32
32
|
require_relative 'k9o/query'
|
|
33
33
|
require_relative 'k9o/projects'
|
|
34
|
+
require_relative 'k9o/locate'
|
|
34
35
|
require_relative 'k9o/gate'
|
|
35
36
|
require_relative 'k9o/studio'
|
|
36
37
|
require_relative 'k9o/compute'
|
|
@@ -42,6 +43,16 @@ require_relative 'k9o/approvals'
|
|
|
42
43
|
require_relative 'k9o/schema'
|
|
43
44
|
require_relative 'k9o/migrations'
|
|
44
45
|
require_relative 'k9o/functions'
|
|
46
|
+
require_relative 'k9o/schedules'
|
|
47
|
+
require_relative 'k9o/vectors'
|
|
48
|
+
require_relative 'k9o/metadata'
|
|
49
|
+
require_relative 'k9o/rampart'
|
|
50
|
+
require_relative 'k9o/moat'
|
|
51
|
+
require_relative 'k9o/custody'
|
|
52
|
+
require_relative 'k9o/analytics'
|
|
53
|
+
require_relative 'k9o/keys'
|
|
54
|
+
require_relative 'k9o/crypto'
|
|
55
|
+
require_relative 'k9o/end_users'
|
|
45
56
|
require_relative 'k9o/policies'
|
|
46
57
|
require_relative 'k9o/audit'
|
|
47
58
|
require_relative 'k9o/orgs'
|
|
@@ -50,6 +61,8 @@ require_relative 'k9o/org'
|
|
|
50
61
|
# kibo ZK BYOK/HYOK crypto — self-guards its native binding (LoadError-safe), so
|
|
51
62
|
# requiring k9o never breaks on a platform without the kibo native lib.
|
|
52
63
|
require_relative 'k9o/kibo'
|
|
64
|
+
require_relative 'k9o/kibo_client_core'
|
|
65
|
+
require_relative 'k9o/kibo_ffi_core'
|
|
53
66
|
# kibo vault REST surface (client.kibo.vault.secrets) —.
|
|
54
67
|
require_relative 'k9o/vault'
|
|
55
68
|
# Barbican guardrail SDK — self-guards its native binding (LoadError-safe), so
|
data/sig/k9o/approvals.rbs
CHANGED
|
@@ -11,17 +11,39 @@ module K9o
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
class Approvals
|
|
14
|
+
attr_reader comments: ApprovalComments
|
|
14
15
|
def initialize: (untyped config) -> void
|
|
15
|
-
#
|
|
16
|
-
|
|
16
|
+
# One page, newest first. `limit` is 1 to 200 (default 50); pass `meta.cursor.next` back as `cursor`
|
|
17
|
+
# (nil on the last page).
|
|
18
|
+
def list: (?status: (String | Symbol)?, ?limit: Integer?, ?cursor: String?) -> Envelope
|
|
17
19
|
def get: (String id) -> Envelope
|
|
18
20
|
# `status` on the answer stays "pending_approval" while more approvers are needed.
|
|
19
21
|
def approve: (String id, ?reason: String?) -> Envelope
|
|
20
22
|
def reject: (String id, reason: String) -> Envelope
|
|
21
|
-
#
|
|
23
|
+
# HTTP 200 even when rows fail: `data["failed"]` is an Array of { "id", "code", "reason" } (empty when none).
|
|
24
|
+
# Compare `affectedIds` with what you intended.
|
|
22
25
|
def bulk: (action: (String | Symbol), criteria: Hash[(String | Symbol), untyped], ?reason: String?) -> Envelope
|
|
23
26
|
end
|
|
24
27
|
|
|
28
|
+
# `approvals.comments`. Posting needs a session (a personal access token alone is refused, K9O_RBAC_DENY).
|
|
29
|
+
class ApprovalComments
|
|
30
|
+
VISIBILITIES: Array[String]
|
|
31
|
+
BODY_MAX: Integer
|
|
32
|
+
|
|
33
|
+
# An Envelope whose data is the Array of comments, oldest first; `next_cursor` is nil on the last page.
|
|
34
|
+
# A comment is { "id", "caseId", "authorId", "visibility", "body", "createdAt" }.
|
|
35
|
+
class Page < Envelope
|
|
36
|
+
attr_reader next_cursor: String?
|
|
37
|
+
def initialize: (data: untyped, meta: Meta, ?next_cursor: String?) -> void
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize: (untyped config) -> void
|
|
41
|
+
# `body` is 1 to 4000 characters; `visibility` is "everyone" or "approvers_only", fixed once posted. HTTP 201.
|
|
42
|
+
def add: (String pending_id, body: String, visibility: (String | Symbol)) -> Envelope
|
|
43
|
+
# `limit` is 1 to 200 (default 50); pass `next_cursor` back as `cursor`.
|
|
44
|
+
def list: (String pending_id, ?limit: Integer?, ?cursor: String?) -> Page
|
|
45
|
+
end
|
|
46
|
+
|
|
25
47
|
class ApprovalPolicies
|
|
26
48
|
COLLECTION_RE: Regexp
|
|
27
49
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# RBS signatures for `client.analytics` (datasets), `client.rollups` and `client.keys`.
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
class Download
|
|
5
|
+
attr_reader body: String
|
|
6
|
+
attr_reader content_type: String?
|
|
7
|
+
def initialize: (body: String, ?content_type: String?) -> void
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class AnalyticsRollups
|
|
11
|
+
def initialize: (untyped config) -> void
|
|
12
|
+
def create: (String dataset_slug, slug: String, source: String, measures: Array[Hash[Symbol | String, untyped]], window_seconds: Integer,
|
|
13
|
+
?dimensions: Array[String]?, ?subject_field: String?, ?non_personal: bool?, ?reattest_interval_days: Integer?) -> Envelope
|
|
14
|
+
def list: (String dataset_slug) -> Envelope
|
|
15
|
+
def delete: (String dataset_slug, String rollup_id) -> Envelope
|
|
16
|
+
# People only: a service, workload or extension token gets 403 K9O_RBAC_DENY (reason human_only).
|
|
17
|
+
def reattest: (String dataset_slug, String rollup_id, ?reattest_interval_days: Integer?) -> Envelope
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class AnalyticsSchedules
|
|
21
|
+
def initialize: (untyped config) -> void
|
|
22
|
+
def create: (String dataset_slug, from_collection: String, interval_seconds: Integer, ?reattest_interval_days: Integer?) -> Envelope
|
|
23
|
+
def list: (String dataset_slug) -> Envelope
|
|
24
|
+
def delete: (String dataset_slug, String schedule_id) -> Envelope
|
|
25
|
+
def reattest: (String dataset_slug, String schedule_id, ?reattest_interval_days: Integer?) -> Envelope
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Analytics
|
|
29
|
+
attr_reader rollups: AnalyticsRollups
|
|
30
|
+
attr_reader schedules: AnalyticsSchedules
|
|
31
|
+
def initialize: (untyped config) -> void
|
|
32
|
+
def list: () -> Envelope
|
|
33
|
+
def query: (String sql, Array[String] datasets) -> Envelope
|
|
34
|
+
def create: (String slug, display_name: String, columns: Array[Hash[Symbol | String, untyped]], residency_class: (String | Symbol),
|
|
35
|
+
?partition_by: Array[String]?, ?storage_target: Hash[Symbol | String, untyped]?) -> Envelope
|
|
36
|
+
def get: (String slug) -> Envelope
|
|
37
|
+
def delete: (String slug) -> Envelope
|
|
38
|
+
def materialize: (String slug, ?from_collection: String?, ?from_query: String?) -> Envelope
|
|
39
|
+
def read: (String slug, ?partition_filter: Hash[Symbol | String, untyped]?, ?cursor: String?, ?limit: Integer?) -> Envelope
|
|
40
|
+
def write: (String slug, (Array[Hash[Symbol | String, untyped]] | String) rows, ?on_error: (String | Symbol)?) -> Envelope
|
|
41
|
+
def upload: (String slug, (String | IO) data, format: (String | Symbol), ?filename: String, ?manifest: Hash[Symbol | String, untyped]?,
|
|
42
|
+
?dry_run: bool?, ?on_error: (String | Symbol)?, ?delimiter: String?, ?encoding: String?, ?has_header: bool?) -> Envelope
|
|
43
|
+
def download_original: (String slug) -> Download
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Rollups
|
|
47
|
+
def initialize: (untyped config) -> void
|
|
48
|
+
def list: () -> Envelope
|
|
49
|
+
def read: (String rollup_slug, ?from: (String | Time)?, ?to: (String | Time)?, ?dims: (Array[String] | String)?) -> Envelope
|
|
50
|
+
def subscribe: (String rollup_slug, ?from_sequence: Integer?, ?on_resync: (^(Hash[String, untyped]) -> untyped)?) -> Enumerator[Hash[String, untyped], untyped]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class Keys
|
|
54
|
+
STATUSES: Array[String]
|
|
55
|
+
def initialize: (untyped config) -> void
|
|
56
|
+
# data: { "items" => [key] } (not paged)
|
|
57
|
+
def list: (?status: (String | Symbol)?) -> Envelope
|
|
58
|
+
def get: (String key_id) -> Envelope
|
|
59
|
+
# data: { "keyId", "previousVersion", "newVersion", "status" => "rotated" }
|
|
60
|
+
# step_up: ^(StepUpChallenge, String, String) -> { method: String, proof: String }
|
|
61
|
+
def rotate: (String key_id, ?step_up: untyped, ?headers: Hash[String, String]?) -> Envelope
|
|
62
|
+
|
|
63
|
+
class StepUpChallenge < Struct[untyped]
|
|
64
|
+
attr_reader scope_set_root: String
|
|
65
|
+
attr_reader nonce: String
|
|
66
|
+
attr_reader min_tier: String?
|
|
67
|
+
attr_reader methods: Array[String]
|
|
68
|
+
attr_reader max_age_sec: Integer?
|
|
69
|
+
attr_reader single_use: bool?
|
|
70
|
+
attr_reader rp_id: String?
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class Crypto
|
|
75
|
+
def initialize: (untyped config) -> void
|
|
76
|
+
def encrypt: (String crypt, Array[Hash[Symbol | String, untyped] | String] items) -> Envelope
|
|
77
|
+
def decrypt: (String crypt, Array[Hash[Symbol | String, untyped] | String] items) -> Envelope
|
|
78
|
+
def rewrap: (String crypt, Array[Hash[Symbol | String, untyped] | String] items) -> Envelope
|
|
79
|
+
def self.text: (Hash[String, untyped] item) -> String?
|
|
80
|
+
# Reads the project's custody mode; every encrypt does this first, with no cache.
|
|
81
|
+
def mode: () -> Mode
|
|
82
|
+
# Registers the Kibo client core (an object with encrypt, decrypt and rewrap); nil removes it.
|
|
83
|
+
def use_client_core: (untyped core) -> untyped
|
|
84
|
+
def self.check_expected_key_refs: (Array[String]? refs) -> Array[String]?
|
|
85
|
+
def self.key_ref_matches?: (String key_ref, Array[String] expected, provider: (:aws | :gcp | :azure | :vault)) -> bool
|
|
86
|
+
|
|
87
|
+
# Ask for a short-lived federated token for +op+ ("encrypt", "decrypt" or "rewrap") on +crypt+.
|
|
88
|
+
def federated_token: (String crypt, String op) -> FederatedToken
|
|
89
|
+
|
|
90
|
+
# Crypts: data is { "crypts" => [...] } (list) or one { "name", "status", "createdAt", "retiredAt" } Hash.
|
|
91
|
+
def crypts_list: () -> Envelope
|
|
92
|
+
def crypts_create: (String name) -> Envelope
|
|
93
|
+
def crypts_retire: (String name) -> Envelope
|
|
94
|
+
|
|
95
|
+
class Mode < Struct[untyped]
|
|
96
|
+
def custody_mode: () -> String?
|
|
97
|
+
def epoch: () -> String?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# +provider+ and +project_id+ are nil unless the gateway names them; they are only checked against configuration.
|
|
101
|
+
class FederatedToken < Struct[untyped]
|
|
102
|
+
attr_reader token: String
|
|
103
|
+
attr_reader expires_at: String
|
|
104
|
+
attr_reader issuer: String
|
|
105
|
+
attr_reader subject: String
|
|
106
|
+
attr_reader audience: String
|
|
107
|
+
attr_reader kid: String
|
|
108
|
+
attr_reader provider: String?
|
|
109
|
+
attr_reader project_id: String?
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class Audit
|
|
114
|
+
# Yields { "auditEventId", "type", "actorType", "occurredAt", "outcome", "eventId" }.
|
|
115
|
+
def tail: (?types: (Array[String | Symbol] | String | Symbol)?, ?on_resync: (^(Hash[String, untyped]) -> untyped)?) -> Enumerator[Hash[String, untyped], untyped]
|
|
116
|
+
def replay: (?topic: String, ?after_audit_event_id: String?, ?from_sequence: Integer?, ?until_sequence: Integer?, ?limit: Integer?) -> Envelope
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class K9oClient
|
|
120
|
+
attr_reader analytics: Analytics
|
|
121
|
+
attr_reader rollups: Rollups
|
|
122
|
+
attr_reader keys: Keys
|
|
123
|
+
attr_reader crypto: Crypto
|
|
124
|
+
end
|
|
125
|
+
end
|
data/sig/k9o/gate.rbs
CHANGED
|
@@ -7,6 +7,8 @@ module K9o
|
|
|
7
7
|
# The refresh token is required: the gateway answers 400 without it.
|
|
8
8
|
def sign_out: (refresh_token: String) -> Envelope
|
|
9
9
|
def whoami: () -> Envelope
|
|
10
|
+
def revoke_all_sessions: () -> Envelope
|
|
11
|
+
# `data` is `{ accessToken, refreshToken, expiresAt }`; the refresh token sent stops working, so store the new one.
|
|
10
12
|
def refresh_token: (refresh_token: String) -> Envelope
|
|
11
13
|
end
|
|
12
14
|
end
|
data/sig/k9o/kerror.rbs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# RBS signatures for the error type raised by every call.
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
class KError < StandardError
|
|
5
|
+
attr_reader code: String
|
|
6
|
+
attr_reader context: Hash[untyped, untyped]
|
|
7
|
+
# Suggested next step as text, or nil.
|
|
8
|
+
attr_reader recommendation: String?
|
|
9
|
+
attr_reader status: Integer?
|
|
10
|
+
attr_reader request_id: String?
|
|
11
|
+
# Seconds the gateway asks the caller to wait before retrying, or nil.
|
|
12
|
+
attr_reader retry_after_sec: Numeric?
|
|
13
|
+
# The step-up challenge of a 401 K9O_STEP_UP_REQUIRED, or nil.
|
|
14
|
+
def step_up: () -> Keys::StepUpChallenge?
|
|
15
|
+
|
|
16
|
+
def initialize: (code: String, message: String, ?context: Hash[untyped, untyped], ?recommendation: String?, ?status: Integer?, ?request_id: String?, ?retry_after_sec: Numeric?) -> void
|
|
17
|
+
def with_recommendation: (String? rec) -> KError
|
|
18
|
+
def message: () -> String
|
|
19
|
+
def to_s: () -> String
|
|
20
|
+
def self.from_wire: (Hash[String, untyped] wire, Integer? status) -> KError
|
|
21
|
+
def self.recommendation_text: (untyped raw) -> String?
|
|
22
|
+
def self.retry_after: (untyped raw) -> Numeric?
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# RBS signatures for the Kibo client core of client.crypto (BYOK and HYOK projects).
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
class KiboClientCore
|
|
5
|
+
EXCHANGE_TIMEOUT: String
|
|
6
|
+
CORE_FUNCTIONS: Array[Symbol]
|
|
7
|
+
PROVIDER_WIRE: Hash[Symbol, String]
|
|
8
|
+
SKEW_SECS: Integer
|
|
9
|
+
DEFAULT_DEADLINE_SECS: Integer
|
|
10
|
+
|
|
11
|
+
# +core+ responds to app_crypto_seal_blocking, app_crypto_open_blocking, acquire_kms_credential_blocking and
|
|
12
|
+
# core_capabilities (the generated KiboKmsRestFfi module does). +federated_token+ answers call(crypt, op) with a
|
|
13
|
+
# Crypto::FederatedToken.
|
|
14
|
+
def initialize: (provider: (:aws | :gcp | :azure | :vault), posture: (:byok | :hyok), key_ref: Hash[Symbol, untyped],
|
|
15
|
+
federation: Hash[Symbol | String, untyped], expected_key_refs: Array[String], federated_token: untyped, core: untyped,
|
|
16
|
+
?project_id: String?, ?provider_config: Hash[Symbol | String, String?], ?exchange_deadline: Numeric,
|
|
17
|
+
?now: ^() -> Integer) -> void
|
|
18
|
+
def enforces_expected_key_refs?: () -> true
|
|
19
|
+
def encrypt: (String crypt, Array[Hash[Symbol, untyped]] items, **untyped options) -> Hash[String, Array[Hash[String, untyped]]]
|
|
20
|
+
def decrypt: (String crypt, Array[Hash[Symbol, untyped]] items, **untyped options) -> Hash[String, Array[Hash[String, untyped]]]
|
|
21
|
+
def rewrap: (String crypt, Array[Hash[Symbol, untyped]] items, **untyped options) -> Hash[String, Array[Hash[String, untyped]]]
|
|
22
|
+
def inspect: () -> String
|
|
23
|
+
def to_s: () -> String
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# RBS signatures for `client.metadata`.
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
class Metadata
|
|
5
|
+
# An Envelope whose data is the Array of rows; `cursor` is the next page's cursor, or nil on the last page.
|
|
6
|
+
# A row is { "surface", "scope", "recordId" (collection) or "eventId" (telemetry), "metadata" => { column => value } }.
|
|
7
|
+
class Page < Envelope
|
|
8
|
+
def cursor: () -> String?
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
OPS: Array[String]
|
|
12
|
+
METRICS: Array[String]
|
|
13
|
+
SLICES: Array[String]
|
|
14
|
+
PREFIX: String
|
|
15
|
+
MAX_COLUMNS: Integer
|
|
16
|
+
MAX_LIMIT: Integer
|
|
17
|
+
TELEMETRY_FIELDS: Array[String]
|
|
18
|
+
|
|
19
|
+
attr_reader vectors: MetadataVectors
|
|
20
|
+
attr_reader index: MetadataIndex
|
|
21
|
+
|
|
22
|
+
def initialize: (untyped config) -> void
|
|
23
|
+
# surfaces: ["collection:posts"]; where: [{ field:, op:, value: }]; limit 1..1000; cursor only with one surface.
|
|
24
|
+
# Options may be keyword arguments or one Hash.
|
|
25
|
+
def filter: (?Hash[String | Symbol, untyped]? input, ?surfaces: Array[String], ?where: Array[Hash[String | Symbol, untyped]]?, ?limit: Integer?, ?cursor: String?) -> Page
|
|
26
|
+
# data is the whole updated record. Columns may be keyword arguments or one Hash.
|
|
27
|
+
def set: (String | Symbol collection, String id, ?Hash[String | Symbol, untyped]? columns, **untyped extra) -> Envelope
|
|
28
|
+
def search: (?Hash[String | Symbol, untyped]? input, ?surface: String, ?field: String | Symbol, ?q: String, ?limit: Integer?) -> Page
|
|
29
|
+
# Yields { "topic", "eventType", "payload" => { "collection", "id" }, "occurredAt", "eventId"? }.
|
|
30
|
+
def subscribe: (String surface, ?Hash[String | Symbol, untyped]? input, ?from_sequence: Integer?, ?fromSequence: Integer?, ?on_resync: (^(Hash[String, untyped]) -> untyped)?) -> Enumerator[Hash[String, untyped], untyped]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class MetadataVectors
|
|
34
|
+
def initialize: (untyped config) -> void
|
|
35
|
+
# Rows carry no score; at most 50 rows come back whatever k is.
|
|
36
|
+
def nearest: (?Hash[String | Symbol, untyped]? input, ?surface: String, ?column: String | Symbol, ?embedding: Array[Numeric], ?k: Integer?, ?metric: (String | Symbol)?, ?where: Array[Hash[String | Symbol, untyped]]?) -> Metadata::Page
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class MetadataIndex
|
|
40
|
+
def initialize: (untyped config) -> void
|
|
41
|
+
# data is { "collection" => String, "columns" => [{ "column" => String, "indexed" => bool }] }.
|
|
42
|
+
def get: (String | Symbol collection) -> Envelope
|
|
43
|
+
def declare: (String | Symbol collection, Array[Hash[String | Symbol, untyped]] columns) -> Envelope
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Realtime
|
|
47
|
+
# The reconnecting reader behind subscribe; initial_query applies to the first connection only.
|
|
48
|
+
def feed: (String route, Hash[String, untyped] q, Hash[String, untyped]? initial_query, (^(Hash[String, untyped]) -> untyped)? on_resync) -> Enumerator[Hash[String, untyped], untyped]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class K9oClient
|
|
52
|
+
attr_reader metadata: Metadata
|
|
53
|
+
end
|
|
54
|
+
end
|
data/sig/k9o/moat.rbs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# RBS signatures for Moat, the local guard around a model call.
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
module Moat
|
|
5
|
+
REASONS: Array[Symbol]
|
|
6
|
+
|
|
7
|
+
# Raised when Moat refuses input or output. `reason` is :role_marker, :input_schema, :output_schema or :pii.
|
|
8
|
+
class Refusal < StandardError
|
|
9
|
+
attr_reader reason: Symbol
|
|
10
|
+
attr_reader detail: String
|
|
11
|
+
def initialize: (Symbol reason, String detail) -> void
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class ConfigError < ArgumentError
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class SchemaError < ConfigError
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.new: (prompt: String, ?input_schema: Hash[String | Symbol, untyped]?, ?output_schema: Hash[String | Symbol, untyped]?, ?filters: Hash[Symbol | String, ^(String) -> untyped]?) -> Guard
|
|
21
|
+
|
|
22
|
+
class Guard
|
|
23
|
+
def initialize: (prompt: String, ?input_schema: Hash[String | Symbol, untyped]?, ?output_schema: Hash[String | Symbol, untyped]?, ?filters: Hash[Symbol | String, ^(String) -> untyped]?) -> void
|
|
24
|
+
def messages: (untyped untrusted_input) -> Array[{ role: String, content: String }]
|
|
25
|
+
def check_output: (untyped model_output) -> untyped
|
|
26
|
+
def prompt_sha256: () -> String
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/sig/k9o/rampart.rbs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# RBS signatures for Rampart classification and receipt verification.
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
module Jcs
|
|
5
|
+
class Error < ArgumentError
|
|
6
|
+
attr_reader kind: Symbol
|
|
7
|
+
def initialize: (String message, ?Symbol kind) -> void
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.canonicalize: (untyped value) -> String
|
|
11
|
+
def self.canonical_bytes: (untyped value) -> String
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Ed25519
|
|
15
|
+
def self.verify: (String public_key, String message, String signature) -> bool
|
|
16
|
+
def self.native?: () -> bool
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Rampart
|
|
20
|
+
RECEIPT_TYP: String
|
|
21
|
+
CLASSIFICATIONS: Array[String]
|
|
22
|
+
DEFAULT_CLOCK_SKEW: Integer
|
|
23
|
+
REASONS: Array[Symbol]
|
|
24
|
+
|
|
25
|
+
# Raised by verify_receipt. `reason` is one of REASONS: :malformed, :key_unknown, :alg, :typ, :signature,
|
|
26
|
+
# :issuer, :project_unknown, :project, :expired, :not_yet_valid, :input_hash.
|
|
27
|
+
class ReceiptRefusal < KError
|
|
28
|
+
attr_reader reason: Symbol
|
|
29
|
+
def initialize: (Symbol reason, String detail) -> void
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Verdict < Struct[untyped]
|
|
33
|
+
attr_accessor classification: String
|
|
34
|
+
attr_accessor risk_bp: Integer
|
|
35
|
+
attr_accessor confidence_bp: Integer
|
|
36
|
+
attr_accessor categories: Array[String]
|
|
37
|
+
attr_accessor project_id: String
|
|
38
|
+
attr_accessor calibration_version: String
|
|
39
|
+
attr_accessor input_sha256: String
|
|
40
|
+
attr_accessor issued_at: Time
|
|
41
|
+
attr_accessor expires_at: Time
|
|
42
|
+
attr_accessor kid: String
|
|
43
|
+
attr_accessor windows: Integer?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def initialize: (untyped config) -> void
|
|
47
|
+
# data: { "classification", "riskScore", "confidence", "categories", "calibrationVersion", "classificationReceipt", "projectId" }.
|
|
48
|
+
def classify: (Hash[String | Symbol, untyped] input, ?label: String?) -> Envelope
|
|
49
|
+
# data: { "keys" => [ { "kid", "kty", "crv", "x", ... } ], "issuer" => "<issuer>/rampart" }. Public: no credential is sent.
|
|
50
|
+
def jwks: () -> Envelope
|
|
51
|
+
# `receipt` is the Envelope classify returned (it carries the receipt and its projectId) or the bare receipt String.
|
|
52
|
+
def verify_receipt: ((Envelope | String) receipt, Hash[String | Symbol, untyped] input, ?key_set: (Hash[untyped, untyped] | Array[Hash[untyped, untyped]])?, ?issuer: String?, ?project_id: String?, ?clock_skew: Numeric, ?clock: (Time | Numeric | ^() -> (Time | Numeric))?) -> Verdict
|
|
53
|
+
def input_digest: (Hash[String | Symbol, untyped] input) -> String
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class K9oClient
|
|
57
|
+
attr_reader rampart: Rampart
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# RBS signatures for `client.realtime`.
|
|
2
|
+
|
|
3
|
+
module K9o
|
|
4
|
+
class Realtime
|
|
5
|
+
DEFAULT_RETRY_MS: Integer
|
|
6
|
+
SEEN_LIMIT: Integer
|
|
7
|
+
|
|
8
|
+
# Seconds without any bytes after which a connection is abandoned (default 45).
|
|
9
|
+
def self.idle_timeout: () -> Numeric
|
|
10
|
+
def self.idle_timeout=: (Numeric) -> Numeric
|
|
11
|
+
# Longest reconnect delay in seconds (default 30).
|
|
12
|
+
def self.max_backoff: () -> Numeric
|
|
13
|
+
def self.max_backoff=: (Numeric) -> Numeric
|
|
14
|
+
def self.sleeper: () -> ^(Numeric) -> void
|
|
15
|
+
def self.sleeper=: (^(Numeric) -> void) -> ^(Numeric) -> void
|
|
16
|
+
|
|
17
|
+
def initialize: (untyped config) -> void
|
|
18
|
+
# Yields { "topic", "eventType", "payload", "occurredAt", "eventId" }. Reconnects with `Last-Event-ID`, drops
|
|
19
|
+
# events already delivered, and calls `on_resync` with { "reason", "action" } when the position is lost.
|
|
20
|
+
def subscribe: (String topic, ?filter: Hash[untyped, untyped]?, ?on_resync: (^(Hash[String, untyped]) -> untyped)?) -> Enumerator[Hash[String, untyped], untyped]
|
|
21
|
+
|
|
22
|
+
# POST /@<slug>/realtime/replay: data is { "events", "nextCursor", "hasMore" } plus "resync" when the topic has no stored history.
|
|
23
|
+
def replay: (String topic, ?from_sequence: Integer?, ?until_sequence: Integer?, ?limit: Integer?) -> Envelope
|
|
24
|
+
|
|
25
|
+
module Topic
|
|
26
|
+
def self.collection: (String collection) -> String
|
|
27
|
+
def self.workflow_runs: () -> String
|
|
28
|
+
def self.query_executed: (String query_id) -> String
|
|
29
|
+
def self.dashboard_refresh: (String dashboard_id) -> String
|
|
30
|
+
def self.function_invocations: (String function_id) -> String
|
|
31
|
+
def self.audit_org: (String event_family) -> String
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|