k9o 1.2.0 → 1.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -1
- data/README.md +359 -68
- data/lib/k9o/ai.rb +15 -0
- data/lib/k9o/approvals.rb +159 -0
- data/lib/k9o/audit.rb +12 -14
- data/lib/k9o/client.rb +11 -7
- data/lib/k9o/compute.rb +32 -15
- data/lib/k9o/data.rb +67 -1
- data/lib/k9o/functions.rb +48 -9
- data/lib/k9o/gate.rb +9 -18
- data/lib/k9o/http_transport.rb +10 -5
- data/lib/k9o/kibo.rb +3 -3
- data/lib/k9o/me.rb +27 -87
- data/lib/k9o/org.rb +81 -68
- data/lib/k9o/orgs.rb +1 -0
- data/lib/k9o/policies.rb +25 -6
- data/lib/k9o/projects.rb +43 -8
- data/lib/k9o/query.rb +174 -0
- data/lib/k9o/realtime.rb +13 -35
- data/lib/k9o/region.rb +20 -18
- data/lib/k9o/schema.rb +35 -0
- data/lib/k9o/streams.rb +94 -0
- data/lib/k9o/studio.rb +21 -11
- data/lib/k9o/url_grammar.rb +46 -20
- data/lib/k9o/vault.rb +76 -80
- data/lib/k9o/version.rb +1 -1
- data/lib/k9o.rb +4 -0
- data/sig/k9o/ai.rbs +4 -0
- data/sig/k9o/approvals.rbs +49 -0
- data/sig/k9o/gate.rbs +7 -6
- data/sig/k9o/query.rbs +26 -0
- data/sig/k9o/region.rbs +14 -3
- metadata +8 -2
data/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# k9o
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Kilimanjaro is an AI-native platform on a Governed Database. Its products are Keep (AI Security and Governance), Pulse (Data Intelligence), Gate (Customer Identity and Access Management) and Kibo (Keys and Secrets Management).
|
|
3
|
+
Client for Kilimanjaro. Read and write your data, store files, manage policies and migrations,
|
|
4
|
+
deploy and invoke agents or functions, and publish to and subscribe to streams and realtime events.
|
|
6
5
|
|
|
7
6
|
## Contents
|
|
8
7
|
|
|
@@ -11,8 +10,15 @@ Kilimanjaro is an AI-native platform on a Governed Database. Its products are Ke
|
|
|
11
10
|
- [Quick start](#quick-start)
|
|
12
11
|
- [Authentication and project context](#authentication-and-project-context)
|
|
13
12
|
- [Running queries](#running-queries)
|
|
13
|
+
- [Query with SQL](#query-with-sql)
|
|
14
|
+
- [Search](#search)
|
|
15
|
+
- [Streams](#streams)
|
|
16
|
+
- [Approvals](#approvals)
|
|
17
|
+
- [Approval policies](#approval-policies)
|
|
18
|
+
- [Schema collections](#schema-collections)
|
|
14
19
|
- [Handling errors](#handling-errors)
|
|
15
20
|
- [Appendix](#appendix)
|
|
21
|
+
- [About Kilimanjaro](#about-kilimanjaro)
|
|
16
22
|
- [License](#license)
|
|
17
23
|
|
|
18
24
|
## Links
|
|
@@ -25,13 +31,13 @@ Kilimanjaro is an AI-native platform on a Governed Database. Its products are Ke
|
|
|
25
31
|
## Install
|
|
26
32
|
|
|
27
33
|
```bash
|
|
28
|
-
gem install k9o
|
|
34
|
+
gem install k9o -v 1.4.0
|
|
29
35
|
```
|
|
30
36
|
|
|
31
37
|
Or add it to your `Gemfile`:
|
|
32
38
|
|
|
33
39
|
```ruby
|
|
34
|
-
gem "k9o"
|
|
40
|
+
gem "k9o", "1.4.0"
|
|
35
41
|
```
|
|
36
42
|
|
|
37
43
|
Requires Ruby 3.2 or newer. See [requirements](#requirements) for details.
|
|
@@ -57,11 +63,11 @@ k9o = K9o.create_client(
|
|
|
57
63
|
```ruby
|
|
58
64
|
new_task = { "title" => "Write the README", "done" => false }
|
|
59
65
|
created = k9o.from("tasks").insert(new_task)
|
|
60
|
-
puts "
|
|
66
|
+
puts "Output: #{created.data['title']}"
|
|
61
67
|
```
|
|
62
68
|
|
|
63
69
|
```text
|
|
64
|
-
|
|
70
|
+
Output: Write the README
|
|
65
71
|
```
|
|
66
72
|
|
|
67
73
|
### List tasks
|
|
@@ -69,12 +75,12 @@ created: Write the README
|
|
|
69
75
|
```ruby
|
|
70
76
|
response = k9o.from("tasks").select.eq("done", false).limit(10).execute
|
|
71
77
|
response.data.each do |task|
|
|
72
|
-
puts "#{task['title']} #{task['done']}"
|
|
78
|
+
puts "Output: #{task['title']} #{task['done']}"
|
|
73
79
|
end
|
|
74
80
|
```
|
|
75
81
|
|
|
76
82
|
```text
|
|
77
|
-
Write the README false
|
|
83
|
+
Output: Write the README false
|
|
78
84
|
```
|
|
79
85
|
|
|
80
86
|
### Update and delete a task
|
|
@@ -97,27 +103,28 @@ The client needs a key and, for project calls, an org and project.
|
|
|
97
103
|
| Option | What it does |
|
|
98
104
|
|---|---|
|
|
99
105
|
| `api_key:` | Bearer token sent with every request. |
|
|
100
|
-
| `org_slug:` | Organization.
|
|
106
|
+
| `org_slug:` | Organization. The SDK builds `https://<org_slug>.<host>` from the platform host. |
|
|
101
107
|
| `project_slug:` | Project. Required by every project-scoped namespace. |
|
|
102
108
|
|
|
103
109
|
See [client options](#client-options) for every option.
|
|
104
110
|
|
|
105
111
|
### Choose a host
|
|
106
112
|
|
|
107
|
-
Pick one of two forms.
|
|
113
|
+
The API host always comes from the base URL. Pick one of two forms.
|
|
108
114
|
|
|
109
|
-
- Default
|
|
110
|
-
-
|
|
115
|
+
- Default public host: pass the org slug, project slug and key.
|
|
116
|
+
- Any other platform, internal or air-gapped host: pass its `base_url` as well.
|
|
111
117
|
|
|
112
118
|
```ruby
|
|
113
119
|
k9o = K9o.create_client(
|
|
114
|
-
base_url: "https://
|
|
120
|
+
base_url: "https://api.corp.internal",
|
|
121
|
+
org_slug: "acme",
|
|
115
122
|
project_slug: "task-app",
|
|
116
123
|
api_key: ENV.fetch("K9O_API_KEY")
|
|
117
124
|
)
|
|
118
125
|
```
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
This client talks to `https://acme.api.corp.internal`. See [regional hosts](#regional-hosts) for the rules.
|
|
121
128
|
|
|
122
129
|
## Running queries
|
|
123
130
|
|
|
@@ -136,13 +143,193 @@ A sub-object the server did not send is `nil`, so use `&.` when it may be absent
|
|
|
136
143
|
```ruby
|
|
137
144
|
request_id = response.meta.context.request_id
|
|
138
145
|
total = response.meta.cursor.total_count
|
|
139
|
-
puts "#{request_id} #{total}"
|
|
146
|
+
puts "Output: #{request_id} #{total}"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
Output: req_01H... 1
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Query with SQL
|
|
154
|
+
|
|
155
|
+
`k9o.query(sql, params)` runs one read-only SQL statement and returns the rows in `data`.
|
|
156
|
+
It is read-only (a write is refused with `K9O_RAW_SQL_READ_ONLY`; change data through `from`), takes one statement per call,
|
|
157
|
+
applies row-level security, accepts at most 100000 characters of SQL and 1000 parameters, and needs the project editor role.
|
|
158
|
+
A reference to `dataset.<name>` reads an analytics dataset.
|
|
159
|
+
|
|
160
|
+
Pass an array to bind `$1`, `$2`, and so on, or a hash to bind `:name` placeholders. Values are bound by the database and can be a string, number, boolean or `nil`; serialise anything else (for example as JSON text) first.
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
rows = k9o.query("SELECT data->>'title' AS title FROM posts WHERE data->>'status' = :status", status: "published")
|
|
164
|
+
puts "Output: #{rows.data.first['title']}"
|
|
165
|
+
|
|
166
|
+
same = k9o.query("SELECT count(*) AS n FROM posts WHERE data->>'status' = $1", ["published"])
|
|
167
|
+
puts "Output: #{same.data.first['n']}"
|
|
140
168
|
```
|
|
141
169
|
|
|
142
170
|
```text
|
|
143
|
-
|
|
171
|
+
Output: Hello
|
|
172
|
+
Output: 12
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
A `:name` with no key in the hash raises `K9o::KError` with code `K9O_INVALID_FIELD_VALUE` before any request is sent.
|
|
176
|
+
|
|
177
|
+
## Search
|
|
178
|
+
|
|
179
|
+
`k9o.search(collection, column:, q:)` runs a full-text search over one text column, best matches first.
|
|
180
|
+
`language:` defaults to `english`; `type:` is `plain` (default), `phrase` or `websearch`. `limit:` and `cursor:` page the result.
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
found = k9o.search("articles", column: "body", q: "climate resilience", limit: 5)
|
|
184
|
+
puts "Output: #{found.data.length} #{found.data.first['title']}"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
Output: 5 Adapting to heat
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The same search is available on a query as `text_search(column, query, language: "english", type: "plain")`.
|
|
192
|
+
`vector_search(column, embedding, metric: "cosine", limit: nil)` finds the closest rows to an embedding, closest first;
|
|
193
|
+
`metric:` is `cosine`, `euclidean` or `dot`, and `limit:` is 1 to 1000.
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
vector = k9o.ai.embed(input: "heat waves").data["embeddings"].first
|
|
197
|
+
near = k9o.from("articles").select.vector_search("embedding", vector, limit: 3).execute
|
|
198
|
+
puts "Output: #{near.data.length}"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
Output: 3
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Streams
|
|
206
|
+
|
|
207
|
+
Publish events to a stream and tail them live. Publishing needs the project admin role; subscribing and replaying need admin or auditor.
|
|
208
|
+
`subscribe` reconnects from the last sequence when the server closes the connection, so no event is repeated or skipped.
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
stream = k9o.streams.create("Orders feed", 1_073_741_824)
|
|
212
|
+
slug = stream.data["slug"]
|
|
213
|
+
puts "Output: #{slug}"
|
|
214
|
+
k9o.streams.publish(slug, { "order" => 1001 }, subject: "eu.new")
|
|
215
|
+
|
|
216
|
+
k9o.streams.subscribe(slug).each do |event|
|
|
217
|
+
puts "Output: #{event["sequence"]} #{event["data"]["order"]}"
|
|
218
|
+
break
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```text
|
|
223
|
+
Output: orders-feed
|
|
224
|
+
Output: 1 1001
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`replay` reads a bounded range of past events.
|
|
228
|
+
Failures raise `K9o::KError`: `K9O_STREAM_UNAVAILABLE` while streams are not available, and `K9O_STREAM_INVALID` for a bad subject.
|
|
229
|
+
|
|
230
|
+
## Approvals
|
|
231
|
+
|
|
232
|
+
Writes that need approval wait in a queue for a second person. `k9o.approvals` reads that queue and decides its changes.
|
|
233
|
+
Project admins and auditors see every change; other members see only the ones they proposed.
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
queue = k9o.approvals.list(status: "pending_approval", limit: 20)
|
|
237
|
+
change = queue.data.first
|
|
238
|
+
|
|
239
|
+
puts "Output: #{change["id"]} #{change["operation"]} #{change["collection"]}"
|
|
240
|
+
|
|
241
|
+
decided = k9o.approvals.approve(change["id"], reason: "checked the amount")
|
|
242
|
+
|
|
243
|
+
puts "Output: #{decided.data["status"]}"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
```text
|
|
247
|
+
Output: apr_01j9x3 update orders
|
|
248
|
+
Output: redeemed
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
- A change is `pending_approval`, `approved`, `rejected`, `expired` or `redeemed` (applied). An `expired` change can still read `pending_approval` until someone decides it.
|
|
252
|
+
- `approve` can answer 200 with `status` still `pending_approval`. That means your approval is recorded and more approvers are needed; the change is not applied yet.
|
|
253
|
+
- `reject(id, reason:)` needs a non-empty `reason`; the SDK checks it before sending.
|
|
254
|
+
- `list` is not paginated: it returns the newest `limit` changes (1 to 200, default 50). `get(id)` reads one.
|
|
255
|
+
- Deciding needs the queue-admin role (project admin or auditor).
|
|
256
|
+
|
|
257
|
+
Decide many at once with `bulk`. `criteria` is a predicate over each pending change (see [Approval policies](#approval-policies)):
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
run = k9o.approvals.bulk(
|
|
261
|
+
action: "reject",
|
|
262
|
+
criteria: { "eq" => ["change.table", "orders"] },
|
|
263
|
+
reason: "stale"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
puts "Output: #{run.data["affectedCount"]} #{run.data["affectedIds"].join(",")}"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
```text
|
|
270
|
+
Output: 2 apr_01j9x3,apr_01j9x4
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- A row that fails (a conflict, an apply error) is skipped silently and left out of `affectedIds`. Compare `affectedIds` with the set you intended.
|
|
274
|
+
- `reject` needs a `reason`; `criteria` must be a predicate hash. `attestationId` identifies the batch, even when nothing matched.
|
|
275
|
+
- It considers the newest 10000 pending changes. Bulk approve is not guaranteed to apply every matched row.
|
|
276
|
+
|
|
277
|
+
## Approval policies
|
|
278
|
+
|
|
279
|
+
A policy decides what happens to writes on one collection that match a predicate: `queue` them for approval, or `auto_approve` them.
|
|
280
|
+
For a write the first matching policy, oldest first, decides. Policies are evaluated for writes sent with the `requireApproval=true` query parameter.
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
policy = k9o.approval_policies.create(
|
|
284
|
+
collection: "orders",
|
|
285
|
+
operation: "update",
|
|
286
|
+
action: "queue",
|
|
287
|
+
predicate: { "gt" => ["change.row.amount", 1000] }
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
puts "Output: #{policy.data["id"]} #{policy.data["action"]}"
|
|
291
|
+
|
|
292
|
+
removed = k9o.approval_policies.delete(policy.data["id"])
|
|
293
|
+
|
|
294
|
+
puts "Output: #{removed.data["id"]}"
|
|
144
295
|
```
|
|
145
296
|
|
|
297
|
+
```text
|
|
298
|
+
Output: apo_01j9x5 queue
|
|
299
|
+
Output: apo_01j9x5
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
- `operation` is `insert`, `update` or `delete`; `action` is `auto_approve` or `queue`.
|
|
303
|
+
- `collection` matches `^[a-zA-Z_][a-zA-Z0-9_]{0,62}$` and `predicate` is a non-null hash. The SDK checks both before sending.
|
|
304
|
+
- A predicate has one operator key: `and`, `or`, `not`, `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `not_in`, `in_cidr`, `not_in_cidr` or `contains`.
|
|
305
|
+
`and` and `or` take an array of predicates, `not` takes one, and a comparison takes `[left, right]`.
|
|
306
|
+
- Operands start with `maker.` or `change.` (for example `change.row.amount`, `change.table`, `change.operation`, `maker.id`).
|
|
307
|
+
- `list` returns all policies. Identical policies are not de-duplicated. Creating and deleting need project admin or author.
|
|
308
|
+
|
|
309
|
+
## Schema collections
|
|
310
|
+
|
|
311
|
+
`k9o.schema.collections.list` lists the project's collections by name, with their size and last migration.
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
page = k9o.schema.collections.list(limit: 2)
|
|
315
|
+
|
|
316
|
+
page.data.each do |collection|
|
|
317
|
+
puts "Output: #{collection["name"]} #{collection["rowCount"]} rows, #{collection["columnCount"]} columns"
|
|
318
|
+
end
|
|
319
|
+
puts "Output: next #{page.meta.cursor.next}"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
```text
|
|
323
|
+
Output: orders 120 rows, 8 columns
|
|
324
|
+
Output: tasks 14 rows, 4 columns
|
|
325
|
+
Output: next dGFza3M
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
- Pass `meta.cursor.next` as `cursor` for the next page; it is `nil` on the last page. `limit` is 1 to 200 (default 50).
|
|
329
|
+
- A row is `name`, `rowCount`, `rowCountIsExact`, `columnCount`, `indexCount`, `lastMigrationSlug` and `lastMigrationAppliedAt`.
|
|
330
|
+
- `rowCount` is a live count that skips soft-deleted rows and ignores row-level security. Without an `org_id` claim on the token, `meta.warnings` says so.
|
|
331
|
+
- Project admins, auditors, editors, members and viewers can read it.
|
|
332
|
+
|
|
146
333
|
## Handling errors
|
|
147
334
|
|
|
148
335
|
Failures raise `K9o::KError`, a subclass of `StandardError`. Branch on `code`.
|
|
@@ -151,14 +338,14 @@ Failures raise `K9o::KError`, a subclass of `StandardError`. Branch on `code`.
|
|
|
151
338
|
begin
|
|
152
339
|
k9o.from("missing").select.execute
|
|
153
340
|
rescue K9o::KError => error
|
|
154
|
-
puts "#{error.code} #{error.status} #{error.request_id}"
|
|
155
|
-
puts error.message
|
|
341
|
+
puts "Output: #{error.code} #{error.status} #{error.request_id}"
|
|
342
|
+
puts "Output: #{error.message}"
|
|
156
343
|
end
|
|
157
344
|
```
|
|
158
345
|
|
|
159
346
|
```text
|
|
160
|
-
K9O_NOT_FOUND 404 req_01H...
|
|
161
|
-
Collection not found
|
|
347
|
+
Output: K9O_NOT_FOUND 404 req_01H...
|
|
348
|
+
Output: Collection not found
|
|
162
349
|
```
|
|
163
350
|
|
|
164
351
|
- See [error attributes](#error-attributes) for every field.
|
|
@@ -179,9 +366,9 @@ Collection not found
|
|
|
179
366
|
| Option | What it does |
|
|
180
367
|
|---|---|
|
|
181
368
|
| `api_key:` | Bearer token sent with every request. |
|
|
182
|
-
| `org_slug:` | Organization.
|
|
369
|
+
| `org_slug:` | Organization. Prefixed to a platform host (`api.<apex>`) to form the org host. |
|
|
183
370
|
| `project_slug:` | Project. Required by every project-scoped namespace. |
|
|
184
|
-
| `base_url:` | Scheme and host only. Defaults to `https://api.k9o.io`. |
|
|
371
|
+
| `base_url:` | Scheme and host only. Defaults to the public host `https://api.k9o.io`. |
|
|
185
372
|
| `default_headers:` | Extra headers added to every request. |
|
|
186
373
|
| `http_client:` | A `Net::HTTP`-compatible object used instead of the default connection. |
|
|
187
374
|
|
|
@@ -228,6 +415,8 @@ Chain filters before `execute`. Values are matched against one column.
|
|
|
228
415
|
| `limit(n)` | Maximum rows to return. |
|
|
229
416
|
| `cursor(token)` | Continue from `meta.cursor.next`. |
|
|
230
417
|
| `count(mode)` | Row count mode: `none`, `exact` or `estimated`. |
|
|
418
|
+
| `text_search(column, query, language: "english", type: "plain")` | Full-text search over one column. |
|
|
419
|
+
| `vector_search(column, embedding, metric: "cosine", limit: nil)` | Nearest rows to an embedding, closest first. |
|
|
231
420
|
|
|
232
421
|
#### Projects
|
|
233
422
|
|
|
@@ -236,9 +425,18 @@ Chain filters before `execute`. Values are matched against one column.
|
|
|
236
425
|
| Method | What it does |
|
|
237
426
|
|---|---|
|
|
238
427
|
| `k9o.projects.list` | List projects you can access. |
|
|
239
|
-
| `k9o.projects.create` | Create a project from `slug`
|
|
428
|
+
| `k9o.projects.create` | Create a project from `display_name:` (optional `slug:`, `region:`, `custody_mode:`, more). |
|
|
240
429
|
| `k9o.projects.get` | Read a project by slug. |
|
|
241
|
-
| `k9o.projects.update` | Change a project
|
|
430
|
+
| `k9o.projects.update` | Change a project by slug: `display_name:`, `slug:` (rename), `description:`, `region:`, more. |
|
|
431
|
+
|
|
432
|
+
```ruby
|
|
433
|
+
project = k9o.projects.create(display_name: "Task app", slug: "task-app")
|
|
434
|
+
puts "Output: #{project.data["slug"]}"
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
```text
|
|
438
|
+
Output: task-app
|
|
439
|
+
```
|
|
242
440
|
|
|
243
441
|
#### Gate (end-user sign-in)
|
|
244
442
|
|
|
@@ -248,20 +446,18 @@ Chain filters before `execute`. Values are matched against one column.
|
|
|
248
446
|
|---|---|
|
|
249
447
|
| `k9o.gate.sign_up` | Create an end user from email and password. |
|
|
250
448
|
| `k9o.gate.sign_in` | Sign in with email and password. |
|
|
251
|
-
| `k9o.gate.sign_out` | Revoke
|
|
449
|
+
| `k9o.gate.sign_out` | Revoke a session. Requires `refresh_token:`. |
|
|
252
450
|
| `k9o.gate.whoami` | Return the signed-in identity. |
|
|
253
451
|
| `k9o.gate.refresh_token` | Exchange a refresh token for a new access token. |
|
|
254
|
-
| `k9o.gate.verify_session` | Check that the current session is valid. |
|
|
255
|
-
| `k9o.gate.hosted` | Return the hosted sign-in `signInUrl` and `rpId`. |
|
|
256
452
|
|
|
257
453
|
```ruby
|
|
258
454
|
session = k9o.gate.sign_in(email: "ada@example.com", password: "correct-horse")
|
|
259
455
|
user = session.data["user"]
|
|
260
|
-
puts user["email"]
|
|
456
|
+
puts "Output: #{user["email"]}"
|
|
261
457
|
```
|
|
262
458
|
|
|
263
459
|
```text
|
|
264
|
-
ada@example.com
|
|
460
|
+
Output: ada@example.com
|
|
265
461
|
```
|
|
266
462
|
|
|
267
463
|
#### Storage
|
|
@@ -303,11 +499,32 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
303
499
|
|
|
304
500
|
| Method | What it does |
|
|
305
501
|
|---|---|
|
|
306
|
-
| `k9o.functions.
|
|
307
|
-
| `k9o.functions.
|
|
502
|
+
| `k9o.functions.upload_artifact` | Upload the build (`bytes`, `kind` of `"wasm"` or `"tar"`). Returns `artifactKey`. |
|
|
503
|
+
| `k9o.functions.deploy` | Deploy from `name:`, `runtime:`, `tier:`, `artifact_key:`, `version:`, optional `description:`. |
|
|
504
|
+
| `k9o.functions.list` | List functions. Options: `status:`, `limit:`, `cursor:`. |
|
|
308
505
|
| `k9o.functions.get` | Read one function by name. |
|
|
309
506
|
| `k9o.functions.delete` | Remove a function. |
|
|
310
|
-
| `k9o.functions.invoke` | Run a function
|
|
507
|
+
| `k9o.functions.invoke` | Run a function. The `payload` argument is sent as `{payload}`. |
|
|
508
|
+
|
|
509
|
+
`tier:` is `"sandbox"` (an OCI image archive, `"tar"`, up to 256 MiB). The `gateway` tier is for functions owned by platform operators and is not available to most projects. Sandbox functions run in a [Firelock](https://github.com/firelock-sandbox/firelock) microVM sandbox.
|
|
510
|
+
|
|
511
|
+
```ruby
|
|
512
|
+
artifact = k9o.functions.upload_artifact(File.binread("hello.tar"), "tar")
|
|
513
|
+
|
|
514
|
+
k9o.functions.deploy(
|
|
515
|
+
name: "hello",
|
|
516
|
+
runtime: "bun",
|
|
517
|
+
tier: "sandbox",
|
|
518
|
+
artifact_key: artifact.data["artifactKey"],
|
|
519
|
+
version: "1.0.0"
|
|
520
|
+
)
|
|
521
|
+
result = k9o.functions.invoke("hello", { "name" => "Ada" })
|
|
522
|
+
puts "Output: #{result.data["greeting"]}"
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
```text
|
|
526
|
+
Output: Hello, Ada
|
|
527
|
+
```
|
|
311
528
|
|
|
312
529
|
#### Migrations
|
|
313
530
|
|
|
@@ -331,11 +548,11 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
331
548
|
| Method | What it does |
|
|
332
549
|
|---|---|
|
|
333
550
|
| `k9o.policies.current` | Read the published bundle. |
|
|
334
|
-
| `k9o.policies.list` | List bundles. |
|
|
335
|
-
| `k9o.policies.create` | Create a bundle from `
|
|
336
|
-
| `k9o.policies.validate` | Check rules without saving. |
|
|
551
|
+
| `k9o.policies.list` | List bundles. Only option: `status:`. |
|
|
552
|
+
| `k9o.policies.create` | Create a bundle from a `policy:` document (optional `rules:`, `editor_mode:`). |
|
|
553
|
+
| `k9o.policies.validate` | Check a `policy:` document (and optional `rules:`) without saving. |
|
|
337
554
|
| `k9o.policies.get` | Read one bundle. |
|
|
338
|
-
| `k9o.policies.update` |
|
|
555
|
+
| `k9o.policies.update` | Replace a bundle's `policy:` (optional `rules:`, `editor_mode:`). |
|
|
339
556
|
| `k9o.policies.delete` | Remove a bundle. |
|
|
340
557
|
| `k9o.policies.publish` | Make a bundle current. |
|
|
341
558
|
|
|
@@ -345,7 +562,9 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
345
562
|
|
|
346
563
|
| Method | What it does |
|
|
347
564
|
|---|---|
|
|
348
|
-
| `k9o.audit.query` |
|
|
565
|
+
| `k9o.audit.query` | Filter by `event_type:`, `actor_id:`, `after:`, `before:`, `limit:`, `cursor:`. |
|
|
566
|
+
|
|
567
|
+
`after:` and `before:` are ISO 8601 timestamps.
|
|
349
568
|
|
|
350
569
|
#### AI embeddings
|
|
351
570
|
|
|
@@ -354,6 +573,21 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
354
573
|
| Method | What it does |
|
|
355
574
|
|---|---|
|
|
356
575
|
| `k9o.ai.embed(input:, model: nil)` | Embed one string or a list of strings. |
|
|
576
|
+
| `k9o.ai.generate(prompt, model: nil, max_tokens: nil, temperature: nil, usage_type: nil)` | Generate text from a prompt. |
|
|
577
|
+
|
|
578
|
+
##### Generate text
|
|
579
|
+
|
|
580
|
+
`model` is optional: without it the org's default model is used. Models from outside providers answer 501 until built.
|
|
581
|
+
The result `data` is the same as `compute.llms.invoke`.
|
|
582
|
+
|
|
583
|
+
```ruby
|
|
584
|
+
result = k9o.ai.generate("Say hi in three words", max_tokens: 20)
|
|
585
|
+
puts "Output: #{result.data['content']}"
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
```text
|
|
589
|
+
Output: Hi there, friend!
|
|
590
|
+
```
|
|
357
591
|
|
|
358
592
|
#### Language models
|
|
359
593
|
|
|
@@ -364,7 +598,18 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
364
598
|
| `k9o.compute.llms.list` | List available models. |
|
|
365
599
|
| `k9o.compute.llms.read` | Read one model. |
|
|
366
600
|
| `k9o.compute.llms.invoke` | Send a prompt and get the reply. |
|
|
367
|
-
| `k9o.compute.llms.invoke_stream` | Stream the reply
|
|
601
|
+
| `k9o.compute.llms.invoke_stream` | Stream the reply chunk by chunk (returns an `Enumerator`). |
|
|
602
|
+
|
|
603
|
+
Pass settings in `params:` (`max_tokens`, `temperature`, `usage_type`). The result `data` has `content` and `usage`.
|
|
604
|
+
|
|
605
|
+
```ruby
|
|
606
|
+
reply = k9o.compute.llms.invoke("llama-3-8b", prompt: "Say hi", params: { max_tokens: 20 })
|
|
607
|
+
puts "Output: #{reply.data['content']} #{reply.data['usage']['totalTokens']}"
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
```text
|
|
611
|
+
Output: Hi there! 14
|
|
612
|
+
```
|
|
368
613
|
|
|
369
614
|
#### Realtime
|
|
370
615
|
|
|
@@ -372,12 +617,52 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
372
617
|
|
|
373
618
|
| Method | What it does |
|
|
374
619
|
|---|---|
|
|
375
|
-
| `k9o.realtime.subscribe` | Stream live events (returns an `Enumerator`). |
|
|
376
|
-
| `k9o.realtime.replay` | Read past events from a sequence number. |
|
|
377
|
-
| `k9o.realtime.subscribe_poll` | Poll for events from a sequence number. |
|
|
620
|
+
| `k9o.realtime.subscribe` | Stream live events over SSE (returns an `Enumerator`). |
|
|
378
621
|
|
|
379
622
|
`K9o::Realtime::Topic` builds topic names, for example `Topic.collection("tasks")`.
|
|
380
623
|
|
|
624
|
+
#### Streams
|
|
625
|
+
|
|
626
|
+
`k9o.streams` publishes to and subscribes to the project's user streams. It needs `project_slug`.
|
|
627
|
+
|
|
628
|
+
| Method | What it does |
|
|
629
|
+
|---|---|
|
|
630
|
+
| `k9o.streams.create(display_name, max_bytes, retention_seconds: nil)` | Create a stream. The slug comes from the name. |
|
|
631
|
+
| `k9o.streams.list` | List the project's streams. |
|
|
632
|
+
| `k9o.streams.get(slug)` | Read one stream. |
|
|
633
|
+
| `k9o.streams.delete(slug)` | Retire a stream. |
|
|
634
|
+
| `k9o.streams.publish(slug, data, subject: nil)` | Append an event. `subject` is a dot-separated routing suffix. |
|
|
635
|
+
| `k9o.streams.replay(slug, from_sequence: nil, until_sequence: nil, limit: nil)` | Read past events: `events`, `nextCursor`, `hasMore`. |
|
|
636
|
+
| `k9o.streams.subscribe(slug, from_sequence: nil)` | Tail live events over SSE (an `Enumerator`). Reconnects from the last sequence. |
|
|
637
|
+
|
|
638
|
+
#### Approvals
|
|
639
|
+
|
|
640
|
+
`k9o.approvals` reads and decides the queue of changes waiting for approval. It needs `project_slug`.
|
|
641
|
+
|
|
642
|
+
| Method | What it does |
|
|
643
|
+
|---|---|
|
|
644
|
+
| `k9o.approvals.list(status: nil, limit: nil)` | Newest pending changes first. Not paginated; `limit` is 1 to 200. |
|
|
645
|
+
| `k9o.approvals.get(id)` | Read one change. |
|
|
646
|
+
| `k9o.approvals.approve(id, reason: nil)` | Approve. `status` can stay `pending_approval` while more approvers are needed. |
|
|
647
|
+
| `k9o.approvals.reject(id, reason:)` | Reject. `reason` is required. |
|
|
648
|
+
| `k9o.approvals.bulk(action:, criteria:, reason: nil)` | Approve or reject every change matching a predicate. `reason` is required to reject. Failed rows are skipped. |
|
|
649
|
+
|
|
650
|
+
#### Approval policies
|
|
651
|
+
|
|
652
|
+
`k9o.approval_policies` holds the rules that decide which writes are queued. It needs `project_slug`.
|
|
653
|
+
|
|
654
|
+
| Method | What it does |
|
|
655
|
+
|---|---|
|
|
656
|
+
| `k9o.approval_policies.list` | All policies, oldest first. |
|
|
657
|
+
| `k9o.approval_policies.create(collection:, operation:, action:, predicate:)` | Create a policy. `action` is `auto_approve` or `queue`. |
|
|
658
|
+
| `k9o.approval_policies.delete(id)` | Delete a policy. |
|
|
659
|
+
|
|
660
|
+
#### Schema
|
|
661
|
+
|
|
662
|
+
| Method | What it does |
|
|
663
|
+
|---|---|
|
|
664
|
+
| `k9o.schema.collections.list(limit: nil, cursor: nil)` | List the project's collections by name, with `meta.cursor.next` paging. |
|
|
665
|
+
|
|
381
666
|
#### Studio
|
|
382
667
|
|
|
383
668
|
`k9o.studio` manages workflows and dashboards.
|
|
@@ -391,7 +676,7 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
391
676
|
| `k9o.studio.workflows.read` | Read one workflow. |
|
|
392
677
|
| `k9o.studio.workflows.update` | Change a workflow. |
|
|
393
678
|
| `k9o.studio.workflows.delete` | Delete a workflow. |
|
|
394
|
-
| `k9o.studio.workflows.trigger` | Start a run. |
|
|
679
|
+
| `k9o.studio.workflows.trigger` | Start a run, with an optional context hash. |
|
|
395
680
|
| `k9o.studio.workflows.runs.list` | List a workflow's runs. |
|
|
396
681
|
| `k9o.studio.workflows.runs.read` | Read one run. |
|
|
397
682
|
| `k9o.studio.workflows.runs.cancel` | Cancel a run. |
|
|
@@ -415,19 +700,13 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
415
700
|
| Method | What it does |
|
|
416
701
|
|---|---|
|
|
417
702
|
| `k9o.me.profile.get` | Read the profile. |
|
|
418
|
-
| `k9o.me.profile.update` | Change
|
|
703
|
+
| `k9o.me.profile.update` | Change `display_name:`, `locale:` or `timezone:` (only those given). |
|
|
419
704
|
| `k9o.me.sessions.list` | List active sessions. |
|
|
420
705
|
| `k9o.me.sessions.revoke` | End one session. |
|
|
421
706
|
| `k9o.me.sessions.revoke_all` | End all sessions. |
|
|
422
707
|
| `k9o.me.api_tokens.list` | List API tokens. |
|
|
423
|
-
| `k9o.me.api_tokens.create` | Create
|
|
708
|
+
| `k9o.me.api_tokens.create` | Create a token from `label:` and `scope_manifest:` (optional `ttl_seconds:`). |
|
|
424
709
|
| `k9o.me.api_tokens.delete` | Delete an API token. |
|
|
425
|
-
| `k9o.me.notifications.list` | List notifications. |
|
|
426
|
-
| `k9o.me.notifications.mark_read` | Mark one as read. |
|
|
427
|
-
| `k9o.me.notifications.mark_all_read` | Mark all as read. |
|
|
428
|
-
| `k9o.me.audit_log.query` | Search your own audit events. |
|
|
429
|
-
| `k9o.me.data_export.request` | Request a data export. |
|
|
430
|
-
| `k9o.me.data_export.status` | Check an export job. |
|
|
431
710
|
|
|
432
711
|
#### Organization
|
|
433
712
|
|
|
@@ -447,30 +726,26 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
447
726
|
| Method | What it does |
|
|
448
727
|
|---|---|
|
|
449
728
|
| `k9o.org.get` | Read the active organization. |
|
|
450
|
-
| `k9o.org.members.list` | List members. |
|
|
729
|
+
| `k9o.org.members.list` | List members. Options: `user_id:`, `role:`, `acceptance_status:`, `status:`, `state:`. |
|
|
451
730
|
| `k9o.org.members.invite` | Invite a member by email and role. |
|
|
452
|
-
| `k9o.org.members.get` | Read one member. |
|
|
453
731
|
| `k9o.org.members.remove` | Remove a member. |
|
|
454
|
-
| `k9o.org.groups.list` | List groups
|
|
455
|
-
| `k9o.org.groups.create` | Create a group. |
|
|
732
|
+
| `k9o.org.groups.list` | List groups, optionally for one `user_id:`. |
|
|
456
733
|
| `k9o.org.groups.get` | Read one group. |
|
|
457
|
-
| `k9o.org.
|
|
458
|
-
| `k9o.org.audit_log.query` | Search organization audit events. |
|
|
734
|
+
| `k9o.org.audit_log.query` | Search organization audit events by `type:` and `limit:`. |
|
|
459
735
|
| `k9o.org.gate.roles.list` | List role grants. |
|
|
460
|
-
| `k9o.org.gate.roles.grant` | Grant a role to a user. |
|
|
736
|
+
| `k9o.org.gate.roles.grant` | Grant a role to a user (`user_id:`, `role:`, optional `decision_reason:`). |
|
|
461
737
|
| `k9o.org.gate.roles.get` | Read one grant. |
|
|
462
738
|
| `k9o.org.gate.roles.revoke` | Revoke a grant. |
|
|
463
739
|
| `k9o.org.gate.idp_providers.list` | List identity providers. |
|
|
464
|
-
| `k9o.org.gate.idp_providers.create` | Add
|
|
740
|
+
| `k9o.org.gate.idp_providers.create` | Add a provider: `idp_provider:`, `display_name:`, `client_id:`, `client_secret_ref:`, `tier_gating:`. |
|
|
465
741
|
| `k9o.org.gate.idp_providers.get` | Read one provider. |
|
|
466
742
|
| `k9o.org.gate.idp_providers.configure` | Change a provider. |
|
|
467
743
|
| `k9o.org.gate.idp_providers.delete` | Remove a provider. |
|
|
468
|
-
| `k9o.org.smtp.
|
|
469
|
-
| `k9o.org.smtp.
|
|
470
|
-
| `k9o.org.smtp.test` | Send a test email. |
|
|
744
|
+
| `k9o.org.smtp.set` | Save the mail server: `host:`, `port:`, `username:`, `password_ref:`, `from_address:`. |
|
|
745
|
+
| `k9o.org.smtp.test` | Send a test email to `to:`, with an optional `subject:`. |
|
|
471
746
|
| `k9o.org.smtp.unset` | Remove the mail server settings. |
|
|
472
747
|
| `k9o.org.default_llm.get` | Read the default model. |
|
|
473
|
-
| `k9o.org.default_llm.set` | Set the default model. |
|
|
748
|
+
| `k9o.org.default_llm.set` | Set the default model from a model id. |
|
|
474
749
|
|
|
475
750
|
#### Vault secrets
|
|
476
751
|
|
|
@@ -479,7 +754,7 @@ signed = avatars.create_signed_url("ada.png", expires_in: 300)
|
|
|
479
754
|
| Method | What it does |
|
|
480
755
|
|---|---|
|
|
481
756
|
| `k9o.kibo.vault.secrets.list` | List secrets by prefix. |
|
|
482
|
-
| `k9o.kibo.vault.secrets.get` | Read a secret
|
|
757
|
+
| `k9o.kibo.vault.secrets.get` | Read a secret by name (`region:` is required). |
|
|
483
758
|
| `k9o.kibo.vault.secrets.create` | Store a new secret. |
|
|
484
759
|
| `k9o.kibo.vault.secrets.reveal_challenge` | Get a one-time challenge for revealing. |
|
|
485
760
|
| `k9o.kibo.vault.secrets.reveal` | Reveal the value using the challenge. |
|
|
@@ -534,10 +809,12 @@ The server may send codes not listed here, so keep a fallback branch.
|
|
|
534
809
|
|
|
535
810
|
### Regional hosts
|
|
536
811
|
|
|
537
|
-
-
|
|
538
|
-
-
|
|
539
|
-
-
|
|
540
|
-
-
|
|
812
|
+
- A platform host is `api.<apex>` or `api-staging.<apex>`, on any apex.
|
|
813
|
+
- With `org_slug`, the SDK builds the org host `<org_slug>.<host>`.
|
|
814
|
+
- A region-free org host gets one credential-free `HEAD` request to learn its region.
|
|
815
|
+
- Later requests go to the regional host, which must share the org, family and apex.
|
|
816
|
+
- Any other base URL (internal, air-gapped, localhost) is used exactly as given.
|
|
817
|
+
- `https://api.k9o.io` is only the default when `base_url:` is omitted.
|
|
541
818
|
|
|
542
819
|
### Related packages
|
|
543
820
|
|
|
@@ -546,6 +823,20 @@ The server may send codes not listed here, so keep a fallback branch.
|
|
|
546
823
|
| `@k9o/core` | TypeScript SDK |
|
|
547
824
|
| `@k9o/cli` | Command-line tool |
|
|
548
825
|
|
|
826
|
+
### About Kilimanjaro
|
|
827
|
+
|
|
828
|
+
Kilimanjaro is a modular developer platform for building secure enterprise applications.
|
|
829
|
+
You define AI agents, functions and data pipelines on the platform.
|
|
830
|
+
The SDKs let you deploy and invoke them, read and write your data, publish to and subscribe to streams,
|
|
831
|
+
sign users in, and encrypt with keys you hold.
|
|
832
|
+
|
|
833
|
+
It provides four building blocks:
|
|
834
|
+
|
|
835
|
+
- **Keep**: AI security and governance. Guardrails and policy for the agents you run.
|
|
836
|
+
- **Pulse**: data intelligence and real-time stream processing. Streams, analytics and live feeds.
|
|
837
|
+
- **Gate**: customer identity and access management. Sign-in and sessions, and automatic data scoping so a signed-in user reaches only their own data.
|
|
838
|
+
- **Kibo**: cryptographic keys and secrets management. Client-side encryption with keys you hold, and a secrets vault.
|
|
839
|
+
|
|
549
840
|
## License
|
|
550
841
|
|
|
551
842
|
Apache-2.0 © Formelo Inc. See `LICENSE` and `NOTICE`.
|