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.
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' => Base64.strict_encode64(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' => Base64.decode64(data['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' => Base64.strict_encode64(value) }
134
+ body = { 'value' => [value].pack('m0') }
136
135
  body['metadata'] = metadata if metadata
137
136
  HttpTransport.request(
138
137
  config: @config, method: 'PUT',
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module K9o
4
- VERSION = '1.4.0'
4
+ VERSION = '1.5.2'
5
5
  end
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
@@ -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
- # Not paginated: only `limit` (1 to 200, default 50) caps the result.
16
- def list: (?status: (String | Symbol)?, ?limit: Integer?) -> Envelope
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
- # Failed rows are skipped without a report: compare `affectedIds` with what you intended.
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
@@ -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
@@ -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