@substrio/sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/substrio_errors.mjs +102 -22
- package/substrio_operations.d.mts +1192 -105
- package/substrio_operations.mjs +151 -37
package/substrio_operations.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by packages/substrio-sdk/scripts/gen_operations.mjs from the server's operation catalog
|
|
2
|
-
// (GET /v1/_openapi, registry version 1,
|
|
2
|
+
// (GET /v1/_openapi, registry version 1, 548 operations with routes);
|
|
3
3
|
// never edit by hand. Regenerate: npm run gen:operations (packages/substrio-sdk).
|
|
4
4
|
|
|
5
5
|
const route = (method, path, params, query, body, bodyProperty = null) =>
|
|
@@ -15,7 +15,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
15
15
|
operation(438, "account.app.attach", "Make an existing app one of an account's apps (platform admin)", [
|
|
16
16
|
route("PUT", "/v1/_tenants/{slug}/account", ["slug"], [], true),
|
|
17
17
|
]),
|
|
18
|
-
operation(377, "account.app.create", "Create an app owned by the account", [
|
|
18
|
+
operation(377, "account.app.create", "Create an app owned by the account. A region (a slug region.choices lists) places it there; one that is not listed is refused with REGION_UNAVAILABLE", [
|
|
19
19
|
route("POST", "/v1/_accounts/{id}/apps", ["id"], [], true),
|
|
20
20
|
]),
|
|
21
21
|
operation(378, "account.app.delete", "Delete one of the account's apps and its data", [
|
|
@@ -30,14 +30,20 @@ export const OPERATIONS = Object.freeze([
|
|
|
30
30
|
operation(445, "account.app.open.set", "Open one of the account's apps to other accounts' keys, or close it", [
|
|
31
31
|
route("PUT", "/v1/_accounts/{id}/apps/{slug}/keys-open", ["id","slug"], [], true),
|
|
32
32
|
]),
|
|
33
|
+
operation(789, "account.apps.order", "Set the order the account's apps run in: apps lists every app id of the account once (any other list is refused, INVALID_ARGUMENT naming apps). The first max_apps run; the rest are paused, keeping every byte, and their requests answer APP_OVER_PLAN until they move up, an app is deleted or the plan allows more. Answers each app in order with active, and max_apps (0: no limit)", [
|
|
34
|
+
route("PUT", "/v1/_accounts/{id}/apps/order", ["id"], [], true),
|
|
35
|
+
]),
|
|
33
36
|
operation(562, "account.audit.list", "Read an account's audit trail (its keys, members, invites, apps and clients), newest first; the filters, limit and cursor of audit.list", [
|
|
34
37
|
route("GET", "/v1/_accounts/{id}/audit", ["id"], ["order","since","limit","cursor","until","action","actor_kind","actor","target_kind","target","outcome"], false),
|
|
35
38
|
]),
|
|
39
|
+
operation(743, "account.console.link", "Mint the person you act for a one-time console sign-in, then open its url in their browser: it lands them signed in on their dashboard. Only a person mints one (an agent session for its own person, or the person signed in to the console); a key, the master key or an agent token is refused CONSOLE_LINK_NEEDS_PERSON. The link lives 5 minutes (the server's --console-link-seconds), signs in once, and a new one replaces the last; its token rides in the url's fragment, so no server receives or logs it. Answers {url, expires_at}", [
|
|
40
|
+
route("POST", "/v1/_accounts/console/link", [], [], false),
|
|
41
|
+
]),
|
|
36
42
|
operation(366, "account.create", "Create an account owned by the signed-in person", [
|
|
37
43
|
route("POST", "/v1/_accounts", [], [], true),
|
|
38
44
|
]),
|
|
39
45
|
operation(369, "account.delete", "Delete an account that owns no apps, with its members and keys", [
|
|
40
|
-
route("DELETE", "/v1/_accounts/{id}", ["id"], [], false),
|
|
46
|
+
route("DELETE", "/v1/_accounts/{id}", ["id"], ["with_apps"], false),
|
|
41
47
|
]),
|
|
42
48
|
operation(367, "account.get", "Read an account", [
|
|
43
49
|
route("GET", "/v1/_accounts/{id}", ["id"], [], false),
|
|
@@ -84,7 +90,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
84
90
|
operation(371, "account.member.set", "Add a person to an account, or change their role", [
|
|
85
91
|
route("PUT", "/v1/_accounts/{id}/members/{user_id}", ["id","user_id"], [], true),
|
|
86
92
|
]),
|
|
87
|
-
operation(423, "account.mfa.set", "Require every member of an account to sign in with
|
|
93
|
+
operation(423, "account.mfa.set", "Require every member of an account to sign in with 2FA, or stop requiring it", [
|
|
88
94
|
route("PUT", "/v1/_accounts/{id}/mfa", ["id"], [], true),
|
|
89
95
|
]),
|
|
90
96
|
operation(621, "account.overview", "Read every app of the account with slug, tenant_id, suspended, live version and calls (invocations, status classes, p95_under_ms, from memory); stored_bytes and documents are present only when open is true (the app's database was already open: never opened here to total it), then the account's totals (apps, suspended, open, calls, stored_bytes, documents) across every app", [
|
|
@@ -99,12 +105,18 @@ export const OPERATIONS = Object.freeze([
|
|
|
99
105
|
operation(655, "account.plan.summary", "Read what the account's plan allows beside what it uses, and every plan with what it adds and its price. An agent explains the plans and the upgrade from this alone", [
|
|
100
106
|
route("GET", "/v1/_accounts/{id}/plan", ["id"], [], false),
|
|
101
107
|
]),
|
|
108
|
+
operation(787, "account.storage", "Read what each app of the account holds on disk (disk_bytes: its database, files, logs, backups, code and the rest, as of its last fold, open or not), the account's total (its apps and their sandboxes, the figure its space is held against) and the space in force (null while none). Supersedes account.overview's stored_bytes, which counts only open apps' document bytes", [
|
|
109
|
+
route("GET", "/v1/_accounts/{id}/storage", ["id"], [], false),
|
|
110
|
+
]),
|
|
102
111
|
operation(368, "account.update", "Rename an account; the system key also sets its services, space and max_apps, and suspends it", [
|
|
103
112
|
route("PATCH", "/v1/_accounts/{id}", ["id"], [], true),
|
|
104
113
|
]),
|
|
105
114
|
operation(446, "account.usage", "Read the account's plan and usage counters at each app it has a plan or usage at", [
|
|
106
115
|
route("GET", "/v1/_accounts/{id}/usage", ["id"], [], false),
|
|
107
116
|
]),
|
|
117
|
+
operation(745, "account.usage.cpu", "Read the CPU the account's apps used in each of the last 60 finished minutes, the hour's mean, the last 15 minutes' mean, the peak minute, the plan's CPU slices and the hour against them (used, of_share), the requests and calls held back while the server was saturated (holds) and each app's minutes and holds, each in CPU slices (1 is the Free plan's CPU under load; Standard holds 4). Explains how close the account runs to its plan's share and who waited for it", [
|
|
118
|
+
route("GET", "/v1/_accounts/{id}/usage/cpu", ["id"], [], false),
|
|
119
|
+
]),
|
|
108
120
|
operation(535, "agent.alert.list", "List the account's agent alerts (a spent key used again revokes its session and leaves one), newest first", [
|
|
109
121
|
route("GET", "/v1/_accounts/{id}/agent/alerts", ["id"], [], false),
|
|
110
122
|
]),
|
|
@@ -120,13 +132,13 @@ export const OPERATIONS = Object.freeze([
|
|
|
120
132
|
operation(530, "agent.enroll.create", "Make a one-time code that an AI agent exchanges for a session acting as you in the account; the code is returned once", [
|
|
121
133
|
route("POST", "/v1/_accounts/{id}/agent/enrollments", ["id"], [], true),
|
|
122
134
|
]),
|
|
123
|
-
operation(531, "agent.enroll.exchange", "Exchange a one-time enrollment code for an agent session: a
|
|
135
|
+
operation(531, "agent.enroll.exchange", "Exchange a one-time enrollment code (substrio_ae_...) for an agent session acting as the member who made it: a bearer one unless a kind is named or a public key is sent (key-bound). Answers the credential once and next, what to do with it; the code is spent. Over MCP it is the tool substrio_connect on any /mcp, with no credential", [
|
|
124
136
|
route("POST", "/v1/_agent/exchange", [], [], true),
|
|
125
137
|
]),
|
|
126
138
|
operation(539, "agent.session.delegate", "An agent session makes a narrower child session for a sub-agent: the child's capabilities and apps are within the caller's, it ends no later than the caller, and it dies with the caller. Returns the child's credentials once", [
|
|
127
139
|
route("POST", "/v1/_accounts/{id}/agent/delegations", ["id"], [], true),
|
|
128
140
|
]),
|
|
129
|
-
operation(532, "agent.session.list", "List the account's agent sessions, oldest first, a page at a time (limit, cursor; next_cursor), or only those reaching one app (app); never a key", [
|
|
141
|
+
operation(532, "agent.session.list", "List the account's live agent sessions (an ended one leaves the list; its log is still read by id), oldest first, a page at a time (limit, cursor; next_cursor), or only those reaching one app (app); never a key", [
|
|
130
142
|
route("GET", "/v1/_accounts/{id}/agent/sessions", ["id"], ["app","limit","cursor"], false),
|
|
131
143
|
]),
|
|
132
144
|
operation(540, "agent.session.log", "List the requests an agent session made, oldest first, each with its operation, outcome (ran, refused, approval asked, approved, denied, spent), status, app and time; pages by limit and cursor (a member sees their own agents, an admin or owner the account's). Kept for agent_log_retention_seconds", [
|
|
@@ -186,24 +198,45 @@ export const OPERATIONS = Object.freeze([
|
|
|
186
198
|
operation(149, "ai.tools", "Describe administration tools available to the caller", [
|
|
187
199
|
route("GET", "/v1/_tools", [], [], false),
|
|
188
200
|
]),
|
|
201
|
+
operation(779, "app.archive", "Download the app as one .substrio.zip, its backup and its portable export: every collection as NDJSON (data/<collection>.ndjson), its users (users.ndjson), versions, config and site files, and manifest.json with each entry's SHA-256. Optional body passphrase seals secret values, password hashes, TOTP enrolments and passkeys into sealed.bin; without it they are left out. Optional backup_id archives that backup instead. app.restore or app.import reads it back on any server version", [
|
|
202
|
+
route("POST", "/v1/_archive/download", [], [], true),
|
|
203
|
+
]),
|
|
204
|
+
operation(749, "app.deploy", "Ship files in one call: commit the units and removals (as version.commit takes them), check the version's functions (unless check is false), make it live where to names (live, or a sandbox), test it there with app.test (unless test is false), and, with promote and a sandbox in to, make it live on the app once the test is green. Stops at the first step that fails: a refused commit, check or live answers that step's own error with stage; a failed test answers ok false, stage test and the test's failures with their fixes, and promotes nothing. Answers ok, stage, deployed {commit, live}, test and promote, each the answer of that step's own tool. Over HTTP it runs the commit, the check and live; test and promote run in the MCP tool", [
|
|
205
|
+
route("POST", "/v1/_app/deploy", [], [], true),
|
|
206
|
+
]),
|
|
207
|
+
operation(751, "app.diagnose", "What is wrong with the app (or a sandbox) and how to fix it, in one call: which version each environment runs against main's head, function.check over the version that runs where the call looks (its diagnostics), its newest function errors, and app.test of its website and GET routes, each finding with its fix. Answers ok, sandbox, live {main, environments}, check, errors {recent, fix}, test and fixes, every fix in order. Read only; the MCP server runs it", [
|
|
208
|
+
route("POST", "/v1/_app/diagnose", [], [], true),
|
|
209
|
+
]),
|
|
189
210
|
operation(413, "app.export", "Stream the app as one bundle (versions, blobs, live stack, views, AI config, database, secrets), sealed whole to seal_to, for that server's POST /v1/_import", [
|
|
190
211
|
route("GET", "/v1/_export", [], ["seal_to"], false),
|
|
191
212
|
]),
|
|
213
|
+
operation(770, "app.get", "Read this app's own record: slug, hosts, domains, suspended, readonly, live_by_promote, account_id, created_at and its limits. The app is the one the request names (X-Substrio-Tenant or its host)", [
|
|
214
|
+
route("GET", "/v1/_app", [], [], false),
|
|
215
|
+
]),
|
|
192
216
|
operation(417, "app.import", "Make an app from a bundle GET /v1/_export streamed (the request body): its data, versions, live version, secrets sealed to this server, views and sandboxes", [
|
|
193
217
|
route("POST", "/v1/_import", [], ["slug","replace"], true),
|
|
194
218
|
]),
|
|
195
219
|
operation(622, "app.mesh", "Read the mesh as this app sees it: enabled, node_id (hex), origin_seq (this node's own last origin sequence), max_applied_hlc (the highest HLC applied), entries (applied, streamed, deduped, pending), peer_count and peers (peer_id, node_id, address, connected, last_sent_seq, latency_us, state, acked_seq and behind, from gossip, null until the peer has gossiped since this process started). An app with no sync answers {\"enabled\": false}", [
|
|
196
220
|
route("GET", "/v1/_mesh/app", [], [], false),
|
|
197
221
|
]),
|
|
198
|
-
operation(620, "app.overview", "Read everything a dashboard's first paint needs for this app in one call: app (slug, tenant_id, created_at, hosts, domains, suspended, live_by_promote, is_sandbox), usage (the same body as tenant.usage), storage (database_bytes on disk and backups: count, bytes, latest_at), versions (live, rollback_to, count, sandboxes), functions (count, names), collections (count), cron (count), queues (count), alerts (log alerts fired in their last window), audit (the last 5 entries, newest first) and mesh (this app's mesh position, in short)", [
|
|
222
|
+
operation(620, "app.overview", "Read everything a dashboard's first paint needs for this app in one call: app (slug, tenant_id, created_at, hosts, domains, suspended, live_by_promote, is_sandbox), usage (the same body as tenant.usage.self), storage (database_bytes on disk and backups: count, bytes, latest_at), versions (live, rollback_to, count, sandboxes), functions (count, names), collections (count), cron (count), queues (count), alerts (log alerts fired in their last window), audit (the last 5 entries, newest first) and mesh (this app's mesh position, in short)", [
|
|
199
223
|
route("GET", "/v1/_overview", [], [], false),
|
|
200
224
|
]),
|
|
225
|
+
operation(780, "app.restore", "Restore a .substrio.zip (the request body, as app.archive made it) into this app through the normal write paths: users, data, versions, site files, config, and with the X-Substrio-Passphrase header the sealed secrets and sign-ins. Every entry is checked against manifest.json before anything is written. An app that holds data is refused unless replace=true. Answers the counts restored and what was left out", [
|
|
226
|
+
route("POST", "/v1/_archive", [], ["replace"], true),
|
|
227
|
+
]),
|
|
201
228
|
operation(662, "app.runtime_level.set", "Set one app's runtime compatibility level: which behaviour of the function runtime it gets. Never above the level this release carries (a higher level refuses the call), and the app's isolates pick the new level up as they are made", [
|
|
202
229
|
route("PATCH", "/v1/_tenants/{slug}/runtime_level", ["slug"], [], true),
|
|
203
230
|
]),
|
|
231
|
+
operation(750, "app.scaffold", "Start an app from a template compiled into the release: commits the app's first version on main, one snapshot of the template's units, each of which passes app.test. site is a page with its stylesheet and script; site-api adds a function route (GET /api/hello) the page calls; site-data-auth is a notes page anyone reads and a signed-in person adds to (GET /api/notes, POST /api/notes auth, GET /api/me auth). Refused with 409 when main already holds a version: change an app that has one with version.commit or app.deploy. Answers version.commit's answer and template; make it live with live.set (with sandbox to try it first)", [
|
|
232
|
+
route("POST", "/v1/_app/scaffold", [], [], true),
|
|
233
|
+
]),
|
|
204
234
|
operation(412, "app.seal_key.get", "Read the X25519 public key bundles this server imports are sealed to (seal_to of an export)", [
|
|
205
235
|
route("GET", "/v1/_seal-key", [], [], false),
|
|
206
236
|
]),
|
|
237
|
+
operation(748, "app.test", "Test the app's website (or a sandbox's) as a browser loads it, in one call: each page of paths (default /) and every same-site page it links, every stylesheet, script, image, icon and CSS url() or @import they name, and every GET function route with no parameter, each fetched in process as route.call does, as a visitor with no credentials. Each is checked for its status, its content type (a stylesheet must be text/css, a script JavaScript, an image image/*, never the site's page in their place) and its size. Answers ok, the table of every resource (path, kind, from, status, type, bytes, ok) and each failure with its problem and its fix. References to another origin are counted, never fetched", [
|
|
238
|
+
route("POST", "/v1/_app/test", [], [], true),
|
|
239
|
+
]),
|
|
207
240
|
operation(669, "apps.calls.get", "Read the app's word on calls between apps: the functions it offers other apps (each to the apps named, or @account: every app of its own account) and the functions of other apps it calls as substrio.apps.<app>.<entry>(...)", [
|
|
208
241
|
route("GET", "/v1/_app_calls", [], [], false),
|
|
209
242
|
]),
|
|
@@ -243,7 +276,13 @@ export const OPERATIONS = Object.freeze([
|
|
|
243
276
|
operation(408, "auth.client.redirect_uris.set", "Replace the redirect URIs an app's sign-ins may return to", [
|
|
244
277
|
route("PUT", "/v1/_accounts/{id}/clients/{slug}", ["id","slug"], [], true),
|
|
245
278
|
]),
|
|
246
|
-
operation(
|
|
279
|
+
operation(790, "auth.devices", "Count the signed-in person's remembered devices: each signs in with the authenticator code and no email until it ends (30 days, or every session of the person ending)", [
|
|
280
|
+
route("GET", "/v1/_auth/devices", [], [], false),
|
|
281
|
+
]),
|
|
282
|
+
operation(791, "auth.devices.forget", "Forget every remembered device of the signed-in person: each signs in by the email code again. No session ends", [
|
|
283
|
+
route("POST", "/v1/_auth/devices/forget", [], [], false),
|
|
284
|
+
]),
|
|
285
|
+
operation(610, "auth.email.code_request", "Mail a short code and a link that prove an address; nobody need be signed in. One pair an address: asked again while it lives, the same code and link are mailed again (every mail sent works) and its life starts over; a spent, expired or locked pair gives way to a new one. Paced by the app's request interval. Answers {accepted, expires_at} whether or not anyone holds the address. On a device remembered for the person the address names (the substrio_device cookie auth.mfa set), their 2FA on, no mail is sent: {accepted, expires_at, mfa_required, challenge}, finished by auth.mfa with the authenticator code", [
|
|
247
286
|
route("POST", "/v1/_auth/email/code_request", [], [], true),
|
|
248
287
|
]),
|
|
249
288
|
operation(611, "auth.email.code_verify", "Prove an address with its mailed code (email and code) or its link (token): the person holding it, made when nobody does, is verified. Answers {verified, email, user_id, created, password_grant?}; a code or link that proves nothing is 400 with its reason (MISSING, USED, EXPIRED, TOO_MANY_ATTEMPTS, WRONG and attempts_remaining)", [
|
|
@@ -261,6 +300,9 @@ export const OPERATIONS = Object.freeze([
|
|
|
261
300
|
operation(251, "auth.google", "Authenticate with Google OAuth", [
|
|
262
301
|
route("POST", "/v1/_auth/google", [], [], true),
|
|
263
302
|
]),
|
|
303
|
+
operation(744, "auth.link.redeem", "For the console: spend a one-time console link (token, from the url's #link fragment) and sign its person in at the identity app, marked: an irreversible call waits for an approval and it decides none (CONSOLE_LINK_STEP_UP) until the person signs in again by the email code or 2FA. Answers {user_id, account_id, token, keyring} as auth.email.code_verify signs in, or the 2FA step due under mfa; a link that signs nobody in is refused CONSOLE_LINK_UNKNOWN (made up, or replaced by a newer link), CONSOLE_LINK_USED or CONSOLE_LINK_EXPIRED", [
|
|
304
|
+
route("POST", "/v1/_auth/link/redeem", [], [], true),
|
|
305
|
+
]),
|
|
264
306
|
operation(17, "auth.login", "Authenticate a user", [
|
|
265
307
|
route("POST", "/v1/_auth/login", [], [], true),
|
|
266
308
|
route("POST", "/v1/auth/login", [], [], true),
|
|
@@ -268,7 +310,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
268
310
|
operation(447, "auth.login.set", "Join the shared login ([login]): make the app a client of the identity app with a callback on each of its hosts", [
|
|
269
311
|
route("PUT", "/v1/_auth/login", [], [], true),
|
|
270
312
|
]),
|
|
271
|
-
operation(380, "auth.logout", "End this session; its token stops working at once", [
|
|
313
|
+
operation(380, "auth.logout", "End this session; its token stops working at once, and this browser's substrio_session cookie ends and is cleared (a remembered device stays)", [
|
|
272
314
|
route("POST", "/v1/_auth/logout", [], [], false),
|
|
273
315
|
]),
|
|
274
316
|
operation(381, "auth.logout_all", "End every session of the signed-in user, this one included", [
|
|
@@ -280,7 +322,10 @@ export const OPERATIONS = Object.freeze([
|
|
|
280
322
|
operation(607, "auth.me.delete", "Erase the signed-in user and their auth records; dry_run checks only", [
|
|
281
323
|
route("DELETE", "/v1/_auth/me", [], ["dry_run"], false),
|
|
282
324
|
]),
|
|
283
|
-
operation(
|
|
325
|
+
operation(792, "auth.me.update", "Edit the signed-in person's own profile: name always, and each member of fields only when the app's _users schema marks that field \"self\": true (a member not marked refuses the whole edit with 403 SELF_FIELD_REFUSED naming it; a value the schema refuses is 400 SCHEMA_REFUSED; a key is refused 403, a key is not a user). Email and password change through their own verified flows. Answers the profile as auth.me does", [
|
|
326
|
+
route("PATCH", "/v1/_auth/me", [], [], true),
|
|
327
|
+
]),
|
|
328
|
+
operation(418, "auth.mfa", "Finish a sign-in that answered mfa_required: trade its challenge and a code, or a recovery code, for the session. After the email code, remember true also remembers the device (the substrio_device cookie, 30 days)", [
|
|
284
329
|
route("POST", "/v1/_auth/mfa", [], [], true),
|
|
285
330
|
]),
|
|
286
331
|
operation(421, "auth.mfa.disable", "Turn MFA off with a code or a recovery code; every older session ends at its refresh", [
|
|
@@ -316,7 +361,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
316
361
|
operation(429, "auth.passkey.rename", "Rename one of the signed-in person's passkeys", [
|
|
317
362
|
route("PATCH", "/v1/_auth/passkeys/{id}", ["id"], [], true),
|
|
318
363
|
]),
|
|
319
|
-
operation(427, "auth.passkey.sign_in", "Sign in with a passkey and no password: verifies the assertion and returns a session; a passkey that verified the person needs no
|
|
364
|
+
operation(427, "auth.passkey.sign_in", "Sign in with a passkey and no password: verifies the assertion and returns a session; a passkey that verified the person needs no 2FA", [
|
|
320
365
|
route("POST", "/v1/_auth/passkeys/sign-in", [], [], true),
|
|
321
366
|
]),
|
|
322
367
|
operation(426, "auth.passkey.sign_in.options", "Start a passkey sign-in: returns the WebAuthn request options for navigator.credentials.get, for a discoverable passkey or a person's by email", [
|
|
@@ -344,9 +389,9 @@ export const OPERATIONS = Object.freeze([
|
|
|
344
389
|
operation(20, "auth.provider.update", "Configure one identity provider of the app for social sign-in; see the schema for its body", [
|
|
345
390
|
route("POST", "/v1/_auth/providers", [], [], true),
|
|
346
391
|
]),
|
|
347
|
-
operation(18, "auth.refresh", "Refresh a session", [
|
|
348
|
-
route("POST", "/v1/_auth/refresh", [], [],
|
|
349
|
-
route("POST", "/v1/auth/refresh", [], [],
|
|
392
|
+
operation(18, "auth.refresh", "Refresh a session: the Bearer token for a new one (the old one stops working). stay true also sets the substrio_session cookie (30 days, renewed by each refresh); with no Bearer, that cookie alone signs its person in again as their sign-in did", [
|
|
393
|
+
route("POST", "/v1/_auth/refresh", [], [], true),
|
|
394
|
+
route("POST", "/v1/auth/refresh", [], [], true),
|
|
350
395
|
]),
|
|
351
396
|
operation(16, "auth.register", "Create a user session", [
|
|
352
397
|
route("POST", "/v1/_auth/register", [], [], true),
|
|
@@ -377,7 +422,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
377
422
|
operation(614, "backup.clone_into", "Replace another tenant's database (body target_slug) with a backup of this app (the route's {id} from backup.list), then catch it up with this app's history from the backup's point to now, so it ends up current, not frozen at the backup. Everything target_slug held is lost; it must be a different tenant. Answers {status:'cloned', backup_id, target_slug, history_position, history_through, history_applied}", [
|
|
378
423
|
route("POST", "/v1/_backups/{id}/clone_into", ["id"], [], true),
|
|
379
424
|
]),
|
|
380
|
-
operation(193, "backup.create", "Start a backup of the app's database as a background job: optional body description. Answers 202 {job_id, status:'running'} at once; backup.get with
|
|
425
|
+
operation(193, "backup.create", "Start a backup of the app's database as a background job: optional body description. Answers 202 {job_id, backup_id, status:'running'} at once, the backup already listed by backup.list as running; backup.get with the job id shows the progress, and with the backup id the backup that backup.restore takes once it completes", [
|
|
381
426
|
route("POST", "/v1/_backups", [], [], true),
|
|
382
427
|
]),
|
|
383
428
|
operation(294, "backup.delete", "Delete one backup by its id (the route's {id}, as backup.list shows it)", [
|
|
@@ -419,6 +464,9 @@ export const OPERATIONS = Object.freeze([
|
|
|
419
464
|
operation(716, "billing.credit.get", "Read the account's credit: the funds it added, in the minor unit, and how many payments made it", [
|
|
420
465
|
route("GET", "/v1/_accounts/{id}/billing/credit", ["id"], [], false),
|
|
421
466
|
]),
|
|
467
|
+
operation(752, "billing.person_cart.create", "Make a cart of plans that are the person's own, paid by the account: they hold them at every app wherever they sign in, apart from the account's (subscriptions.mine lists them). The plans, the offer and checkout_url as billing.cart.create gives them; the subject is the person whose own session starts the checkout. No space_blocks: the add-on is the account's", [
|
|
468
|
+
route("POST", "/v1/_accounts/{id}/billing/person-carts", ["id"], [], true),
|
|
469
|
+
]),
|
|
422
470
|
operation(718, "billing.revenue", "What every account has paid the platform (paid: the identity ledger's maintained balance of the platform party, in USD, with how many payments) and how many accounts' newest invoice is unpaid (past_due). Platform operator only", [
|
|
423
471
|
route("GET", "/v1/_billing/revenue", [], [], false),
|
|
424
472
|
]),
|
|
@@ -440,7 +488,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
440
488
|
operation(663, "cluster.ready", "Whether this node has caught up with the mesh: 200 when every app's replication has nothing outstanding (its links up, no catch-up in flight, and every position its peers have gossiped already held here) and 503 while one has, naming the app it waits on and what it waits for. A rolling upgrade asks this between nodes, with curl alone, before it installs on the next one", [
|
|
441
489
|
route("GET", "/v1/_cluster/ready", [], [], false),
|
|
442
490
|
]),
|
|
443
|
-
operation(660, "cluster.version", "Read the
|
|
491
|
+
operation(660, "cluster.version", "Read the versions in use: the wire range this release speaks, the version chosen for the mesh (the lowest max over the members), the version the mesh has FINALIZED, the storage format, the runtime level this release carries, and every member with its range and whether its link is live", [
|
|
444
492
|
route("GET", "/v1/_cluster/version", [], [], false),
|
|
445
493
|
]),
|
|
446
494
|
operation(661, "cluster.version.finalize", "Finalize the mesh at wire version {version}: allowed only when this node and every member already speak it (a member whose link is down or whose max is lower refuses the call and is named). It is written durably before the call answers and never goes back: from then on a node that does not speak it is refused at the handshake, so finalize only once every node runs the new release", [
|
|
@@ -460,6 +508,9 @@ export const OPERATIONS = Object.freeze([
|
|
|
460
508
|
route("GET", "/v1/{collection}/_stats", ["collection"], [], false),
|
|
461
509
|
route("GET", "/v1/{collection}/stats", ["collection"], [], false),
|
|
462
510
|
]),
|
|
511
|
+
operation(785, "dashboard.get", "Read the dashboard an app declares, the file its live version shipped (nav items and their panels, canonical), for the console to draw. The app is one of the account's own or a featured app the account has enabled; any other answers TENANT_NOT_FOUND. An app that declares none answers an empty nav. With version (a number or id), that version's dashboard instead, checked against its own routes as a push's is: what the console will draw once it goes live (an app of the account's own)", [
|
|
512
|
+
route("GET", "/v1/_accounts/{id}/apps/{slug}/dashboard", ["id","slug"], ["version"], false),
|
|
513
|
+
]),
|
|
463
514
|
operation(68, "data.delete", "Delete one record by its _id; 404 when it does not exist, 403 when the key may not delete it", [
|
|
464
515
|
route("DELETE", "/v1/{collection}/{id}", ["collection","id"], [], false),
|
|
465
516
|
]),
|
|
@@ -491,6 +542,9 @@ export const OPERATIONS = Object.freeze([
|
|
|
491
542
|
operation(454, "documentation.agent_guide", "Read the guide for AI agents building on substrio (text/plain); a tenant's own /llms.txt function route or site file is served instead", [
|
|
492
543
|
route("GET", "/llms.txt", [], [], false),
|
|
493
544
|
]),
|
|
545
|
+
operation(747, "documentation.brief", "Start here, in at most 3 KB. Answers the app, what this key is, the live version number, the goals an app is built for (website, api, database, auth, events: each with its summary, its skill and the tools it needs), the build loop's tools in order, and how to reach every other tool. Takes no input; substrio_tools {area: <goal>} answers a goal's full definitions, substrio_start every area's tools", [
|
|
546
|
+
route("GET", "/v1/_brief", [], [], false),
|
|
547
|
+
]),
|
|
494
548
|
operation(4, "documentation.capabilities", "Describe typed capabilities", [
|
|
495
549
|
route("GET", "/v1/_capabilities", [], [], false),
|
|
496
550
|
]),
|
|
@@ -533,24 +587,28 @@ export const OPERATIONS = Object.freeze([
|
|
|
533
587
|
operation(681, "env.host.remove", "Remove one of the app's hosts: it answers nothing from then on and its label is free. ENV_HOST_NOT_FOUND when the app holds no such host. Answers 204", [
|
|
534
588
|
route("DELETE", "/v1/_env_hosts/{host}", ["host"], [], false),
|
|
535
589
|
]),
|
|
536
|
-
operation(679, "env.host.set", "Bind a host of the app's choosing, <label>.<root> under an enabled root domain (my-app-v2.substrio.com), to one of its environments: live, or a sandbox (with version, a new sandbox running that version). The host answers exactly what that environment runs, with its data. A label has one owner across the platform: HOST_TAKEN when another app holds it, HOST_RESERVED for
|
|
590
|
+
operation(679, "env.host.set", "Bind a host of the app's choosing, <label>.<root> under an enabled root domain (my-app-v2.substrio.com), to one of its environments: live, or a sandbox (with version, a new sandbox running that version). The host answers exactly what that environment runs, with its data. A label has one owner across the platform: HOST_TAKEN when another app holds it, HOST_RESERVED for a reserved name or an app host, HOST_INVALID (with suggestion) for a label that is not a hostname label, ROOT_DOMAIN_UNKNOWN or ROOT_DOMAIN_DISABLED for the root. Binding a host the app holds again moves it. version.live.set with from: <host> pushes what it runs live", [
|
|
537
591
|
route("POST", "/v1/_env_hosts", [], [], true),
|
|
538
592
|
]),
|
|
539
|
-
operation(686, "featured.disable", "
|
|
593
|
+
operation(686, "featured.disable", "Deprecated: every account has every jet, always on, so it is refused FEATURED_ALWAYS_ON. A key stops with account.key.revoke; an app's keys go with the app", [
|
|
540
594
|
route("DELETE", "/v1/_accounts/{id}/featured/{jet}", ["id","jet"], [], false),
|
|
541
595
|
]),
|
|
542
|
-
operation(685, "featured.enable", "
|
|
596
|
+
operation(685, "featured.enable", "Deprecated: every account has every jet, always on. Answers the jet's entry as featured.list writes it and changes nothing", [
|
|
543
597
|
route("PUT", "/v1/_accounts/{id}/featured/{jet}", ["id","jet"], [], false),
|
|
544
598
|
]),
|
|
545
|
-
operation(687, "featured.key.create", "
|
|
599
|
+
operation(687, "featured.key.create", "Deprecated: a jet key belongs to one of the account's apps, so it is refused JET_KEY_NEEDS_APP; jet.key.create makes it", [
|
|
546
600
|
route("POST", "/v1/_accounts/{id}/featured/{jet}/keys", ["id","jet"], [], true),
|
|
547
601
|
]),
|
|
548
|
-
operation(684, "featured.list", "List the featured apps (the jets: MapJet, SendJet, AuthJet and every later one)
|
|
602
|
+
operation(684, "featured.list", "List the featured apps (the jets: MapJet, SendJet, AuthJet and every later one) for the account: what each does, the plan it is metered against and the account's use this day and month (every app of the account summed) against that plan's limits. Every account has every jet: there is nothing to enable", [
|
|
549
603
|
route("GET", "/v1/_accounts/{id}/featured", ["id"], [], false),
|
|
550
604
|
]),
|
|
551
|
-
operation(688, "featured.set", "Feature an app for every account, or stop featuring it (platform admin). It must be open to other accounts' keys and its plans file must have a featured block (title, summary, admin).
|
|
605
|
+
operation(688, "featured.set", "Feature an app for every account, or stop featuring it (platform admin). It must be open to other accounts' keys and its plans file must have a featured block (title, summary, admin). An account on a plan there keeps it when it stops being featured", [
|
|
552
606
|
route("PUT", "/v1/_featured/{jet}", ["jet"], [], true),
|
|
553
607
|
]),
|
|
608
|
+
operation(742, "file.adopt", "Platform operator only: store a file that is already on this server's own disk (under the server's --adopt-dir) at a path of the app the request names, with 0 bytes written: hashed in one read, then renamed in. Answers what file.upload answers", [
|
|
609
|
+
route("POST", "/v1/_files/adopt", [], [], true),
|
|
610
|
+
route("POST", "/v1/files/adopt", [], [], true),
|
|
611
|
+
]),
|
|
554
612
|
operation(239, "file.delete", "Delete one stored file by its path (the route's tail, as file.list shows it)", [
|
|
555
613
|
route("DELETE", "/v1/_files/{path}", ["path"], ["path"], false),
|
|
556
614
|
route("DELETE", "/v1/files/{path}", ["path"], ["path"], false),
|
|
@@ -563,12 +621,23 @@ export const OPERATIONS = Object.freeze([
|
|
|
563
621
|
route("GET", "/v1/_files", [], ["limit","cursor","prefix"], false),
|
|
564
622
|
route("GET", "/v1/files", [], ["limit","cursor","prefix"], false),
|
|
565
623
|
]),
|
|
624
|
+
operation(783, "file.move", "Rename a stored file's path from one path to another, its content never copied. Refused with FILE_EXISTS when a file is at to, unless replace is true; FILE_NOT_FOUND when none is at from. Answers the file at its new path, as file.upload answers", [
|
|
625
|
+
route("POST", "/v1/_files/move", [], [], true),
|
|
626
|
+
route("POST", "/v1/files/move", [], [], true),
|
|
627
|
+
]),
|
|
628
|
+
operation(786, "file.public.get", "Read a stored file marked public, with no key, on the app's own hosts; a private or missing path is the same 404", [
|
|
629
|
+
route("GET", "/_files/{path}", ["path"], [], false),
|
|
630
|
+
]),
|
|
566
631
|
operation(453, "file.site_root.get", "Read the tenant site's index page (the app named by Host or X-Substrio-Tenant)", [
|
|
567
632
|
route("GET", "/", [], [], false),
|
|
568
633
|
]),
|
|
569
634
|
operation(333, "file.static_asset.get", "Read a tenant static asset", [
|
|
570
635
|
route("GET", "/_/{path}", ["path"], [], false),
|
|
571
636
|
]),
|
|
637
|
+
operation(782, "file.update", "Change a stored file's settings (public, content_type, cache_control) by its path, with no byte of it sent again: its bytes and hash are untouched and an absent setting keeps its value. Answers what file.upload answers; FILE_NOT_FOUND when no file is at the path", [
|
|
638
|
+
route("PATCH", "/v1/_files/{path}", ["path"], ["path"], true),
|
|
639
|
+
route("PATCH", "/v1/files/{path}", ["path"], ["path"], true),
|
|
640
|
+
]),
|
|
572
641
|
operation(237, "file.upload", "Upload a file into the app's file storage at its path; see the schema for the body. Answers its path, which file.download and file.delete take", [
|
|
573
642
|
route("POST", "/v1/_files", [], [], true),
|
|
574
643
|
route("POST", "/v1/files", [], [], true),
|
|
@@ -596,7 +665,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
596
665
|
operation(132, "function.delete", "Delete one function by name; answers {success, deleted}, 404 when no such function", [
|
|
597
666
|
route("DELETE", "/v1/functions/{name}", ["name"], [], false),
|
|
598
667
|
]),
|
|
599
|
-
operation(130, "function.deploy", "Deploy one function now, outside a version: body name and
|
|
668
|
+
operation(130, "function.deploy", "Deploy one function now, outside a version: body name and its source (required): script for JavaScript, or rust for a Rust function's source, which the server compiles; optional main, routes (like 'GET /orders/:id' or 'ANY /hooks/*'), vars, bindings and deploy_nodes. The source is compiled to a handler first; a source that does not compile, or that calls require(), is refused and nothing changes. On success it is live at once and the answer is the function's metadata (id, content_hash, routes, env_vars, bindings). To ship several functions together, or to try them first, use version.create, sandbox.create and version.live.set; function.check shows the problems without writing", [
|
|
600
669
|
route("POST", "/v1/functions", [], [], true),
|
|
601
670
|
route("POST", "/v1/deploy", [], [], true),
|
|
602
671
|
]),
|
|
@@ -612,7 +681,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
612
681
|
operation(473, "function.errors", "Read the errors the app's functions threw, newest first: each has an id (also a cursor), function, revision (the function's newest revision when it threw), request_id, timestamp, error name, message and the stack as frames (function, file, line, column). Options: function, request_id, revision (only errors of one deploy), since and until (Unix seconds), limit and cursor. Answers errors, count and next_cursor. Check the revision against version.log with the function path: a deploy that fixed a bug stops adding errors under the new revision", [
|
|
613
682
|
route("GET", "/v1/_function_errors", [], ["since","function","limit","cursor","request_id","until","revision"], false),
|
|
614
683
|
]),
|
|
615
|
-
operation(134, "function.eval", "Run a Lua script against the tenant's default key-value store, Redis style
|
|
684
|
+
operation(134, "function.eval", "Run a Lua script against the tenant's default key-value store, Redis style, in a build with the Redis frontend (not in this release, where the Redis frontend is a build switch, off, and the call answers NOT_IMPLEMENTED). Body script (or sha of a script loaded earlier with load:true, which answers {sha}), keys and args as arrays of strings. Answers {result}. This is Lua over key-value data, not JavaScript: to run JavaScript use function.invoke, route.call or function.execute_inline", [
|
|
616
685
|
route("POST", "/v1/eval", [], [], true),
|
|
617
686
|
]),
|
|
618
687
|
operation(256, "function.execute_inline", "Run inline JavaScript in the app's runtime: body code (required, not empty). It runs with the caller's own permissions through the same data API a function uses, and nothing is deployed or stored. For a deployed function use function.invoke; to check source without running it use function.check", [
|
|
@@ -645,7 +714,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
645
714
|
operation(357, "function.patch.approval.revoke", "Revoke one pending function patch approval", [
|
|
646
715
|
route("DELETE", "/v1/functions/{name}/approvals/{token}", ["name","token"], [], false),
|
|
647
716
|
]),
|
|
648
|
-
operation(338, "function.source.edit", "Replace one byte range or one exclusive line range of a function's source: body replacement, an approval_token from function.source.preview (required: preview the patch first), the range, and expected_hash (defaults to the current content_hash; a source that changed since is refused with 409). The edited source is compiled before it is stored", [
|
|
717
|
+
operation(338, "function.source.edit", "Replace one byte range or one exclusive line range of a function's source: body replacement, an approval_token from function.source.preview (required: preview the patch first), the range, and expected_hash (defaults to the current content_hash; a source that changed since is refused with 409). The edited source is compiled before it is stored, JavaScript or Rust (a Rust edit that does not compile is RUST_COMPILE_FAILED with rustc's diagnostics, and the function keeps serving its last source)", [
|
|
649
718
|
route("PATCH", "/v1/functions/{name}/source", ["name"], [], true),
|
|
650
719
|
]),
|
|
651
720
|
operation(352, "function.source.preview", "Preview and approve one bounded function source edit", [
|
|
@@ -654,7 +723,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
654
723
|
operation(349, "function.source.read", "Read a bounded line range from the current function source", [
|
|
655
724
|
route("GET", "/v1/functions/{name}/source", ["name"], ["start_line","end_line","max_bytes","expected_hash"], false),
|
|
656
725
|
]),
|
|
657
|
-
operation(337, "function.source.replace", "Replace one function's whole source with a compare-and-swap: body script (required, compiled before it is stored), optional expected_hash (the content_hash you last read; it defaults to the current one, and a source that changed since is refused with a conflict), main, routes, vars, bindings and deploy_nodes. Fields left out keep their values; an empty array clears. The function must exist (404). Answers the function's metadata", [
|
|
726
|
+
operation(337, "function.source.replace", "Replace one function's whole source with a compare-and-swap: body script for a JavaScript function or rust for a Rust one (required, compiled before it is stored; a replace keeps the function's language), optional expected_hash (the content_hash you last read; it defaults to the current one, and a source that changed since is refused with a conflict), main, routes, vars, bindings and deploy_nodes. Fields left out keep their values; an empty array clears. The function must exist (404). Answers the function's metadata", [
|
|
658
727
|
route("PUT", "/v1/functions/{name}/source", ["name"], [], true),
|
|
659
728
|
]),
|
|
660
729
|
operation(350, "function.source.search", "Search bounded source snippets in the current function", [
|
|
@@ -669,7 +738,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
669
738
|
operation(133, "function.toggle", "Enable or disable one function without deleting it: body enabled (required boolean). Answers {success, enabled}, 404 when no such function. A disabled function is left out of route.list", [
|
|
670
739
|
route("POST", "/v1/functions/{name}/toggle", ["name"], [], false),
|
|
671
740
|
]),
|
|
672
|
-
operation(131, "function.update", "Replace one function's source: PATCH with the body function.deploy takes (script is required); the name comes from the path and a name in the body is ignored. A source that does not compile to a handler is refused with 400 and nothing changes. On success it is live at once and the answer is the function's metadata. For a change to part of the source use function.source.edit", [
|
|
741
|
+
operation(131, "function.update", "Replace one function's source: PATCH with the body function.deploy takes (script or rust is required); the name comes from the path and a name in the body is ignored. A source that does not compile to a handler is refused with 400 and nothing changes. On success it is live at once and the answer is the function's metadata. For a change to part of the source use function.source.edit", [
|
|
673
742
|
route("PATCH", "/v1/functions/{name}", ["name"], [], true),
|
|
674
743
|
]),
|
|
675
744
|
operation(115, "graph.analytics", "Run an analytics algorithm over one graph label (the route's {label}); see the schema for the algorithm and its inputs", [
|
|
@@ -712,6 +781,15 @@ export const OPERATIONS = Object.freeze([
|
|
|
712
781
|
operation(645, "host.list", "List the hosts of the manifest in id order, each with its state, location, group settings, labels and key fingerprint (never the key)", [
|
|
713
782
|
route("GET", "/v1/_hosts", [], [], false),
|
|
714
783
|
]),
|
|
784
|
+
operation(776, "host.node.deploy", "Install a packed release on a host as a node (a new slot) or onto an existing node (its slot): the release streams over the host's SSH into the release's own installer, which preflights, installs the unit, health checks and rolls back. Refused before anything is copied with HOST_NOT_PROBED, HOST_NO_SYSTEMD, HOST_NGINX_PRESENT, RELEASE_NOT_FOUND, RELEASE_NOT_PACKED, RELEASE_ISA_UNKNOWN, HOST_CPU_MISSING (the detail names the features), HOST_NODE_LIMIT or NODE_BUSY. Answers 202 with the node as it stands; host.node.list then answers running, failed or rolled_back", [
|
|
785
|
+
route("POST", "/v1/_hosts/{id}/nodes", ["id"], [], true),
|
|
786
|
+
]),
|
|
787
|
+
operation(775, "host.node.list", "List the nodes installed on a host in slot order, each with its slot, port, state (deploying, running, failed, rolled_back, undeploying), the release last asked of it and when. A run that stays deploying for 30 minutes reads failed", [
|
|
788
|
+
route("GET", "/v1/_hosts/{id}/nodes", ["id"], [], false),
|
|
789
|
+
]),
|
|
790
|
+
operation(777, "host.node.undeploy", "Stop and remove a node's service and unit on a host; its data folder stays. Refused with HOST_HAS_APPS for the last node of a host while apps are placed on it and NODE_BUSY while its run is under way. Answers 202 with the node as it stands", [
|
|
791
|
+
route("DELETE", "/v1/_hosts/{id}/nodes/{slot}", ["id","slot"], [], false),
|
|
792
|
+
]),
|
|
715
793
|
operation(658, "host.probe", "Probe a host over its orchestrator's SSH, read only: whether it runs its own kernel (bare metal, kvm, another VM) or shares one (openvz, lxc, another container), systemd, the zram module, and zram swap active with zstd. Answers 202 with the host as it stands; the run lands on the host record in one transaction, and host.get answers virt, capabilities, probe (ok, unreachable, failed) and probed_at", [
|
|
716
794
|
route("POST", "/v1/_hosts/{id}/probe", ["id"], [], false),
|
|
717
795
|
]),
|
|
@@ -733,6 +811,12 @@ export const OPERATIONS = Object.freeze([
|
|
|
733
811
|
operation(477, "index.list", "List the indexes of the SQL tables with each one's value-key state: state ready (every row has its key and every write keeps it), building (a first query is posting the keys now) or none (no key kept; reason says why; a failed build reports none and the engine retries it). Pass table for one table. A collection's field is keyed by its first query, or declared with index.create kind collection: pass kind collection with collection and field to read whether the field is declared (unique, or indexed: a unique field is indexed too) and its key state and the reason it has no key; without field it lists every field the collection declares. Pass kind conflicts with collection and field (or fields, a JSON array, for a compound, with nulls skip or count) to list what stops the field from being declared unique (a declaration over duplicates answers INDEX_DUPLICATES with the first and the count): {conflicts: [{_id, value}], count, next_cursor}, the documents that hold a value another document holds, in id order, paged with limit and cursor; it builds the field's keys when they are not kept yet. Pass kind space to list the unique spaces: {spaces: [{space, members: [{collection, field}]}], count}. Pass kind expiry with collection to read the collection's expiry declaration {field, after_ms, armed_ms}. A collection's keyword and element declarations are listed beside its fields, each its own entry: keyword true with keyword_term_bytes, or elements (the tuple's members), with state and reason. Pass kind aggregate with collection to list the collection's declared live aggregates (index.create kind aggregate): {name, group, metrics, where, state}", [
|
|
734
812
|
route("GET", "/v1/_indexes", [], ["limit","cursor","table","kind","collection","field","fields","nulls","within"], false),
|
|
735
813
|
]),
|
|
814
|
+
operation(784, "jet.key.create", "Make a key of one of the account's apps scoped to one jet; the secret is returned once. The jet sees each call as that app (request.auth.app) and meters it to the account's plan, the app's own use counted beside; removing the app revokes the key. With origins the key is publishable (safe in a page's source, answering only those origins). An unscoped account key may make one; a scoped key may not", [
|
|
815
|
+
route("POST", "/v1/_accounts/{id}/apps/{slug}/jets/{jet}/keys", ["id","slug","jet"], [], true),
|
|
816
|
+
]),
|
|
817
|
+
operation(781, "jet.list", "List the jets of one of the account's apps: each jet's menu in the app's dashboard (app_nav: its pages, each a list, form or detail over the jet's own routes), the account's plan and use there (every app summed: the limit is the account's) and this app's own use this day and month", [
|
|
818
|
+
route("GET", "/v1/_accounts/{id}/apps/{slug}/jets", ["id","slug"], [], false),
|
|
819
|
+
]),
|
|
736
820
|
operation(196, "job.cancel", "Cancel one running background job by id: answers the job; 404 when no such job, and refused when it already ended", [
|
|
737
821
|
route("POST", "/v1/_jobs/{id}/cancel", ["id"], [], false),
|
|
738
822
|
]),
|
|
@@ -1020,6 +1104,9 @@ export const OPERATIONS = Object.freeze([
|
|
|
1020
1104
|
operation(443, "plans.account.set", "Put an account on one of the tenant's plans", [
|
|
1021
1105
|
route("PUT", "/v1/_plans/accounts/{account}", ["account"], [], true),
|
|
1022
1106
|
]),
|
|
1107
|
+
operation(746, "plans.catalog", "Read every plan sold, with no account and no person data: the Substrio plans (each with the jet plan it bundles per jet) and every featured app's (jet's) own plans, each with its limits and standalone price, each plans file as plans.get answers it. The sites build their prices from this. No key needed", [
|
|
1108
|
+
route("GET", "/v1/_billing/catalog", [], [], false),
|
|
1109
|
+
]),
|
|
1023
1110
|
operation(441, "plans.delete", "Delete the app's plans file: every plan definition is gone and cannot be restored by this call (keep the plans.get answer to put it back with plans.set). Answers 204 with no body; accounts keep no plan and fall back to the default", [
|
|
1024
1111
|
route("DELETE", "/v1/_plans", [], [], false),
|
|
1025
1112
|
]),
|
|
@@ -1029,10 +1116,10 @@ export const OPERATIONS = Object.freeze([
|
|
|
1029
1116
|
operation(440, "plans.set", "Replace the tenant's plans file; a bad file is refused and the deployed plans stay in force", [
|
|
1030
1117
|
route("POST", "/v1/_plans", [], [], true),
|
|
1031
1118
|
]),
|
|
1032
|
-
operation(563, "platform.audit.list", "Read the
|
|
1119
|
+
operation(563, "platform.audit.list", "Read the server-wide audit trail (tenant create, update and delete, platform settings, shared providers, key rotations), newest first; the filters, limit and cursor of audit.list", [
|
|
1033
1120
|
route("GET", "/v1/_platform/audit", [], ["order","since","limit","cursor","until","action","actor_kind","actor","target_kind","target","outcome"], false),
|
|
1034
1121
|
]),
|
|
1035
|
-
operation(741, "platform.counts", "Read the
|
|
1122
|
+
operation(741, "platform.counts", "Read the account and host counts for the operator, each kept with the accounts and hosts it counts", [
|
|
1036
1123
|
route("GET", "/v1/_platform/counts", [], [], false),
|
|
1037
1124
|
]),
|
|
1038
1125
|
operation(436, "platform.setting.set", "Set one platform setting on the running server; it takes effect at once, is kept in the system database and wins over substrio.conf. The server-wide runtime limits (jsc.*, queue.*, objects.*) are set here; an empty value unsets one, restoring its default", [
|
|
@@ -1041,7 +1128,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
1041
1128
|
operation(435, "platform.settings.get", "Read the platform settings: each one's kind, live value and meaning", [
|
|
1042
1129
|
route("GET", "/v1/_platform/settings", [], [], false),
|
|
1043
1130
|
]),
|
|
1044
|
-
operation(740, "platform.usage.history", "Read the
|
|
1131
|
+
operation(740, "platform.usage.history", "Read the KPI history of every app on this server for the operator, oldest first: one point per finished minute (step=minute, the default) or hour (step=hour), each with what every app's API requests (total, status classes 2xx to 5xx, latency buckets and percentiles) and function calls added in it, summed as each app's minute is written. since and until are Unix seconds (default: the last hour of minutes, or the last two days of hours); limit caps the points. Counted on the node that answers; platform calls (no app) are not counted", [
|
|
1045
1132
|
route("GET", "/v1/_platform/usage/history", [], ["limit","since","until","step"], false),
|
|
1046
1133
|
]),
|
|
1047
1134
|
operation(82, "query.aggregate", "Run an aggregation pipeline over one collection: stages run in order, one operator per stage (see the schema); use_index false forces a scan. For SQL-style grouping with having use query.chain", [
|
|
@@ -1123,6 +1210,18 @@ export const OPERATIONS = Object.freeze([
|
|
|
1123
1210
|
operation(631, "ref.restore", "Put a name back to a version its own log holds (the route's {kind} and {name}): name the version, or the log entry by sequence. A name that was deleted is made again, and one that still lives is moved, which is itself one more log entry. A tag is never moved, only made again at the version it marked. Answers {name, version, previous}", [
|
|
1124
1211
|
route("POST", "/v1/_refs/{kind}/{name}/restore", ["kind","name"], [], true),
|
|
1125
1212
|
]),
|
|
1213
|
+
operation(774, "region.choices", "List the regions an app can be created in: those that are available and serving, each with its slug, public name and country. Empty until a region serves. Pass a slug as region to account.app.create to place the app there", [
|
|
1214
|
+
route("GET", "/v1/_accounts/{id}/regions", ["id"], [], false),
|
|
1215
|
+
]),
|
|
1216
|
+
operation(771, "region.list", "List the regions in id order, each with its slug, public name, country, state (available or hidden) and whether it is serving (answers app traffic; the operator cannot set that)", [
|
|
1217
|
+
route("GET", "/v1/_regions", [], [], false),
|
|
1218
|
+
]),
|
|
1219
|
+
operation(773, "region.remove", "Remove a region by slug; refused with REGION_IN_USE while a host names it. Answers 204 with no body", [
|
|
1220
|
+
route("DELETE", "/v1/_regions/{slug}", ["slug"], [], false),
|
|
1221
|
+
]),
|
|
1222
|
+
operation(772, "region.set", "Create the region of this slug (the name is required) or change its name, country or state; a field left out keeps its value. A host joins a region through host.update", [
|
|
1223
|
+
route("PUT", "/v1/_regions/{slug}", ["slug"], [], true),
|
|
1224
|
+
]),
|
|
1126
1225
|
operation(668, "release.list", "List this host's platform releases, newest first: every folder of <root>/releases holding the server binary (the folder's name is the commit it was built from, with the binary's size and time), which one this process runs, which one the current link names (the next restart's) and which one the previous link names (a rollback's), and restart_pending when current is not the running one. A release is switched on the host by deploy-stax.sh, which health-checks it and rolls back", [
|
|
1127
1226
|
route("GET", "/v1/_releases", [], [], false),
|
|
1128
1227
|
]),
|
|
@@ -1147,10 +1246,10 @@ export const OPERATIONS = Object.freeze([
|
|
|
1147
1246
|
route("PUT", "/v1/_roles/{name}", ["name"], [], true),
|
|
1148
1247
|
route("PATCH", "/_roles/{name}", ["name"], [], true),
|
|
1149
1248
|
]),
|
|
1150
|
-
operation(675, "root_domain.add", "Add a root domain (onstax.com)
|
|
1249
|
+
operation(675, "root_domain.add", "Add a root domain (onstax.com) that apps answer under as <slug>.<root> and their test hosts as <label>.<root>: stored before the call answers, published to the front as *.<root> and, when the platform has a DNS-01 token, its wildcard certificate requested from the configured ACME provider. Answers 201 with domain, enabled, source, hosts and added_at; KEY_EXISTS (field domain) when the platform has it, INVALID_ARGUMENT (field domain) when it is not a domain", [
|
|
1151
1250
|
route("POST", "/v1/_root_domains", [], [], true),
|
|
1152
1251
|
]),
|
|
1153
|
-
operation(676, "root_domain.list", "List the
|
|
1252
|
+
operation(676, "root_domain.list", "List the root domains: those of substrio.conf (source config) then the ones added (source admin), each with enabled and its count of test hosts", [
|
|
1154
1253
|
route("GET", "/v1/_root_domains", [], [], false),
|
|
1155
1254
|
]),
|
|
1156
1255
|
operation(678, "root_domain.remove", "Remove an added root domain; refused ROOT_DOMAIN_IN_USE (hosts: the count) while a test host is bound under it, UNSUPPORTED_OPERATION (field domain) for one of substrio.conf. Answers 204", [
|
|
@@ -1195,7 +1294,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
1195
1294
|
operation(572, "sandbox.stub.list", "Read a test tenant's stubs, with how often each answered, and every fetch its functions made since the last clear (method, URL, headers, body, the tenant's clock, stubbed or unmatched)", [
|
|
1196
1295
|
route("GET", "/v1/_stubs", [], [], false),
|
|
1197
1296
|
]),
|
|
1198
|
-
operation(571, "sandbox.stub.set", "Stub one outbound request of a test tenant: its functions' fetch() of the given method, origin and path resolves with the stub's status, headers and body instead of going out. Only a test tenant
|
|
1297
|
+
operation(571, "sandbox.stub.set", "Stub one outbound request of a test tenant: its functions' fetch() of the given method, origin and path resolves with the stub's status, headers and body instead of going out. Only a test tenant takes stubs, any other is refused with TEST_TENANT_REQUIRED. The newest matching stub that is not spent answers; a fetch no stub matches is rejected. Answers {id}", [
|
|
1199
1298
|
route("POST", "/v1/_stubs", [], [], true),
|
|
1200
1299
|
]),
|
|
1201
1300
|
operation(234, "schedule.create", "Create a cron schedule: body function (the function to run, which must define a scheduled handler or a default export) and expression (five fields; one that never fires, like 31 February, is refused), optionally timezone (an IANA zone id the expression is evaluated in, default UTC; an unknown id is refused) and catch_up (none, last or all: what fires missed while the server was down do; default last). A run that fails or is refused is retried with backoff (settings cron.max_attempts, cron.backoff_ms), then goes to _dead_letters. Answers {id, success}; the id is what schedule.run, schedule.next, schedule.history, schedule.update and schedule.delete take", [
|
|
@@ -1250,7 +1349,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
1250
1349
|
operation(178, "security.audit", "Read the failed sign-ins of the audit trail (action auth.failure in audit.list), newest first: the key id of the key that proved nothing, or the caller's address when no key was presented; since, until, order, limit, cursor, actor_kind and actor filter it", [
|
|
1251
1350
|
route("GET", "/v1/_security/audit", [], ["order","since","limit","cursor","until","actor_kind","event_type"], false),
|
|
1252
1351
|
]),
|
|
1253
|
-
operation(177, "security.rate_limit", "Manage the
|
|
1352
|
+
operation(177, "security.rate_limit", "Manage the per-address rate limit (the --rate-limit server option): GET shows it, POST sets max_tokens and tokens_per_second, DELETE with clear turns it off", [
|
|
1254
1353
|
route("GET", "/v1/_security/rate_limit", [], ["max_tokens","tokens_per_second","clear"], false),
|
|
1255
1354
|
route("POST", "/v1/_security/rate_limit", [], [], true),
|
|
1256
1355
|
route("DELETE", "/v1/_security/rate_limit", [], [], true),
|
|
@@ -1270,7 +1369,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
1270
1369
|
operation(240, "settings.get", "List the app's settings: answers settings (each {key, value}) and count", [
|
|
1271
1370
|
route("GET", "/v1/_settings", [], [], false),
|
|
1272
1371
|
]),
|
|
1273
|
-
operation(241, "settings.set", "Set one app setting: body key and value (a string, or true or false). Any key is stored; substrio reads changes.history (keep change history so change streams can resume) and webhooks.brand (the webhook signature and delivery id header names, <brand>-Signature and <brand>-Delivery-Id) and the delivery settings webhooks.max_attempts, webhooks.backoff_ms, triggers.max_attempts, triggers.backoff_ms, cron.max_attempts, cron.backoff_ms, delivery.backoff_cap_ms (milliseconds) and delivery.retain_days (days a delivered record stays replayable, 0 keeps none), each a whole number (the attempt counts at least 1), and refuses a value that does not suit them. A server-wide runtime limit (jsc.*, queue.*, objects.*) is a platform setting and is refused here with SETTING_IS_PLATFORM
|
|
1372
|
+
operation(241, "settings.set", "Set one app setting: body key and value (a string, or true or false). Any key is stored; substrio reads changes.history (keep change history so change streams can resume) and webhooks.brand (the webhook signature and delivery id header names, <brand>-Signature and <brand>-Delivery-Id) and the delivery settings webhooks.max_attempts, webhooks.backoff_ms, triggers.max_attempts, triggers.backoff_ms, cron.max_attempts, cron.backoff_ms, delivery.backoff_cap_ms (milliseconds) and delivery.retain_days (days a delivered record stays replayable, 0 keeps none), each a whole number (the attempt counts at least 1), and refuses a value that does not suit them. A server-wide runtime limit (jsc.*, queue.*, objects.*) is a platform setting and is refused here with SETTING_IS_PLATFORM", [
|
|
1274
1373
|
route("PUT", "/v1/_settings", [], [], true),
|
|
1275
1374
|
route("POST", "/v1/_settings", [], [], true),
|
|
1276
1375
|
]),
|
|
@@ -1297,6 +1396,9 @@ export const OPERATIONS = Object.freeze([
|
|
|
1297
1396
|
operation(700, "source.search", "Search the text units of a version (the route's {version}) for pattern: exact bytes, or a regular expression with regex:true (linear time, line by line). glob narrows the files; context adds lines before and after. Answers {version, matches: [{path, line, text, before, after}], count, truncated}, bounded by max_matches and max_bytes", [
|
|
1298
1397
|
route("GET", "/v1/_commits/{version}/search", ["version"], ["max_bytes","max_matches","branch","glob","pattern","regex","context"], false),
|
|
1299
1398
|
]),
|
|
1399
|
+
operation(778, "source.shell", "Run one shell command over the app, the way you work on a checkout: no process, a POSIX shell subset interpreted in the server. Mounts: /src is the branch's tree (writable, default main, the starting directory), /site the live release's site/ (read only), /files the app's stored files (writable; rm there is refused, file.delete does it). Any other path is refused. Builtins: grep (-r -n -l -L -c -i -v -w -o -q -E -F -A -B -C -m --include --exclude), sed (-n -E -i -e, addresses N $ /re/ ranges !, s///g with \\1 groups, p d q a i c y { }), find (-name -iname -path -type -maxdepth -mindepth -empty -exec {} + -delete), cat, head, tail, wc, sort, uniq, cut, tr, diff (unified), ls, cd, pwd, mv, cp, rm, mkdir, touch, echo, printf, tee, test and [ ], xargs, basename, dirname, true, false, commit -m, and js (a script in the app's own runtime with fs.readFile, writeFile, readdir, stat, exists, rm, rename, mkdir on the same mounts; input is stdin). Variables: $PWD $HOME $SRC $SITE $FILES $BRANCH $?. Every /src change of one call lands as ONE commit (message: the command, or commit -m) when the command ends with status 0; any other status, or any refusal, lands nothing. No state is kept between calls. Answers {stdout, stderr, exit_code, offset, truncated, next_offset, commit: {version, number, changed}}; output past the budget is cut with truncated:true and continued with offset. Examples: grep -rn 'TODO' /src; sed -n 10,40p src/app.js; find /src/functions -name '*.js' | xargs sed -i 's/oldName/newName/g'; cat > /src/site/about.html <<'EOF' (lines) EOF; js 'fs.writeFile(\"notes.txt\", input.toUpperCase())' < notes.txt", [
|
|
1400
|
+
route("POST", "/v1/_source/shell", [], [], true),
|
|
1401
|
+
]),
|
|
1300
1402
|
operation(673, "source.tree", "List the paths a version holds (the route's {version}: an id, a number, a branch, live or a sandbox name), ordered by path, each with its kind (file or dir). prefix walks only that directory; glob keeps the files whose path matches (* and ? also match /); sizes:true adds each file's size. Answers {version, entries, count}", [
|
|
1301
1403
|
route("GET", "/v1/_commits/{version}/tree", ["version"], ["branch","prefix","glob","sizes"], false),
|
|
1302
1404
|
]),
|
|
@@ -1306,6 +1408,15 @@ export const OPERATIONS = Object.freeze([
|
|
|
1306
1408
|
operation(474, "sql.schema", "Describe the SQL catalog from the engine's typed descriptors: tables and views with columns (name, declared type, nullable, default, key, generated), indexes (columns with order, unique), foreign keys, and routines; pass table for one table", [
|
|
1307
1409
|
route("GET", "/v1/_sql/schema", [], ["table"], false),
|
|
1308
1410
|
]),
|
|
1411
|
+
operation(755, "subscriptions.cancel", "End one of the signed-in person's own subscriptions now: a paid one leaves the account's provider subscription that pays it (that line, or the whole subscription when it was the last) and the person falls back to what else they hold there (a bundle's plan, else the app's default). A bundle child ends only with its Substrio plan (SUBSCRIPTION_NOT_FOUND). The person's own session only, never a key or an agent (SUBSCRIPTION_PERSON_REQUIRED). Answers the subscription, ended", [
|
|
1412
|
+
route("POST", "/v1/_subscriptions/{id}/cancel", ["id"], [], false),
|
|
1413
|
+
]),
|
|
1414
|
+
operation(754, "subscriptions.list", "List an app's subscribers a page at a time: every subscription to its plans (a purchase, a bundle child of a Substrio plan, or a grant), by subject (an account, a person, or an app's own user) and id. The caller's own app, or (app) another app of the same owning account; any other app's is refused SUBSCRIBERS_NOT_VISIBLE", [
|
|
1415
|
+
route("GET", "/v1/_subscriptions", [], ["app","cursor","limit"], false),
|
|
1416
|
+
]),
|
|
1417
|
+
operation(753, "subscriptions.mine", "List the signed-in person's own subscriptions at every app, active and ended: what they bought for themselves (a cart for the person), each Substrio plan's bundle children, and the plans apps granted them. A person's session only (at the console or at any app); a key is refused SUBSCRIPTION_PERSON_REQUIRED", [
|
|
1418
|
+
route("GET", "/v1/_subscriptions/mine", [], [], false),
|
|
1419
|
+
]),
|
|
1309
1420
|
operation(211, "sync.push", "Push replication operations", [
|
|
1310
1421
|
route("POST", "/v1/_sync", [], [], true),
|
|
1311
1422
|
]),
|
|
@@ -1412,6 +1523,9 @@ export const OPERATIONS = Object.freeze([
|
|
|
1412
1523
|
route("POST", "/v1/tenants/{slug}/resume", ["slug"], [], false),
|
|
1413
1524
|
route("POST", "/v1/projects/{slug}/resume", ["slug"], [], false),
|
|
1414
1525
|
]),
|
|
1526
|
+
operation(788, "tenant.storage", "Read what this app's folder holds on disk (disk_bytes, as of the last fold, each second) by kind (database, files, logs, backups, code, other), its storage limit and the room left (null while no limit is set). The storage limit is held against disk_bytes; tenant.usage's used_bytes and blob_bytes count only document and file bytes", [
|
|
1527
|
+
route("GET", "/v1/_storage", [], [], false),
|
|
1528
|
+
]),
|
|
1415
1529
|
operation(576, "tenant.suspend", "Suspend a tenant with a typed reason and an optional note", [
|
|
1416
1530
|
route("POST", "/v1/_tenants/{slug}/suspend", ["slug"], [], true),
|
|
1417
1531
|
route("POST", "/v1/tenants/{slug}/suspend", ["slug"], [], true),
|
|
@@ -1428,7 +1542,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
1428
1542
|
operation(619, "tenant.usage.history", "Read this app's KPI history, oldest first: one point per finished minute (step=minute, the default; kept two days) or hour (step=hour; kept 90 days), each with what the app's function calls added in it (calls, status classes, latency buckets and percentiles, refusals, CPU and wall time) and its stored documents and bytes at its end. since and until are Unix seconds (default: the last hour of minutes, or the last two days of hours); limit caps the points. Counted on the node that answers", [
|
|
1429
1543
|
route("GET", "/v1/_usage/history", [], ["limit","since","until","step"], false),
|
|
1430
1544
|
]),
|
|
1431
|
-
operation(484, "tenant.usage.self", "Read this tenant's own exact usage and limits: storage bytes, documents, requests per minute (a limit is null until one is set) and the function limits (cpu, memory, concurrency, timeout, body and value sizes)
|
|
1545
|
+
operation(484, "tenant.usage.self", "Read this tenant's own exact usage and limits: storage bytes, documents, requests per minute (a limit is null until one is set) and the function limits (cpu, memory, concurrency, timeout, body and value sizes), for the tenant the key belongs to", [
|
|
1432
1546
|
route("GET", "/v1/_usage", [], [], false),
|
|
1433
1547
|
]),
|
|
1434
1548
|
operation(309, "thread.ask", "Ask one question in an agent thread by id; see the schema", [
|
|
@@ -1536,7 +1650,7 @@ export const OPERATIONS = Object.freeze([
|
|
|
1536
1650
|
operation(635, "version.changes", "What one commit changed (the route's {version}): every path that differs from the version it is compared against, added, removed or changed, with the object ids on both sides. against names another version (default: the commit's first parent, so the answer is the commit's own change)", [
|
|
1537
1651
|
route("GET", "/v1/_commits/{version}/changes", ["version"], ["branch","against"], false),
|
|
1538
1652
|
]),
|
|
1539
|
-
operation(489, "version.check", "Check an app version before it is pushed or made live: the body is the version.create body. Runs function.check over every function whose source is uploaded (parsed as script, CommonJS or ES module, with deploy's require() refusal) and answers ok, diagnostics (function, severity, message, line, column) and the blobs still missing. Nothing is stored or run. version.create and version.live.set run this same check unless they are sent check:false", [
|
|
1653
|
+
operation(489, "version.check", "Check an app version before it is pushed or made live: the body is the version.create body, or {version} naming one already committed (a number, an id, a branch, live or a sandbox's name; no manifest). Runs function.check over every function whose source is uploaded (parsed as script, CommonJS or ES module, with deploy's require() refusal) and answers ok, diagnostics (function, severity, message, line, column) and the blobs still missing. Nothing is stored or run. version.create and version.live.set run this same check unless they are sent check:false", [
|
|
1540
1654
|
route("POST", "/v1/_versions/_check", [], [], true),
|
|
1541
1655
|
]),
|
|
1542
1656
|
operation(632, "version.commit", "Write one commit on a branch of the app's history: the units it changes (bytes as text, as base64, or the id of an object the app already holds, such as the hash site.blob.upload answered), the paths it removes, and what it did. This is how a file is written or deleted: one file is {message, units: [{path: \"site/index.html\", text: \"<h1>Hi</h1>\"}]}, one removal is {message, removals: [\"site/old.css\"]}; no hash is computed by the caller. A function is functions/<name>.js with its routes in functions/<name>.json ({\"routes\": [\"GET /hello\"]}), a site file site/<path>. The answered number is promotable at once: version.live.set {version: number}, with sandbox to run it there first. mode changes applies them to the parent version, mode snapshot makes the units the whole content. expected is the version the branch must still point at, so a racing commit is refused rather than overwritten. Answers {version, root, number, generation, branch, blobs_written, trees_written}", [
|