--- title: "rote architecture — lifecycle and governance" description: "Lifecycle (pull → init → call → cache → query → crystallize → replay), four governance proofs, and the full ~/.rote/ layout." doc_version: "1.1" last_updated: "2026-05-01" canonical: "https://modiqo.ai/docs/rote-architecture.txt" --- # rote — architecture > **Lifecycle: pull adapter → init session → call → cache `@N` → query → crystallize → release → recall → replay. Plus the four governance proofs (fingerprint pinning, write guard, vault portability, archive analytics) and the full `~/.rote/` layout.** How the lifecycle runs, where each piece lives on disk, and the four properties that distinguish rote from a "captured shell script" prototype. ## The lifecycle ```text pull adapter ──► init session ──► call ──► cache @N ──► query ──► crystallize ──► release ──► recall ──► replay │ │ │ │ │ │ │ │ │ spec parser vault token write guard workspace jq engine audit trail draft→released Tantivy fingerprint tool generator refresh nonce check SQLite → frontmatter BM25 index gate bound session audit append Parquet write guard archive ``` Each arrow is enforced — not a marketing diagram. ### 1. Pull adapter `rote adapter pull ` (catalog lookup) or `rote adapter new --spec ` (custom). - The spec parser dispatches by detected format (`openapi3`, `openapi31`, `swagger2`, `discovery`, `graphql`, `mcp`, `data`). - Tools are generated into a uniform shape regardless of source format. - The result is an installed adapter at `~/.rote/adapters//` with a stable **fingerprint** (e.g., `mcp_39UEoxu3DX9zayEgtzXN8HtJxnxy`) hashed from the manifest contents. ### 2. Init session `adapter.initSession()` resolves credentials from the vault (auto-refreshing OAuth if within five minutes of expiry), validates the adapter is callable, and registers a per-workspace session at `~/.rote/adapters//runtime/sessions/workspace_.json`. ### 3. Call A tool call goes through the adapter executor. Before the HTTP request leaves, the **write guard** middleware classifies it. ### 4. Cache `@N` Every response is saved as `@1.json`, `@2.json`, ... in the workspace under `~/.rote//workspaces//.rote/responses/`. The counter is a single SQLite-backed integer per workspace; it never resets within the workspace's lifetime. ### 5. Query `rote @2 '.items[0].id'` — or, in TypeScript, `dex.query(@2).select('.items[0].id').execute()` — runs **jq** against the cached body. Token cost of querying a cached response is roughly two orders of magnitude lower than re-issuing the call. ### 6. Crystallize When a workspace has produced a useful sequence, `rote flow template create --workspace ` reads the SQLite command log and the per-adapter `write_guard.jsonl` audit trail, and emits a TypeScript flow with frontmatter declaring required adapters, fingerprints, parameters, and write permissions. ### 7. Release The flow lands as `draft`. The author tests with new parameters. When confident, they re-export with `--release`, which sets `status: released` in the frontmatter and makes the flow discoverable via `rote flow search`. ### 8. Recall `rote flow search ""` queries the Tantivy BM25 index over released flows. The author chooses one, runs it. If `rote flow infer` is used, parameter values are proposed from pattern matching, environment variables, and user context with confidence scores. ### 9. Replay `rote deno run --allow-all main.ts ` launches the flow. The first thing the flow does is verify the installed adapter fingerprint matches the one frozen in its frontmatter. Mismatch is a hard failure. ## The four governance proofs These are the properties that make rote a product, not a prototype. ### Proof 1 — Fingerprint pinning (anti-drift) A flow's frontmatter declares the fingerprint of every adapter it depends on: ```yaml mcp_servers: adapter/calendar: fingerprint: mcp_39UEoxu3DX9zayEgtzXN8HtJxnxy ``` At runtime, `getExpectedFingerprint("adapter/calendar")` returns this value, and `AdapterChecker.checkEndpoint(...)` compares it to `~/.rote/adapters/calendar/manifest.json`'s current fingerprint. If they differ — because the adapter was re-pulled, the spec changed, or someone replaced it locally — the flow refuses to execute and prints a clear error: ```text ⚠️ Calendar adapter fingerprint mismatch. Expected: mcp_39UEoxu3DX9zayEgtzXN8HtJxnxy Installed: mcp_… Re-export this flow to update fingerprints. ``` A hand-written recipe in `recipes.md` cannot do this. A captured bash script cannot do this. Drift is silent without fingerprint pinning. ### Proof 2 — Write guard (anti-blast-radius) The write guard middleware sits between the executor and the network. Every call is classified into one of four modes: | Mode | Effect | |---|---| | `Allow` | Proceed silently | | `Audit` | Proceed; append a JSONL audit entry | | `Confirm` | Block until a nonce is presented; append entry | | `Deny` | Refuse | Defaults: `GET`, `HEAD` are skipped entirely; `POST`, `PATCH` default to `Audit`; `DELETE`, `PUT` default to `Confirm`. Per-tool overrides live in the adapter's `config/policies.json` (`safe_list`, `confirm_list`, `deny_list`). **The nonce protocol.** A confirm-mode call requires a fresh nonce: 1. The agent calls `create_nonce(adapter, tool)`. A 16-hex-character token is minted, written to `~/.rote/adapters//logs/write_guard_nonces.json` with a 5-minute TTL, and returned. 2. The agent retries the original call with the nonce. The middleware checks the nonce, marks it consumed, and proceeds. 3. The decision is appended to `~/.rote/adapters//logs/write_guard_.jsonl` with timestamp, tool, decision, method, risk level, impact, reason, dry_run flag, flow name, workspace. **Flow blessing.** A blessed flow's `~/.rote/flows//.permissions.json` is HMAC-signed and lists the tools it is permitted to call. When the flow runs, `flow_permits_tool()` verifies the signature, checks the flow's source checksum, and — on match — admits write calls without per-call nonce confirmation. The decision is still audited. The audit trail is what `rote flow template create --workspace` reads to populate `write_permissions` when crystallizing — so the next agent that recalls the flow inherits a precise allowlist of writes the original workspace performed, with nothing more. ### Proof 3 — Vault portability (anti-fragmentation) Tokens live encrypted at `~/.rote/secrets/tokens.json` (AES-256-GCM, machine-keyed, file mode 600). They never enter the flow file. They never enter the registry artifact. They are referenced indirectly by `auth.token_env` in the manifest. For team distribution: `rote vault export ` produces a portable `.dxvt` capsule (`MAGIC | VERSION | JSON header | SALT | NONCE | CIPHERTEXT+TAG`, passphrase-derived via Argon2id). `rote vault push` uploads to the registry; `rote vault pull` downloads. The auto-sync preference pushes after every refresh. For headless machines: `rote provision [--ttl 30]` packages a refresh token into a base64 `dxp_…` capsule with a default 30-minute TTL; `rote claim` decodes it on the headless side. CI / exe.dev / remote shells get authenticated without a browser. A captured shell script with bare bearer tokens in argv has none of this. ### Proof 4 — Archive analytics (telemetry recipes don't have) Workspaces archive to Parquet, not to a single log file. Six tables (ZSTD-compressed): - `metadata.parquet` — per-workspace identity, model provider/name/version, total tokens, total duration, original inference trigger - `responses.parquet` — per-response timing, token counts (request/response/total/encoding), binary metadata - `commands.parquet` — per-command type, response IDs produced, dependencies, query expressions, *token savings* from query refinement - `dependencies.parquet` — which response field this call's request body referenced - `variables.parquet` — named variables and their provenance - `sessions.parquet` — session bindings: endpoint, fingerprint, auth source Cost analysis, latency P95 per endpoint, dependency depth, query-savings ratio — all expressible in SQL against the archive. DuckDB is the natural reader. A `recipes.md` markdown file is not queryable. ## On-disk layout — the full `~/.rote/` tree ```text ~/.rote/ ├── adapters/ Installed adapter packages │ └── / │ ├── manifest.json │ ├── tools.json │ ├── spec.json │ ├── capabilities.json, sensitivity.json │ ├── config/policies.json │ ├── runtime/sessions/ │ ├── logs/ write_guard_.jsonl, write_guard_nonces.json │ └── index/ ├── flows/ │ ├── bootstrap/ installed via powerpack │ ├── //main.ts │ └── .index/ Tantivy BM25 index ├── secrets/ │ └── tokens.json AES-256-GCM, mode 600 ├── registry/ │ └── config.json JWT tokens, environment ├── chronicle/ Event journal (SQLite) ├── config/ │ ├── preferences.toml browser, vault settings │ └── mcp.json stdio MCP server definitions ├── data/ │ └── sensitivity/ adapter classification index ├── storage/ │ ├── endpoints.db │ ├── flow_analytics.db │ └── plg_events.db ├── antipatterns/ YAML hint rules ├── lib/sdk/ts/ TypeScript SDK ├── deno_cache/ ├── bin/ cached binaries (e.g., embedded Deno) ├── shell/ shell completions and integrations ├── log/ CLI execution logs └── / ├── workspaces//.rote/ │ ├── workspace.db │ ├── responses/@N.json │ └── artifacts/ └── archives/_/ ├── metadata.parquet ├── responses.parquet ├── commands.parquet ├── dependencies.parquet ├── variables.parquet └── sessions.parquet ``` ## TypeScript SDK surface The flow author's surface (`~/.rote/lib/sdk/ts/src/`): - `Rote` — entry point. `Rote.workspace(name)` creates an isolated workspace. - `FlowOutput` — mode-aware output. Auto-detects `--output=human|summary|json` from `Deno.args`. Three methods: `.human(text)`, `.summary(text)`, `.result(data)`. - `Adapter` — `.call(tool, args)`, `.callBg(tool, args, opts)`, `.batchCall(items)`, `.probe(query)`, `.initSession()`, `.withFingerprint(fp)`. - `AdapterRegistry` — `dex.adapter(id)` returns the handle. - `AdapterChecker` — `.checkEndpoint(id, expectedFingerprint)` for preflight. - `Query` — fluent jq builder: `.select()`, `.path()`, `.at()`, `.pipe()`, `.filter()`, `.map()`, `.parseJson()`. - `TaskQueue` — `.run(fn, {label})`, `.submit(fn, {label})` (background). - `TokenManager` — `.check(endpoint)`, `.refresh(endpoint)`, `.info(endpoint)`. - `runPreflight(rote, {adapters, ...})` — single-call validation for all listed adapters (fingerprint + token + session). - `initAutoTracking(executor)` — hooks the executor to record invocations on flow exit (`addEventListener("unload")` calls `rote record-invocation `). - `getExpectedFingerprint(endpoint)` — reads the value parsed from the flow's frontmatter. - `Browser` — Playwright + Chrome DevTools handle (separate subsystem from adapters). ## Top-level CLI surface A condensed map of `rote --help` (cross-referenced with `rote grammar` and `rote guidance` topic lists): - **Workspace:** `init`, `ls`, `cd`, `clean`, `clear`, `shutdown`, `daemon`, `action-id` - **Adapter:** `adapter list / info / delete / new / new-from-mcp / new-data / sessions`, `adapter pull ` (catalog), `catalog submit` - **Flow:** `flow search / list / health / doctor / index / validate / stats / template create` - **Token / OAuth:** `token set / get / list / delete`, `oauth authorize`, `oauth client-credentials`, `oauth setup google` - **Stdio MCP:** `stdio add / list / remove / start / stop / status` - **Config & profile:** `config check`, `fingerprint`, `profile`, `model` - **Observability:** `ps`, `stats`, `plan` - **Core ops:** `POST`, `GET`, `@N` queries, `export`, `set`, `vars`, `is-error`, `exists`, `has-session` - **Powerpack & registry:** `pull powerpack`, `registry adapter push/pull/list`, `registry flow push/pull/list/search/info/delete`, `registry vault push/pull`, `registry login/logout/whoami`, `registry org`, `registry community` - **Provisioning:** `provision`, `claim` - **Onboarding & docs:** `how`, `grammar `, `guidance `, `man ` ## What this enables, that scripts and recipes don't - **Drift surfaces.** Fingerprint mismatch refuses to run; the operator sees the divergence immediately. - **Writes are bounded.** Per-tool policies + flow blessing + nonce confirmation + audit trail mean an agent's write actions are inspectable, replayable, and constrainable. - **Tokens are portable and refreshable.** No bare credentials in flow files. Vault export / push / pull / provision / claim cover the laptop, team, and CI cases. - **Traces are queryable.** Six Parquet tables let you answer "what did this cost?", "where is latency?", "which tool calls saved tokens via cached query?" without instrumentation. - **Recall is fast and ranked.** Tantivy BM25 over released flows; sub-10ms over 10K. - **Distribution is portable.** Adapters and flows are signed registry artifacts with SHA-256; powerpack presets bootstrap a working environment in seconds.