foldrun 0.9.0 → 1.1.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.
package/bin/foldrun.mjs CHANGED
@@ -38,6 +38,7 @@
38
38
  // foldrun eval new <name> a new eval in a deployed workspace
39
39
  // foldrun agent run <name> run one agent once on a platform, no flow
40
40
  // foldrun tool test <name> exercise one tool alone — no model, no run
41
+ // foldrun hook <verb> ls / add / test / rm — your own code at every tool call and step
41
42
  // foldrun deploy [dir] push this account — or one workspace — into an installation
42
43
  // foldrun pull bring the platform's account down into this folder
43
44
  // foldrun status what differs here from what is deployed — --platform [--history]: is the platform up
@@ -64,6 +65,11 @@ import { fileURLToPath } from "node:url";
64
65
  import fs from "node:fs";
65
66
  import path from "node:path";
66
67
  import process from "node:process";
68
+ import { guardTerminal } from "../src/terminal.mjs";
69
+
70
+ // Before anything prints: a platform's text never writes escape sequences to
71
+ // a person's terminal (src/terminal.mjs). Pipes and files are untouched.
72
+ guardTerminal();
67
73
 
68
74
  const HERE = path.dirname(fileURLToPath(import.meta.url));
69
75
 
@@ -101,6 +107,7 @@ const HELP = `foldrun — agents are just folders
101
107
  foldrun agent run <name> run one agent once on the platform (--task "…", --to, --wait, --test)
102
108
  foldrun agent link <agent> add to its team, in its frontmatter: --subagent <worker>, --consult <agent> or --can-ask (--description for a worker with none)
103
109
  foldrun agent unlink <agent> take one away: --subagent <name>, --consult <name> or --can-ask
110
+ foldrun agent network <agent> the sites its sandbox may reach; set the level (open, trusted, none, inherit) and --add / --remove <site> (repeatable)
104
111
  foldrun agent import <workspace>/<agent> copy an agent you already have in another workspace into this one — its agent.md, own skills and scripts, not its memory (--as <name>, --workspace <name>; --to <ws> on the platform)
105
112
  foldrun agent import --list what could be imported into a deployed workspace: every other workspace you can read, and its agents (--to, --json)
106
113
  foldrun flow add <flow> <pattern> one canvas block as a markdown edit: chain, parallel, router, fan-out, loop, approval, ask, wait, rescue, subflow — diff shown, checked, --dry-run
@@ -116,7 +123,13 @@ const HELP = `foldrun — agents are just folders
116
123
  foldrun flow copy-step <flow> --step <n|agent>[,<n>…] the steps' markdown on stdout, as the canvas's Copy
117
124
  foldrun flow paste <flow> step markdown from stdin or --file in as new groups (--after <g>, default last) or beside one (--group <g>) — diff, checked, --dry-run
118
125
  foldrun flow draft "<what it should do>" Draft with AI on the platform (--to; --flow <name> redrafts one): shows the files and the check, saves on yes (--yes, --dry-run)
126
+ foldrun assistant ["<message>"] the dashboard's assistant (also: foldrun ai): answers from the foldrun docs and the deployed workspace's files, writes agents, flows, tools and skills as diffs with the check, saves on yes (--to, --yes, --dry-run, --json); every reply is kept — --conversation <id> carries one on; no message in a terminal is a conversation (/save, /files, /drop, /new, /quit)
127
+ foldrun assistant history your kept conversations, newest first (--q <text> searches them, --workspace, --json); show <id> prints one, rm <id> deletes it
119
128
  foldrun tool test <name> exercise one tool alone, no model and no run (key=value args, --to)
129
+ foldrun hook ls the hooks in this workspace and the account library: on, tool, description — invalid ones with the reason (--json); --to <ws>: the DEPLOYED workspace's, who turns each on, and what they denied, rewrote or failed lately (--runs <n>, --all)
130
+ foldrun hook add <name> --on <event> a hook folder: hook.md and a run.mjs that allows everything, the contract in its comments (--tool <pattern>, --library, --to)
131
+ foldrun hook test <name> run one hook here on a sample event, as a step would: its answer and how long it took (--event '<json>'|@file; --to <workspace>: the deployed one, on the platform)
132
+ foldrun hook rm <name> delete a hook folder — says which agents still turn it on, asks (--yes, --library)
120
133
  foldrun extract [dir] move single-file script tools into folders (tool.md + run.*)
121
134
  foldrun run <target> run an agent or flow HERE (target: name, or flow:name) — on the platform: flow run / invoke a flow, agent run one agent
122
135
  foldrun eval [name] run one eval, or all of them (--to <workspace>: the deployed evals, run on the platform)
@@ -171,10 +184,11 @@ const HELP = `foldrun — agents are just folders
171
184
  foldrun history [path] every change to a deployed workspace, newest first (--id <revision> for its diff, --limit, --to)
172
185
  foldrun history restore <path> --id <revision> put one file back as that revision left it, as a new revision — diff, asks (--yes, --dry-run, --to)
173
186
  foldrun repo ls a workspace's branches and tags — also diff <branch>, deploy <ref>, merge <branch> (both ask first), rm-branch <branch>, mirror <git-url|off>, mirror-now (--to)
174
- foldrun restore <ws> --to <commit|time|3d> put a workspace's source back as it was then — shows the change, asks for the name typed back (--dry-run: the diff only, --yes)
187
+ foldrun restore <ws> --to <commit|time|age> put a workspace's source back as it was then — shows the change, asks for the name typed back (--dry-run: the diff only, --yes)
175
188
  foldrun backups how the account is backed up (encrypted?), when it last was, the snapshots it is in — also request --what runs|state|storage|everything --at "<when>" [--to <ws>] [--note]
176
189
  foldrun backups requests every restore asked of the platform team, and where each stands (--json)
177
- foldrun secrets set NAME store a secret (prompted, never echoed) — also ls, rm, status; --kind file|ssh|api|service-account|m2m for the other shapes
190
+ foldrun secrets set NAME store a secret (prompted, never echoed) — also ls, rm, status; --kind file|ssh|api|service-account|m2m for the other shapes; --site <domain> (repeatable) binds it as it is stored
191
+ foldrun secrets sites NAME [site…] bind a secret to sites: the egress proxy fills it for those alone and it never enters a sandbox; no sites lists them, --clear unbinds
178
192
  foldrun secrets clients the OAuth clients saved on the platform — also add <name> (--provider or --authorize-url/--token-url, --client-id, --scopes), rm <name>
179
193
  foldrun connect NAME OAuth sign-in from the terminal, stored as an auto-refreshing secret — --client <saved> runs it from a saved client through the platform
180
194
  foldrun deploy [dir] push the whole account, or deploy <workspace> for one of them
@@ -188,7 +202,7 @@ const HELP = `foldrun — agents are just folders
188
202
  foldrun workspaces show <name> what is deployed in one: its agents, flows and how each fires, its files by folder (--json)
189
203
  foldrun workspaces new <name> --platform make it on the platform: blank (an AGENTS.md), or --starter for the example researcher, writer and flow
190
204
  foldrun workspaces demo make the demo pipeline workspace on the platform — "Try the demo pipeline"
191
- foldrun library the account's shared library on the platform: ls [skills|tools|scripts|knowledge|memory], cat <kind>/<path>, put <kind>/<path> (--file, else stdin), rm <kind>/<path> (asks)
205
+ foldrun library the account's shared library on the platform: ls [skills|tools|hooks|scripts|knowledge|memory], cat <kind>/<path>, put <kind>/<path> (--file, else stdin), rm <kind>/<path> (asks)
192
206
  foldrun library new <kind>/<name> a new one from the platform's template, as the Library page's New (--template <transport or gallery entry>, --language)
193
207
  foldrun find <words> search the account as the dashboard's ⌘K does: workspaces, agents, flows, tools, skills, knowledge, memory, runs (--json)
194
208
  foldrun invoke <flow> start a flow on a running platform (--to <workspace>; --once <key> so a retry never starts a second run; --tag <t> repeatable)
@@ -196,8 +210,8 @@ const HELP = `foldrun — agents are just folders
196
210
  foldrun source new <kind> <name> the dashboard's New for any document — tools, knowledge, memory, skills, scripts (agents, flows, evals too): the platform writes the template (--agent <a> inside one agent, --template, --language, --to)
197
211
  foldrun open [page] the dashboard for this workspace, in the browser
198
212
  foldrun api spec the platform's OpenAPI 3.1 document (GET /api/openapi.json) — stdout, or --out <file>
199
- foldrun api version the API version this account's requests get, the versions served, the rate limits — also pin <YYYY-MM-DD>, unpin (admin; --json)
200
- foldrun changelog the platform's release notes, newest first: features, fixes, docs (--limit <n>, --json)
213
+ foldrun api version the API version this account's requests get, the versions served, the rate limits, your last 30 days by version and any deprecated route you still call — also pin <YYYY-MM-DD>, unpin (admin; --json)
214
+ foldrun changelog the platform's release notes, newest first: features, fixes, docs — and the API's versions, breaking changes and deprecations (--limit <n>, --json)
201
215
  foldrun version this CLI's version, its core's, and the platform's: release, API version, component shas — warns when the platform ships a newer CLI (--json)
202
216
 
203
217
  Signing in
@@ -278,18 +292,26 @@ Options
278
292
  --step <n> approve, reject: decide only that step; default is every step that is waiting
279
293
  --payload <json|text|@f> approve: release a step waiting on wait: event with this body, recorded as you
280
294
  --note "<text>" approve, reject: guidance the agent reads — or the reason for a refusal; billing credit: the ledger line's note
281
- --yes approve, stop, rerun (bulk), flow rotate-hook, flow draft, history restore, restore <ws>, import, billing plan, repo, workspace set name, and every rm/revoke/unshare: skip the confirmation, deliberately (required with no terminal)
282
- --json report: the raw run record instead of the report; observe, usage, api version, changelog, find, preferences, workspace vocabulary, flags, workspaces, workspaces show, agent ls, flow ls, agent import --list, backups requests, team: the raw document; version: {cli, core, platform}
295
+ --yes approve, stop, rerun (bulk), flow rotate-hook, flow draft, assistant, history restore, restore <ws>, import, billing plan, repo, workspace set name, and every rm/revoke/unshare: skip the confirmation, deliberately (required with no terminal)
296
+ --json report: the raw run record instead of the report; observe, usage, api version, changelog, find, preferences, workspace vocabulary, flags, workspaces, workspaces show, agent ls, flow ls, agent import --list, backups requests, team, hook ls: the raw document; version: {cli, core, platform}
283
297
  --events <a,b> account set notify: failed, awaiting-approval, completed
284
298
  --failed webhooks deliveries: only the ones that gave up
285
299
  --limit <n> runs, billing, history, changelog: how many rows
286
300
  --value "<text>" secrets set: skip the prompt (careful with shell history)
301
+ --site <domain> secrets set, secrets sites: a site the secret is bound to — the egress proxy fills it for those alone (repeatable, or a,b)
302
+ --clear secrets sites: unbind it
303
+ --add <site> agent network: add a site to its network: list (repeatable)
304
+ --remove <site> agent network: take a site off its network: list (repeatable)
287
305
  --out <file> api spec: where to write the OpenAPI document (default: stdout)
288
306
  --file <path> source put, library put, flow paste: the local file to send (default: stdin); secrets set --kind file|service-account: the file it holds; storage get, report get, report live, account export, export, billing statement: where to write it (- for stdout)
289
307
  --message "<why>" source put: recorded on the file's revision
290
308
  --account secrets: account scope instead of the workspace's
291
309
  --wait invoke, agent run: hold on and print the result
292
310
  --path <p> tool test: the path an http tool should probe, appended to its base:
311
+ --on <event> hook add: what it listens for — tool.call, tool.result, step.start or step.end
312
+ --tool <pattern> hook add: which tools a tool.call or tool.result hook sees — Bash, Write|Edit, mcp__* (default all)
313
+ --library hook add, hook rm: in the account library (every workspace), not this workspace
314
+ --event <json|@file> hook test: the event the hook reads on stdin (default: a sample for its on:)
293
315
  --watch invoke: follow the run's trace here as it happens
294
316
  --print open: print the URL only
295
317
  --from <n> invoke, flow run, rerun: start at step n; earlier steps are recorded as skipped (a bulk rerun defaults to 1)
@@ -305,7 +327,7 @@ Options
305
327
  --grace <span> keys rotate: keep the old key working for 1h or 24h while you swap the new one in (default 0: revoked now)
306
328
 
307
329
  Platform options (deploy, invoke, secrets, logs, keys)
308
- --to <workspace> workspace on the platform (deploy default: folder name); flow add/show, agent link: edit or show the deployed copy instead of this folder; restore <ws>: the commit, date or age to put it back to (3d, 2026-09-28T14:00)
330
+ --to <workspace> workspace on the platform (deploy default: folder name); flow add/show, agent link: edit or show the deployed copy instead of this folder; hook test: run the deployed hook on the platform; hook ls: list the deployed workspace's hooks and what they did lately; restore <ws>: the commit, date or age to put it back to (3d, 2026-09-28T14:00)
309
331
  --tenant <name> account to deploy into (default: default, local only)
310
332
  --data <dir> the installation's data directory
311
333
  --url <url> a running platform (or FOLDRUN_URL, or where you last signed in)
@@ -317,7 +339,7 @@ Platform options (deploy, invoke, secrets, logs, keys)
317
339
  --local deploy: into the installation on this machine, even when signed in
318
340
  --commit <sha> deploy: record which commit this is
319
341
  --overwrite import: replace files the workspace already has with different text (refused otherwise)
320
- --dry-run deploy: check and report, change nothing; flow add, flow set, flow trigger, flow move-step, flow paste, flow draft, history restore, agent link, agent unlink: show the diff, write nothing; restore <ws>: the diff only, restore nothing; import: the preview only, write nothing; stop, rerun (bulk): list what matches, touch nothing
342
+ --dry-run deploy: check and report, change nothing; flow add, flow set, flow trigger, flow move-step, flow paste, flow draft, assistant, history restore, agent link, agent unlink: show the diff, write nothing; restore <ws>: the diff only, restore nothing; import: the preview only, write nothing; stop, rerun (bulk): list what matches, touch nothing
321
343
  --no-runtimes deploy: do not wait for the workspace's environments to be built
322
344
  --force deploy: deploy even while runs are in flight; pull, storage get: overwrite local files
323
345
  --yes deploy: allow deleting files this folder no longer has (asked otherwise; required with no terminal)
@@ -338,9 +360,9 @@ if (!command || command === "--help" || command === "-h") {
338
360
  // `--value` as the account's argument and stored an empty secret; `--force
339
361
  // ./dir` swallowed the directory. A flag followed by another flag is also
340
362
  // boolean, so an unlisted switch at least does not eat its neighbour.
341
- const BOOLEAN_FLAGS = new Set(["account", "follow", "force", "oauth2", "wait", "watch", "print", "dry-run", "help", "no-browser", "local", "test", "flat", "yes", "platform", "json", "forever", "all", "check", "new-client", "quiet", "off", "can-ask", "preview", "csv", "failed", "starter", "history", "list"]);
363
+ const BOOLEAN_FLAGS = new Set(["account", "follow", "force", "oauth2", "wait", "watch", "print", "dry-run", "help", "no-browser", "local", "test", "flat", "yes", "platform", "json", "forever", "all", "check", "new-client", "quiet", "off", "can-ask", "preview", "csv", "failed", "starter", "history", "list", "library", "clear"]);
342
364
  // Flags said more than once collect into a list: `--tag a --tag b`.
343
- const REPEATABLE = new Set(["tag", "expect", "header"]);
365
+ const REPEATABLE = new Set(["tag", "expect", "header", "site", "add", "remove"]);
344
366
  // `--wait` is a switch everywhere but `flow add <flow> wait`, where it takes
345
367
  // a span: `--wait 30m`, `--wait event`. Only a value that reads as one is
346
368
  // taken, so `invoke x --wait` and friends are as they were.
package/docs/agents.md CHANGED
@@ -60,10 +60,12 @@ the point of the week; a step that weighs two arguments and picks one is not.
60
60
  | `agents` | colleagues it may consult mid-run — an answer, no tools |
61
61
  | `subagents` | colleagues it may delegate a whole job to — own context, own tools, never more than its own ([below](#delegating-to-a-sub-agent)) |
62
62
  | `scripts` | programs in `scripts/`, each becoming a callable tool |
63
+ | `hooks` | your own code at every tool call, step start and step end — refuse, rewrite, add context — the same on every model ([Hooks](hooks)). Also in a workspace's or the account's `AGENTS.md` for every agent under it |
63
64
  | `apis` | an HTTP API declared inline, as one tool |
64
65
  | `mcpServers` | an MCP server declared inline |
65
66
  | `secrets` | vault entries its tools may use — names only, never values |
66
67
  | `limits` | the most calls one step may make — per tool, per web action, or in all: `limits: {web.search: 40, crm: 20, calls: 300}`. A call past a limit is refused, never run ([Tools](tools#limits--how-many-calls-a-step-may-make)) |
68
+ | `network` | which sites its sandbox may reach: `open`, `trusted` or `none`, or a list of sites added to the trusted list — `network: [api.hubapi.com]`. Unset, an agent that may fetch or browse the web is `open` and every other `trusted`. Also in a workspace's or the account's `AGENTS.md` ([Network](network)) |
67
69
  | `permissionMode` | `plan` makes the run read-only, whatever else was granted |
68
70
  | `web` | the `web` tool's settings: `actions:` — which of its eight it may use (all when absent) — and who does each, `web: {search: brave, fetch: jina, browse: browserbase, crawl: firecrawl}`, with the key named in the vault. Unset is foldrun's own. `search:` also takes a block of the own engine's settings — engines, categories, safesearch, plugins ([Tools](tools#tuning-the-accounts-own-engine)); `browse:` a block of the browser's ([below](#webbrowse--which-browser-and-how-it-presents-itself)), and `browse:` alone cascades from `AGENTS.md`. The providers and their keys: [Tools](tools#reaching-the-web). A per-action key outside the block is not read — `check` errors on it |
69
71
 
@@ -476,7 +478,7 @@ take, like `eval` or `download`; a misspelt one is refused by `check`).
476
478
  Beside them: `boundaries: true` marks the page's words in every reply,
477
479
  `init:` runs workspace scripts before each page's own, `extensions:` loads
478
480
  unpacked Chrome extensions, `webgpu: true`, `ignore_https_errors: true`, and
479
- `state_key:` names a secret that encrypts saved logins; `video: true`
481
+ `state_key:` names a secret that encrypts saved logins; `state:` names a saved login every call uses (beside `cookies:` it needs `state_key:`, and the secret then seeds it once — see tools.md); `video: true`
480
482
  records every call for the run page to play, and `live_view: false` stops
481
483
  the page being shown live there. See
482
484
  [What this agent's browser may do](tools#what-this-agents-browser-may-do).
package/docs/api-usage.md CHANGED
@@ -50,8 +50,22 @@ The CLI sends the version it was built for on every call.
50
50
  replaces it is released. A pin to a retired version is answered as the
51
51
  current version, never refused.
52
52
  - Every breaking change is announced in **What's new** (the dashboard's
53
- changelog, and `GET /api/changelog`) on the day it ships, with the date the
54
- previous shape stops being served.
53
+ changelog, `GET /api/changelog` and `foldrun changelog`) on the day it
54
+ ships: the version that brought it and what changed, and every deprecated
55
+ route with the date it stops answering.
56
+ - **Checked before it ships.** Every change to the platform is compared with
57
+ the version that is deployed. A route or documented field taken away, a
58
+ type changed, or a request field made required cannot ship unless it comes
59
+ as a new dated version, with the old shape kept for older pins. A route
60
+ can only be removed once its deprecation has passed its sunset date.
61
+ - **You are told if it affects you.** Every request is counted by the
62
+ version that answered it, and a call to a deprecated route by the key or
63
+ person that made it. **Settings → API** and `foldrun api version` show the
64
+ last 30 days. An account that called a deprecated route in the last 30
65
+ days is emailed when it is first seen doing so, and again 90, 30 and 7
66
+ days before the sunset — one email per step, naming the keys that still
67
+ call it. This is required mail: it cannot be switched off, because it is
68
+ the only warning before something you rely on stops.
55
69
  - A route on its way out answers with
56
70
  [`Deprecation`](https://www.rfc-editor.org/rfc/rfc9745) (`@<unix time>` it
57
71
  was deprecated) and, once the date is fixed,
package/docs/api.md CHANGED
@@ -65,7 +65,7 @@ api workspaces/$WS/flows/publish/run -X POST # start a flow
65
65
 
66
66
  | Route | Methods | |
67
67
  |---|---|---|
68
- | `/api/workspaces` | GET, POST | list; create (`name`, plus one of: `template: true` — blank, just an AGENTS.md, what the dashboard sends; `starter: true` — the example researcher, writer and publish flow; `demo: true`; `files`) |
68
+ | `/api/workspaces` | GET, POST | list; create (`name`, plus one of: `template: true` — blank, just an AGENTS.md, what the dashboard sends; `starter: true` — the example researcher, writer and publish flow; `demo: true`; `files`). A POST to a name that exists deploys over it; add `ifAbsent: true` to create only — a name that exists is then `409` and nothing changes, decided atomically (the assistant's Create workspace and `foldrun assistant` send it) |
69
69
  | `/api/workspaces/<ws>` | GET, PATCH, DELETE | read; rename or re-describe (`name`, `description`), or set its defaults (`timezone`, `budget`, `notify` — null clears one, edited in AGENTS.md in place); delete |
70
70
  | `/api/workspaces/<ws>/vocabulary` | GET | which document kinds this workspace admits — its `docs` list is what `[[ ]]` resolves: knowledge, memory, `state/` and `storage/` files, plus the folders themselves |
71
71
 
@@ -145,12 +145,35 @@ Left out, `test` is decided by the platform:
145
145
  a preview workspace's runs are tests unless the flow says `live: true`;
146
146
  everywhere else a run is live.
147
147
 
148
+ ## Assistant
149
+
150
+ | Route | Methods | |
151
+ |---|---|---|
152
+ | `/api/workspaces/<ws>/assistant` | POST | one reply from the assistant, which answers from the foldrun docs and this workspace's files only and writes agents, flows, tools and skills: `{ messages: [{ role: "user"\|"assistant", content }], files?: [{ path, content }], page?, conversation? }` — the conversation so far (the last message the person's, at most 8,000 characters), the files it proposed that are not saved yet, and the dashboard page being looked at. Returns `{ ok, reply, files: [{ path, content, before, sha256 }], issues: [{ where, message, level }], sources: [{ doc, section, url }], supply, workspace, usage, chargedUsd }` — `files` is every unsaved proposal as it now stands, checked; `workspace` is `{ name, purpose }` when it proposes a new workspace (free and kebab-case when proposed; create it with `POST /api/workspaces { name, template: true }`, then talk to the assistant there), else `null`; `usage` is `{ inputTokens, outputTokens, costUsd }` across every model call of the reply; `chargedUsd` what the account was charged for it on the platform's models (the same price as a run's tokens), `null` on its own provider; `conversation` is the id it was kept under — send it back as `conversation` to carry on (without one, a new conversation starts). **Writes nothing**; save with the source PUT (`ifMatch` the `sha256`, `null` for a new file). With `Accept: text/event-stream` it streams `status` (what it is reading), `text` (the reply as it is written), then `done` with the same body, or `error`. Needs `workspace:write`; 60 replies an hour per person or key, and on the platform's models 300 a day per account (`429`); at most 200 messages of 60,000 characters each (`413`). Closing the stream does not stop the reply: it finishes (at most $2, 30 tool calls, five minutes) and is charged; a Chat-Completions `provider:` is `422`. On the platform's models the balance is checked first (`402`) and each reply is one ledger line, flow `ai-assistant` |
153
+ | `/api/assistant/conversations?workspace=&q=&limit=` | GET | the asker's kept assistant conversations — a person's own, or a key's own — newest first, across the workspaces they may see: `{ conversations: [{ id, workspace, title, updatedAt, messages, unsaved }] }`. `q` searches the titles and every message; `workspace` narrows to one; `limit` 1–200 (50) |
154
+ | `/api/assistant/conversations/<id>` | GET, DELETE | one kept conversation, whole: `{ id, workspace, title, messages: [{ role, content, meta? }], files, createdAt, updatedAt }` (`meta` is what the dashboard draws under a reply: sources, tokens, charge, a proposed workspace); DELETE removes it. Anyone else's id is `404`, the same as none |
155
+
156
+ It may propose only `flows/<name>.md`, `agents/<name>/agent.md`,
157
+ `skills/<name>/SKILL.md` (and files under the skill's `scripts/`,
158
+ `references/` or `assets/`), and `tools/<name>/tool.md` with its program, or
159
+ `tools/<name>.md`. It reads the workspace's source and the account library,
160
+ never `state/`, `storage/`, runs or secrets. A proposal is checked with the
161
+ deploy's checks and the flow lint; a frontmatter key the docs never name, and
162
+ a value shaped like a credential, are errors.
163
+
164
+ A reply is not computed by the web: it is queued, and the platform's
165
+ assistant workers answer it — a pool of their own that scales with the
166
+ queue, so a burst of questions never slows the dashboard or takes a run's
167
+ turn. At most three replies of one account run at once. Each reply is kept
168
+ as a conversation private to whoever asked; the caller still sends the
169
+ history with each message.
170
+
148
171
  ## Agents and evals
149
172
 
150
173
  | Route | Methods | |
151
174
  |---|---|---|
152
175
  | `/api/workspaces/<ws>/agents` | GET, POST | list; create (`name`) |
153
- | `/api/workspaces/<ws>/agents/<agent>` | PATCH | edit one line of an agent's frontmatter in place, as the flow canvas does: `{ list: { key, name, action } }` — `key` one of `subagents`, `agents`, `tools`; `action` `add` or `remove` — or `{ set: { key: "description", value } }`. Only that key's lines change — comments, key order and prose stay. Answers `{ ok, changed }`; 404 for an unknown agent. `workspace:write` |
176
+ | `/api/workspaces/<ws>/agents/<agent>` | PATCH | edit one line of an agent's frontmatter in place, as the flow canvas does: `{ list: { key, name, action } }` — `key` one of `subagents`, `agents`, `tools`; `action` `add` or `remove` — or `{ set: { key: "description", value } }`, or `{ network }` — the sites its sandbox may reach: `"open"`, `"trusted"`, `"none"`, a list of sites, or `{ level, allow, defaults }`; `null` removes the line so it inherits ([Network](network)). A `network` nobody could read is a 400 that says why. Only that key's lines change — comments, key order and prose stay. Answers `{ ok, changed }`; 404 for an unknown agent. `workspace:write` |
154
177
  | `/api/workspaces/<ws>/agents/<agent>/run` | POST | run one agent alone (`task`; `test: true` for a test run) |
155
178
  | `/api/workspaces/<ws>/agents/import` | GET, POST | copy an agent in from another workspace of the account. GET lists what could be imported (every other workspace you can read, with its agents). POST `{ from, agent, as? }` copies `agents/<agent>/` — its agent.md and own skills/, scripts/, knowledge/; never memory/, outputs/, state/ — and answers `{ name, files, warnings }`; `warnings` names tools, skills, scripts and secrets the copy uses that this workspace lacks. 409 on a name clash (pass `as`), 404 for an unknown workspace or agent, 403 when `from` is outside your key's scope. Idempotent |
156
179
  | `/api/workspaces/<ws>/export` | GET | this workspace, one flow or one agent as a `.zip` — `?kind=flow&name=<flow>` (the flow, every flow it runs as a step, every agent those steps and their colleagues name, and the workspace tools, skills and scripts they grant), `?kind=agent&name=<agent>` (its folder and what it grants), or neither for the whole authored tree. Never memory/, state/, outputs/, storage, runs or a secret's value; `foldrun-package.json` inside says what it is. Any role that can read the workspace |
@@ -158,6 +181,8 @@ everywhere else a run is live.
158
181
  | `/api/workspaces/<ws>/evals` | GET, POST | list; create (`name`) |
159
182
  | `/api/workspaces/<ws>/evals/<eval>/run` | POST | run one eval |
160
183
  | `/api/workspaces/<ws>/tools/<tool>/test` | POST | exercise one tool (`args`, `path`). An http tool with an `operations:` allowlist is probed only with a GET to one of its operations (no `path`: the first GET without parameters), and fails if the allowlist resolves nothing, as a run does |
184
+ | `/api/workspaces/<ws>/hooks` | GET | the [hooks](hooks) this workspace can turn on — its own and the library's (shadowing marked), invalid ones with the reason, and who turns each on — plus `recent`: every deny, rewrite and failure hooks wrote in the last `?runs=` runs (1–500, default 50), newest first; `?all=1` includes notes and context |
185
+ | `/api/workspaces/<ws>/hooks/<name>/test` | POST | run one [hook](hooks) on a sample event: body `{ event }`, answer the hook's own (`ok`, `decision`, `reason`, `input`, `context`, `reply`, `note`, `failure`, `ms`). Shares the `hooks/` path with flow webhooks below — `/test` is only ever a hook's. Answers `404` for no such hook, `422` for an invalid `hook.md`, `400` for an event that is not an object. On an isolated install hook code never runs in the platform's own process, so this answers with where to test it instead (`foldrun hook test` locally, or a test run) |
161
186
 
162
187
  Running an agent alone is the fastest way to test a prompt change without
163
188
  paying for the steps around it.
@@ -291,7 +316,7 @@ its header and requires a URL it can GET for itself.
291
316
 
292
317
  | Route | Methods | |
293
318
  |---|---|---|
294
- | `/api/secrets?workspace=` | GET, PUT, DELETE | the vault: list which secrets exist (never a value); set one (`name` plus one shape, below); delete one (`name`). `workspace` names a workspace's own store; unsaid is the account's, which a key scoped to some workspaces may not touch. PUT is idempotent |
319
+ | `/api/secrets?workspace=` | GET, PUT, PATCH, DELETE | the vault: list which secrets exist (never a value), each with the `sites` it is bound to; set one (`name` plus one shape, below, and optionally `sites`); bind one to sites with PATCH `{ name, sites }` — `sites` a list like `["api.example.com", "*.example.com"]`, `[]` unbinds, the value untouched, 404 for a secret that is not there, 400 for a site with a scheme, path or port and for a file, ssh or api secret ([bound to sites](secrets#bound-to-sites)); delete one (`name`). `workspace` names a workspace's own store; unsaid is the account's, which a key scoped to some workspaces may not touch. PUT is idempotent; PATCH and a PUT with `sites` are audited as `secret.sites` / `secret.set` |
295
320
 
296
321
  `PUT` takes `name` plus exactly one shape: `value` (a string), `file` (a PEM,
297
322
  a cert), `oauth2`, `service_account`, `ssh`, or `api`. They go to the vault;
@@ -317,7 +342,7 @@ Reads return which secrets exist, never their values.
317
342
  | `/api/account/close` | POST | the owner asks to close the account — in person, never a key, with `confirm` set to the account's name and an optional `reason`. Not a delete: the account is suspended at once (runs in flight finish, sign-in and the export still work), `closeRequested` is set on its admin state, and the super admin is mailed to settle the last invoice and any refund. Returns `{ ok, suspended, notified }`; 409 when already asked. Hosted installs only |
318
343
  | `/api/account/backups` | GET | what is backed up for this account: `policy` (`schedule`, `keepLocal`, `keepOffsiteDays`, `encrypted`, `covers`, `excludes`), `last`, `backups` (each nightly archive that holds this account, newest first: `id`, `at`, `bytes` (the encrypted object), `sha256` (the archive inside the encryption — what a decrypt must reproduce), `kinds` — files, database, keys — `verified`, `offsite`, `keepDays`, `encrypted` (sealed with age before it left the server; false for archives taken before October 2026), `recipient` (the key's fingerprint, `sha256:<16 hex>`, or null)), `requests` (earlier restore requests) and `workspaces` (`name`, `head`, `history`, `lastChange`) — what a restore can reach. See [Backups and restore](backups-and-restore.md) |
319
344
  | `/api/account/backups/requests` | GET, POST | ask the platform team to restore what an account cannot restore itself. POST `{ what, target, workspace?, backupId?, note? }` (`what` is runs, state, storage or everything) records the request, mails the operator and audits it as `backup.restore_requested`; answers 201 `{ ok, request }` with `notified` saying whether the mail went. Restores nothing. `workspace:manage`, and a member scoped to some workspaces must name one. GET lists this account's requests |
320
- | `/api/account/api` | GET, PATCH | how this account meets the API — GET `{ version: { current, pinned, effective, supported, supportMonths }, limits: { plan, windowSeconds, perCredential: { read, write }, perAccount: { read, write } } }`; PATCH `{ version: "YYYY-MM-DD" \| null }` pins the version requests without a `Foldrun-Version` header get (null: always current). `keys:manage`. See [api-usage](api-usage.md) |
345
+ | `/api/account/api` | GET, PATCH | how this account meets the API — GET `{ version: { current, pinned, effective, supported, supportMonths }, limits: { plan, windowSeconds, perCredential: { read, write }, perAccount: { read, write } }, usage }`. `usage` is the last 30 days — `{ days, versions: [{ version, requests, lastSeen }], deprecated: [{ route, requests, lastSeen, deprecatedAt, sunsetAt, link, callers: [{ credential, requests, lastSeen }] }] }`, `credential` naming a key by its label and prefix — or `null` when it could not be read; PATCH `{ version: "YYYY-MM-DD" \| null }` pins the version requests without a `Foldrun-Version` header get (null: always current). `keys:manage`. See [api-usage](api-usage.md) |
321
346
  | `/api/account/flags` | GET | the feature flags as they stand for this account — `{ flags: [{ name, description, owner, default, on, source, rollout }] }`, `source` being what decided it: `account` (an override for this account), `global` (the install-wide setting), `rollout` (a percentage of accounts, by a stable hash of account and flag; `rollout` is the percentage) or `default`. Read-only, every role; only the super admin changes a flag. See [Feature flags](feature-flags.md) |
322
347
  | `/api/openapi.json` | GET | this API as an OpenAPI 3.1 document — every route and method, bearer auth, the rate-limit, version and Idempotency-Key headers, and request/response schemas for runs, flow runs, deploy, source, secrets, keys and billing. No key needed. Generated from the route files by `scripts/gen-openapi.mjs`; the dashboard renders it at Docs → API reference, `foldrun api spec` saves it |
323
348
  | `/api/account` | GET, PATCH | the account's defaults from its AGENTS.md frontmatter — `timezone`, `budget` (a USD cap — a number for a month, `"60/day"`, `"60/week"`, or `{ usd, period }`; null is no limit), `notify` (`{ url?, email?, events }`); PATCH sets the keys sent, null clears one, and edits the file in place |
@@ -374,7 +399,7 @@ What a platform runs, and what changed. See [Versions and releases](versions).
374
399
  | Route | Methods | |
375
400
  |---|---|---|
376
401
  | `/api/version` | GET | open, no key. `{ version, released_at, build, api, components: { core, platform, web, docs, infra, cli }, packages: { core, cli }, browsers, browser_versions }` — `version` is `vYYYY.MM.DD.N` (`dev` on an install the deploy did not build); `components` are short commit shas (`cli` is the CLI commit released alongside); `packages.cli` is that CLI's npm version; `api` is the API contract's date version; `browsers` is the engines the runner image was built with (its `/opt/browser/engines.json`, read by the deploy — see [Engines](tools#engines)), `null` when the deploy did not say; `browser_versions` is their versions where the manifest has a dotted one (`{ chromium: "153.0.8010.12", chrome: "154.0.8037.58", lightpanda: "0.4.1" }`), which `foldrun check` compares with a pinned `user_agent`, `null` when none. `Cache-Control: no-store` |
377
- | `/api/changelog?limit=` | GET | `{ version, releases: [{ version, released_at, previous, components, notes }] }`, newest first — 10 by default, at most 50. `notes` is `{ features, fixes, docs, other }`, each a list of `{ repo, sha, subject }` grouped from commit subjects, or `null` when the deploy could not read what changed. Needs a key or a session |
402
+ | `/api/changelog?limit=` | GET | `{ version, api, releases: [{ version, released_at, previous, components, notes }] }`. `api` is the API's own changelog: `{ current, supportMonths, versions: [{ version, changes }], deprecations: [{ route, deprecatedAt, sunsetAt, link }] }` — every version a caller may pin, newest first, with the breaking changes it introduced, and the routes on their way out. `releases` newest first — 10 by default, at most 50. `notes` is `{ features, fixes, docs, other }`, each a list of `{ repo, sha, subject }` grouped from commit subjects, or `null` when the deploy could not read what changed. Needs a key or a session |
378
403
 
379
404
  Every `/api` response carries the version in its headers, refusals included,
380
405
  so a client learns what it is talking to without a second request:
@@ -0,0 +1,139 @@
1
+ # Assistant
2
+
3
+ The assistant is a panel on every page of the dashboard. Ask it how
4
+ foldrun works, or describe what you want built. It writes agents, flows,
5
+ tools and skills for a workspace and shows each one as a diff, checked, for
6
+ you to save.
7
+
8
+ Open it with **Ask AI** at the bottom right. **Draft with AI** on the Flows
9
+ page opens it too, with "Draft a flow that" ready to finish.
10
+
11
+ Inside a workspace it works in that workspace. Anywhere else, such as the
12
+ account home, Billing or the docs, it works in the one chosen in its header;
13
+ your browser remembers the choice. Each workspace keeps its own conversation.
14
+
15
+ ## What it knows
16
+
17
+ Only two things:
18
+
19
+ - **foldrun's docs**, these pages. It searches them and reads the sections
20
+ it needs before it answers, and it cites what it used as
21
+ `[doc › section]`, which links to the heading. Under each reply,
22
+ **Read N doc sections** lists them.
23
+ - **This workspace's files**: its `AGENTS.md`, agents, flows, skills,
24
+ tools, hooks, knowledge, evals and scripts, plus the account library and
25
+ the account's `AGENTS.md`. It never reads `state/`, `storage/`, runs or
26
+ secrets.
27
+
28
+ It has no web access, no shell and no other tools. If the docs do not cover
29
+ something, it says so; it does not guess. Anything that is not about
30
+ building on foldrun gets a one-line refusal. Text inside your files is
31
+ treated as data, never as instructions to it.
32
+
33
+ ## What it writes
34
+
35
+ Only these paths:
36
+
37
+ | Kind | Path |
38
+ |---|---|
39
+ | a flow | `flows/<name>.md` |
40
+ | an agent | `agents/<name>/agent.md` |
41
+ | a skill | `skills/<name>/SKILL.md`, and files under the skill's `scripts/`, `references/` or `assets/` |
42
+ | a tool | `tools/<name>/tool.md` with its program beside it, or `tools/<name>.md` |
43
+
44
+ When something else needs to change, such as `AGENTS.md`, a secret or a
45
+ setting, it tells you what and where, and you make that change.
46
+
47
+ ## New workspaces
48
+
49
+ When you ask for a new workspace, or describe a separate desk or pipeline,
50
+ it proposes one: a name and what it is for, with a **Create workspace**
51
+ button. Creating it needs `workspace:manage` (admin or owner). The workspace
52
+ starts blank, with only its `AGENTS.md`. The dashboard then opens it, and the
53
+ conversation carries on there: the assistant writes the new workspace's
54
+ agents, flows, tools and skills for you to save.
55
+
56
+ It never proposes a name that is taken. An account with no workspace yet
57
+ gets a box to name its first one.
58
+
59
+ Every file it proposes is checked as it writes it, and the model sees the
60
+ result and fixes errors before it finishes:
61
+
62
+ - the [deploy's own checks](deploying) and the flow lint, over the workspace
63
+ as it would be with the file in it;
64
+ - a frontmatter key that appears nowhere in these docs is an error, because
65
+ foldrun would not read it;
66
+ - a value shaped like a credential (an API key, a token, a private key) is
67
+ an error. Secrets go in the vault; see [Secrets](secrets);
68
+ - a skill needs `name:` (its folder) and `description:`;
69
+ - a script tool's `run:` program must be proposed or already exist.
70
+
71
+ Anything still wrong after that is shown beside the file, not hidden.
72
+
73
+ ## Saving
74
+
75
+ Nothing is written until you press **Save** on a file, or **Save all**. Each
76
+ save goes through the same write as the editor: one revision per file, with
77
+ the note "written with the assistant: …", which you can restore from
78
+ [History](backups-and-restore). If a file changed after it was proposed, the
79
+ save is refused and nothing is overwritten; ask the assistant to propose it
80
+ again.
81
+
82
+ Saving is not deploying. On the platform, the saved file is the workspace's
83
+ source, the same as an edit in the dashboard.
84
+
85
+ ## History
86
+
87
+ Every reply is kept. **History** in the panel's header lists your
88
+ conversations, newest first and grouped by day, across every workspace you
89
+ can see; type to search them, by title or by anything said in them. Open one
90
+ to carry it on: the files it proposed and you did not save come back with
91
+ it, and the panel moves to its workspace. **New chat** starts another.
92
+
93
+ Conversations are yours alone. Nobody else in the account, owners included,
94
+ can list or read them; an API key's conversations belong to that key. Each
95
+ person keeps their newest 500.
96
+
97
+ ## Where it runs
98
+
99
+ Not in the dashboard. Each reply is queued and answered by the platform's
100
+ assistant workers, a pool of its own that grows with the queue, so a burst
101
+ of questions never slows the dashboard or takes a turn from your runs. At
102
+ most three replies of one account run at once; more wait their turn, and the
103
+ panel says so. The model only ever reads through the assistant's own tools,
104
+ on the server, scoped to your account and workspace; it runs no code and has
105
+ no network or shell of its own.
106
+
107
+ ## Who can use it, and what it costs
108
+
109
+ You need to be able to edit the workspace (`workspace:write`: editor,
110
+ admin or owner). Viewers do not see the button enabled.
111
+
112
+ Each reply is one model call that may use several tools, on the same models
113
+ your runs use: the workspace's or account's own [provider](providers) or
114
+ key when there is one, otherwise the platform's. On the platform's models
115
+ the balance is checked first, and the reply is charged at the same price as
116
+ a run's tokens, as one line on [usage](budgets-and-billing), flow
117
+ `ai-assistant`. On your own provider or key, that provider bills you and
118
+ foldrun charges nothing.
119
+
120
+ Under every reply is what it used: tokens in (cache reads included) and out,
121
+ and what it was charged, or "on your own model key". One reply is capped at
122
+ $2, 30 tool calls and five minutes. Each person or key can send 60 replies an
123
+ hour, and when the platform's models answer, an account can send 300 a day.
124
+
125
+ **Stop** stops what you see, not the reply: it finishes on the server within
126
+ those caps and is charged, because what a reply cost is only known when it
127
+ ends. Its answer is in **History**.
128
+
129
+ ## From a terminal, and the API
130
+
131
+ `foldrun assistant "<message>" --to <workspace>` (or `foldrun ai`) asks
132
+ once, prints the reply, what it read and every proposed file as a diff, and
133
+ saves on yes. With no message, in a terminal, it is a conversation.
134
+ `foldrun assistant history` lists your kept conversations and
135
+ `--conversation <id>` carries one on. See [CLI](cli).
136
+
137
+ The route is `POST /api/workspaces/<ws>/assistant`. It writes nothing and
138
+ returns the proposals for you to save with the source PUT. See
139
+ [API](api#assistant).
@@ -6,75 +6,47 @@ different things:
6
6
  | | What it brings back | Who does it | How far back |
7
7
  |---|---|---|---|
8
8
  | **A workspace's history** | its agents, flows, knowledge, skills, tools, scripts — the markdown | you, from Settings → Backups, `foldrun restore` or the API | every change it ever had |
9
- | **The nightly snapshot** | everything else: runs, `state/`, storage's index, secrets, people, keys, the ledger, the audit log | the platform team, when you ask | the snapshots kept (below) |
9
+ | **A database dump before a migration** | the database: people, API keys, the ledger and every balance, the queue, the audit log | the platform team, when you ask | the last 10 deploys that changed the schema |
10
10
 
11
11
  You do not need to switch either on.
12
12
 
13
- ## What the nightly snapshot holds
14
-
15
- On the hosted platform today (a single server), a job runs
16
- **nightly at 03:30, server time (Sydney)** and archives every account
17
- together:
18
-
19
- - the whole data volume: every workspace's files — source, `memory/`,
20
- `state/`, run records — each workspace's git history, the account's
21
- encrypted secrets (the vault) and the storage index;
22
- - the database (`pg_dump`): people, API keys, sessions, the ledger and every
23
- balance, the queue, the audit log;
24
- - the server's key file.
25
-
26
- **Encrypted before it leaves the server.** Every file the job writes — the
27
- archive (with the database dump inside it), the list of what it holds, and
28
- the key file — is encrypted with [age](https://age-encryption.org) to a
29
- public key whose private half is **not** on the server: the server can make
30
- a backup and cannot open one, and neither can anyone holding the object
31
- storage it is copied to. The archive is encrypted as it is written, so it is
32
- never stored unencrypted, even on the server. The job refuses to run rather
33
- than copy anything unencrypted. Snapshots taken before this change
34
- (October 2026) were copied off the server unencrypted, all but the key file:
35
- the first encrypted run deletes those copies from object storage, and the
36
- server's own copies of those nights age out of the 14 kept.
37
-
38
- Before an archive is kept it is checked: the database dump must be readable
39
- by `pg_restore --list` and list table data, row counts are taken from the
40
- live database at the same moment, and the archive is listed in full and
41
- its checksum recorded — of the very bytes being encrypted, in the same pass.
42
- An archive that fails the check is deleted and the older ones are kept.
43
- That an encrypted archive decrypts, with the private key, to exactly that
44
- checksum is proved by an automated test of the whole job on every change to
45
- it; the server cannot prove it nightly, because it does not have the key.
46
-
47
- **Kept:** the last 14 on the server, and 15 days in object storage off it
48
- (Cloudflare R2).
49
-
50
- A full restore from one of these archives was performed on 2026-09-05, from
51
- the R2 copy: the archive matched the server's byte for byte and restored the
52
- workspaces, run records, the database and the sealed key file. That was
53
- before the archive itself was encrypted; *not yet performed* on an encrypted
54
- one — the next restore drill is.
13
+ ## There is no nightly snapshot
14
+
15
+ The hosted platform today is a development server, and since 11 October 2026
16
+ it takes **no nightly backup**. Run records, `state/`, storage's index and
17
+ the vault are on its data volume and are not copied anywhere on a schedule.
18
+ The last full archive is from 7 October 2026 (`foldrun-data-20261007-0330`,
19
+ in Cloudflare R2), encrypted with [age](https://age-encryption.org) to a key
20
+ the server does not hold.
21
+
22
+ What remains is the **database dump taken before every deploy that runs a
23
+ new migration**. It is checked with `pg_restore --list` before it is kept,
24
+ encrypted to the same off-server key (the server can write it and cannot
25
+ read it), and the deploy refuses to go ahead without it, because a migration
26
+ has no way back. The newest 10 are kept on the server and a copy goes to R2.
27
+ It holds the database only, not the data volume.
55
28
 
56
29
  ### What it does not hold
57
30
 
58
- - **Storage file bytes.** On the hosted platform the files in a workspace's
59
- Storage live in object storage (R2), not on the data volume; the snapshot
60
- has the index of what exists, not the bytes. *Unverified:* whether a
61
- storage file deleted by mistake can still be recovered from object
62
- storage. Ask, and say when it was deleted.
31
+ - Workspace files, run records, `state/`, `memory/`, the vault and each
32
+ workspace's git history: the data volume is not backed up. A workspace's
33
+ source can still be restored from its own history (below).
34
+ - **Storage file bytes.** These live in object storage (R2), not on the data
35
+ volume. *Unverified:* whether a storage file deleted by mistake can still
36
+ be recovered from object storage. Ask, and say when it was deleted.
63
37
  - Anything that only existed inside a sandbox while a step ran.
64
38
  - What model providers and connected services hold on their side.
65
39
 
66
40
  A self-hosted install backs up however its operator set it up; this page
67
- describes the hosted platform. `foldrun-infra/dev/backup.sh` is the job, if
68
- you want the same for yours: it needs `age` and `FOLDRUN_BACKUP_RECIPIENT`
69
- (your age public key) and will not run without them.
41
+ describes the hosted platform. `foldrun-infra/dev/ci/premigrate-backup.sh`
42
+ is the pre-migration dump, if you want the same for yours: it needs `age` and
43
+ `FOLDRUN_BACKUP_RECIPIENT` (your age public key) and will not run without them.
70
44
 
71
45
  ## Seeing your snapshots
72
46
 
73
- Settings → **Backups** shows how often, how many are kept, when the last one
74
- was taken and whether it was verified, copied off the server and
75
- **encrypted**, and each snapshot that holds your account, newest first —
76
- each one marked encrypted or not (the ones from before October 2026 are
77
- not).
47
+ Settings → **Backups** and `foldrun backups` list the archives recorded for
48
+ your account. With the nightly job gone, nothing new is added to that list:
49
+ it ends at the last nightly run, on 7 October 2026.
78
50
 
79
51
  ```sh
80
52
  foldrun backups # the same, in the terminal
@@ -84,14 +56,10 @@ foldrun backups # the same, in the terminal
84
56
  GET /api/account/backups
85
57
  ```
86
58
 
87
- Each archive the job verifies is recorded with the accounts it holds; an
88
- account sees only the archives it is in, and its own name as the only entry
89
- in `accounts`. Each record says `encrypted` and
90
- names the key it was encrypted to by fingerprint (`recipient`,
91
- `sha256:` and 16 hex digits of the public key) — a change of fingerprint is
92
- a change of key. *Not yet verified on the server:*
93
- the recording landed on 2026-10-01, so the list starts with the first
94
- nightly run after that deploy — older archives exist but are not listed.
59
+ An account sees only the archives it is in, and its own name as the only
60
+ entry in `accounts`. Each record says `encrypted` and names the key it was
61
+ encrypted to by fingerprint (`recipient`, `sha256:` and 16 hex digits of the
62
+ public key).
95
63
 
96
64
  ## Restoring a workspace yourself
97
65
 
@@ -138,8 +106,9 @@ deploy any one of them — the same operation, by commit instead of by time.
138
106
  ## Asking for a restore from a snapshot
139
107
 
140
108
  Run records, `state/`, storage and the account's records (people, keys,
141
- balances) come back only from a nightly snapshot. That restore replaces a
142
- volume and a database every account shares, and the key that opens the
109
+ balances) come back only from an archive: the database from a pre-migration
110
+ dump, the rest only from the last full archive (7 October 2026). That restore
111
+ replaces a volume and a database every account shares, and the key that opens the
143
112
  sealed half of the archive is deliberately not on the server, so it is done
144
113
  by the platform team rather than by a button.
145
114
 
@@ -170,7 +170,7 @@ avatar, with eight tabs:
170
170
 
171
171
  | tab | what it is |
172
172
  |---|---|
173
- | **Balance** | the number, large, with how long it lasts in words, thirty days of spend as a sparkline, what is held against runs in flight, and Add credit with the amount that covers a month already chosen |
173
+ | **Balance** | the number, large, with how long it lasts in words, thirty days of spend as a sparkline, what is held against runs in flight, and Add credit with the amount that covers a month already chosen. While billing is off on an install (self-hosted, or a test platform), Add credit puts credit on by hand instead — no payment, and an empty balance stops nothing then — the same as `foldrun billing credit <usd>` |
174
174
  | **Plan** | the plan you are on, this cycle's credits and when they reset, and the plans you could move to |
175
175
  | **Usage** | what was consumed, from the run records, and what each flow and agent cost by week from the ledger |
176
176
  | **Statement** | the ledger as a statement: grouped by day, human labels, month-to-date, filters, and a CSV for the month |