SafetyClaw

White paper

Agent-safe QuickBooks workflows for SMBs

A sandbox-first control plane for LLM-assisted accounting. SafetyClaw lets agents and models produce useful bookkeeping proposals while preserving human authority over approval, publish, audit, and rollback.

Abstract

Separate cognition from authority.

Large language models and autonomous agents are increasingly capable of clerical, analytic, and semi-structured accounting tasks. For small and medium-sized businesses, these capabilities are attractive because bookkeeping work is repetitive, detail-heavy, interrupt-driven, and commonly backlogged.

The same capabilities become dangerous when a probabilistic system can mutate the accounting ledger directly. A fluent but incorrect update can affect financial statements, tax posture, cash management, reconciliation, and professional trust. SafetyClaw addresses this by allowing agents to inspect bounded context and propose changes while routing every state-changing action through a deterministic, human-controlled safety kernel.

The core design claim is that accounting agents should be powerful proposal generators and weak actuators. They may prepare work, explain work, and route work for review; they should not possess unilateral authority to mutate QuickBooks.

The current product exposes this boundary as a four-step Review & Publish flow: QuickBooks Sync, Review, Approval, and Final Publish. The names help non-technical SMB users separate read-only context, proposed work, staged approval, and the final write decision.

Key terms

A short vocabulary before the machinery.

A handful of terms recur throughout. Most are standard; these are the project- and QuickBooks-specific ones worth pinning down first.

Realm Intuit's term for one QuickBooks Online company. A user connects one realm at a time, and every proposal is bound to the realm it was created against.
Snapshot (mirror) SafetyClaw's read-only local copy of selected QuickBooks data, used for analysis, chat context, and before/after comparison. Never authority to write.
Proposal A structured candidate change — a sandbox_changes row drafted by an agent, model, upload, or rule. It lives in SafetyClaw until a human publishes it.
write_plan_hash A canonical-JSON SHA-256 of a proposal's write plan, stamped at creation. It binds what the human reviewed to what gets published; publish recomputes it and refuses to write if it changed.
Review & Publish The human UI flow: QuickBooks Sync → Review → Approval → Final Publish. Only Final Publish can write to QuickBooks.
SyncToken QuickBooks' per-record optimistic-concurrency token. SafetyClaw re-reads it immediately before writing; a changed token means the record drifted, so the write fails closed.
Capability gate The policy layer that classifies actions. Approve, publish, and rollback are human_only and unreachable to non-human callers by construction.
MCP Model Context Protocol — the tool-calling protocol external agents use to reach SafetyClaw. Over MCP a caller can create proposals, never publish them.
OpenClaw An external agent environment that connects over MCP as one proposal source, treated as untrusted like any model or upload.

Problem setting

SMB accounting is high-leverage and high-consequence.

A useful accounting assistant can reduce latency between business activity and books, convert messy inputs into structured review work, identify category drift, answer operational questions, and keep owners and bookkeepers from losing context. The practical inputs are heterogeneous: QuickBooks snapshots, receipts, bank and card CSVs, PDF statements, spreadsheet exports, email attachments, manual owner instructions, bookkeeper cleanup notes, and agent-generated reasoning.

The unsafe implementation is to give an agent a QuickBooks token and rely on prompts, model quality, or provider safeguards. SafetyClaw takes the opposite position: model quality is useful, but authority must live in a smaller, inspectable control plane.

Requirements

What an agent-safe accounting layer must preserve

Source agnosticism OpenClaw, ChatGPT, Claude, Gemini, uploads, and future importers should enter one safety path.
Least authority External systems can propose work, not approve, publish, enable live writes, or recover credentials.
Human promotion Automation output stays draft work until a person promotes it through review and final publish.
Inspectability Reviewers see source, before and after state, risk, confidence, reasoning, validation checks, and rollback basis.
Retry safety Source keys collapse duplicate active proposals created by retries, double-pastes, or network repeats.
Drift resistance Supported writes re-read QuickBooks and refuse to overwrite unexpected live-state changes.

Safety kernel

One gate for every source

SafetyClaw's safety kernel is QuickBooks OAuth, encrypted token storage, read-only snapshot sync, normalized proposal creation, deterministic policy validation, human review, final publish confirmation, audit logging, and rollback for supported committed changes.

Everything outside that kernel is treated as an input source. OpenClaw may use streamable HTTP MCP. A direct model may power the Agent page. A document may be parsed locally. The kernel receives all of them as proposed work.

QuickBooks Sync
  -> read-only local snapshot
  -> bounded context for analyzers and Agent sources
Untrusted or semi-trusted sources
  -> source-specific adapters
  -> create_sandbox_change (stamps write_plan_hash)
  -> deterministic policy validation (capability gate)
  -> Review
  -> Approval (binds approved_with_hash)
  -> Final Publish confirmation
  -> stale-proposal guard (hash + SyncToken + realm)
  -> guarded QuickBooks write adapter
  -> audit log and rollback metadata

System model

Five concepts that should not be conflated

Ledger QuickBooks Online is the system of record and is treated as production even when the current realm is a sandbox.
Snapshot SafetyClaw's read-only local representation supports analysis and chat context, but is not authority to write.
Source Any system that can produce candidate work: OpenClaw, models, uploads, analyzers, or local rules.
Proposal A structured candidate change with source, action type, risk, confidence, before, after, inverse, and validation checks.
Commit A guarded attempt to mutate QuickBooks, available only through the authenticated web UI after approval and confirmation.

The browser maps these concepts onto the same visible workflow used in day-to-day operation: Step 1 QuickBooks Sync, Step 2 Review, Step 3 Approval, and Step 4 Final Publish.

Account boundary

The human side is a real, verified account.

The safety model puts approval and final publish behind "the authenticated web UI." That boundary is only as strong as the account behind it, so SafetyClaw's own login is built to production grade on the same conservative, dependency-free stack as the rest of the control plane — Python standard library only, no third-party authentication or cryptography packages.

Verified identity New accounts confirm their email before they can connect QuickBooks, create proposals, or publish. Until a person is verified the surface stays read-and-propose only.
Strong credentials Passwords are hashed with PBKDF2-SHA256, held to a twelve-character floor, and checked against known public breaches with a k-anonymity range query, so no password ever leaves the host.
Federated sign-in "Continue with Google" uses OAuth2 with PKCE and reads identity from Google's UserInfo endpoint over TLS, so the design carries no token-signature-verification dependency.
Abuse resistance Per-IP and per-account backoff throttles brute force on login and signup. Password reset has its own separate budget so reset spam cannot lock a victim out of their own login.
Single-use links Email verification and password reset use single-use, hash-only tokens. Completing a reset rotates the password and revokes every existing session.
Still least authority None of this hands any external source new power. Verification and sign-in apply to the human; agents and bearer-token callers stay propose-only no matter who is signed in.

Technical overview

How SafetyClaw pulls, mirrors, proposes, and publishes.

SafetyClaw is intentionally implemented as a small control plane: Python 3, SQLite, static HTML/CSS/JavaScript, and a narrow helper module for bounded file parsing and proposal drafting. The point of that conservative stack is inspectability. The QuickBooks write boundary lives in ordinary backend functions, not in an opaque agent workflow.

HTTP surface app/server.py owns the HTTP routes, OAuth, MCP transport, and the lazy server accessor. The dispatch and validator code was extracted in May 2026.
Safety kernel app/safetyclaw/ is the modular package: 15 subpackages covering dispatch, capabilities, sandbox lifecycle, validators, QBO adapters, parsers, skills, and audit.
Storage SQLite stores users, sessions, QuickBooks connections, mirrored entities, sync runs, proposals (with write_plan_hash, approved_with_hash, realm_id), confirmations, tokens, and audit events.
Assistant helpers app/assistant.py normalizes uploads, extracts bounded previews, drafts candidates, and cannot publish to QuickBooks.

A sustained decomposition moved the safety logic out of server.py into the subpackages above, so what remains in server.py is the HTTP/routing surface, OAuth plumbing, and re-export shims. The point of that work is reviewability: each invariant lives in a single named module — for example the capability registry is safety/capabilities.py and the canonical write-plan hash is sandbox/canonical.py — so a reviewer can find and audit each guarantee in one place.

An architecture fitness gate (qa/architecture_fitness_safetyclaw.py) scans every .py file under app/ and refuses commits or deploys that introduce intent regex, forbidden helper symbols, or top-level re.compile constants outside a small allowlist. The gate runs at pre-commit, on push, and at deploy preflight.

QuickBooks mirror

QuickBooks data is copied into a read-only local snapshot.

After Intuit OAuth, SafetyClaw stores the connected realm in a per-user QuickBooks connection row and stores the OAuth token payload through the configured token vault. When sync runs, SafetyClaw queries selected QuickBooks entities with pagination and writes each entity into user_qbo_entities, keyed by user, entity type, and QuickBooks id.

The current mirror includes company info, accounts, customers, vendors, items, classes, departments, tax codes, invoices, bills, payments, purchases, deposits, and journal entries. Each mirrored row stores display name, QuickBooks sync token, sparse flag, raw JSON, and update time. Sync runs are recorded with status, entity counts, errors, and timestamps.

QuickBooks OAuth callback
  -> store realm per SafetyClaw user
  -> seal token payload in the token vault
  -> query selected QuickBooks entity types
  -> insert or replace user_qbo_entities rows
  -> record user_qbo_sync_runs
  -> expose summaries to analyzers and Agent context

This mirror is useful for analysis, search, chat context, before-state capture, and drift comparison. It is not authority to write. Supported publish and rollback adapters re-read live QuickBooks before mutating anything.

Proposal lifecycle

Manipulation happens in SafetyClaw before it can happen in QuickBooks.

When OpenClaw, a model, an upload parser, a local analyzer, or a user action "changes" something, SafetyClaw creates a sandbox_changes row. The proposal records owner, batch, canonical action type, source, proposer, risk, confidence, amount, before JSON, after JSON, inverse JSON, validation checks, source key, and status — plus the canonical-JSON SHA-256 write_plan_hash stamped at creation time.

Source keys make retries safe: duplicate active submissions from the same user and source key return the existing active proposal instead of creating queue noise. Review state then moves through proposed, approved, blocked, rejected, undone, publishing, or committed. Approval rechecks current policy, stamps approved_with_hash and approved_at to bind the human's review to the exact write plan, and refuses stale browser updates.

The UI intentionally summarizes those states as Review, Approval, and Final Publish so "approved" is not mistaken for "already in QuickBooks."

Source output
  -> create_sandbox_change (stamps write_plan_hash)
  -> canonicalize action type
  -> validate against current user policy + capability gate
  -> store before / after / inverse JSON
  -> attach source and proposer labels
  -> apply sourceKey idempotency
  -> show in Review & Publish
  -> human approval binds approved_with_hash + approved_at

Bad-edit prevention

SafetyClaw uses multiple independent gates, not one warning.

Before publish

  • External sources authenticate with scoped bearer tokens or model connections.
  • Capability gate refuses human_only capabilities (approve, publish, rollback) to LLM callers, agent tokens, and MCP clients by construction.
  • Agents and uploads can create proposals, not publish tools.
  • Every proposal stamps a canonical-JSON write_plan_hash at creation; the human's approval binds that hash as approved_with_hash.
  • Action types are canonicalized before policy checks.
  • Blocked actions, high-risk changes, amount thresholds, and confidence thresholds are enforced per user.
  • Approval is separate from publish and rechecks current policy.
  • Final publish requires a hashed one-time confirmation token that expires and is consumed.

At the write boundary

  • Non-GET QuickBooks requests require a guarded write context.
  • The write context must match user id, operation, sandbox change id, and gate.
  • Approved rows are claimed as publishing before live writes.
  • Stale-proposal guard recomputes write_plan_hash, checks realm_id binding, and re-reads live SyncToken before any write — any drift returns the row to approved.
  • QuickBooks is re-read and compared to expected before-state.
  • Unexpected live drift blocks publish or rollback.
  • Audit events record proposal, approval, hash mismatch, freshness drift, capability decision, publish preparation, commit, failure, and rollback.

Live adapters

Nine live publish paths, each with rollback.

The live publish surface today covers nine action types — each paired with a rollback handler — defined in PUBLISH_LIVE_ACTION_TYPES in app/safetyclaw/sandbox/publishers.py:

Before any live write, the adapter runs the Phase 6 stale-proposal guard: recompute write_plan_hash against approved_with_hash, verify the proposal's realm_id matches the user's currently connected QBO realm, and re-read the live SyncToken. Any drift fails closed and the proposal returns to approved for re-review.

Update-style adapters (purchase-category) load before/after state, re-read the live QuickBooks record, locate the relevant line, compare the live value with the expected before value, construct a sparse update with the live SyncToken, and write only through the guarded QuickBooks update function. Create-style adapters (the agent_*_draft family) construct the full create payload and write through the same guarded layer with a prepared_commit context.

Rollback follows the same pattern in reverse. SafetyClaw re-reads the live record, confirms it still matches the committed after-state, then issues the inverse — restoring the prior account for purchase-category, voiding or deleting for create-style adapters per QBO's accepted inverse semantics — through a committed_rollback write context. If another human or app changed the record after SafetyClaw committed it, rollback stops instead of overwriting later legitimate work.

Adapters are added one workflow at a time. Action types without a publish adapter still create sandbox proposals, but cannot be pushed live yet.

Streaming event UX

Progressive feedback during agent turns.

The agent chat uses a streaming, event-driven UX layer. The dispatch graph emits a normalized event vocabulary (commentary, tool_started / tool_completed, validation_issue, proposal_created, final_answer, and others) that the chat UI renders progressively as work happens, instead of leaving the user staring at a frozen spinner until the response arrives all at once.

When the client sends Accept: text/event-stream, /api/agent/message upgrades to Server-Sent Events and the chat UI shows commentary, tool cards, and proposal chips as they fire. When the client doesn't (older browsers, MCP clients, test scripts), the same handler returns the canonical JSON response with the post-hoc event list under streamingEvents so non-streaming consumers can still replay the progression after the fact.

The safety invariants — write-plan-hash binding, capability gate, sandbox proposals, freshness / stale guard, dependency model — are unchanged. The event system is a pure UX layer on top of the same dispatch graph and audit chain. Reasoning effort is tuned per dispatch type (low for parsers and state queries, medium for workflow dispatch and general-answer turns) so routine bookkeeping doesn't pay for unnecessary model deliberation.

Threat model

The design assumes useful inputs may be unreliable.

SafetyClaw is not built around a fantasy of perfect models. Agents may hallucinate vendors, infer the wrong account, misunderstand a receipt, overgeneralize from sparse data, or produce internally inconsistent JSON. Uploaded files may contain prompt-injection instructions. Network calls may duplicate. Users may approve from stale pages. QuickBooks may drift after a proposal is created.

In scope

  • Agent hallucination and overconfidence.
  • Prompt and document injection.
  • Bearer-token revocation and leakage boundaries.
  • Tenant isolation across users.
  • Duplicate and concurrent agent submissions.
  • External QuickBooks drift before publish or rollback.

Out of scope

  • Guaranteeing every approved proposal is accounting-correct.
  • Replacing a CPA, tax advisor, or bookkeeper.
  • Protecting against a malicious server administrator.
  • Making provider OAuth available where providers do not officially support it.
  • Solving semantic tax judgment with deterministic software.

Safety invariants

The contract SafetyClaw should preserve as it grows

  1. No agent, model, upload parser, CSV importer, or assistant route may call the QuickBooks write adapter directly.
  2. All work must exist as a source-labeled proposal before it can be approved.
  3. Approval is not publish. Approval stages work; it does not write to QuickBooks.
  4. A live write requires QuickBooks connection, live-write policy, approved work, final confirmation, a supported adapter, and authenticated web UI action.
  5. Current policy is rechecked before approval and again before publish.
  6. Publishing claims the item so review and undo cannot race the live write.
  7. Supported adapters block publish or rollback when live QuickBooks state has drifted unexpectedly.
  8. Full bearer tokens and provider secrets are not normal application state.
  9. The approved write plan is hash-bound. Every proposal stamps a canonical-JSON SHA-256 of its write plan at creation; approval binds the hash the human reviewed; publish recomputes and refuses to write if it has changed.
  10. Capabilities are structurally bounded. Approve, publish, and rollback capabilities are human_only and unreachable to LLM callers, agent tokens, or MCP clients by construction — not by audit, by code.
  11. Stale approvals do not survive state change. A human's prior approval is bound to the realm-id, the QBO SyncToken, and the write_plan_hash at approval time; if any drifts before publish, the publisher fails closed and the row returns to approved for re-review.

Source transparency

Every proposal should say where it came from.

SafetyClaw records source labels such as QuickBooks snapshot analyzer, Agent connection, ChatGPT OAuth connection, OpenAI API connection, Claude, Gemini, uploaded file context, and local SafetyClaw review rules. Source provenance changes how humans review the work. A business owner may trust a bookkeeper-authored cleanup differently from a model-authored cleanup. An accountant may bulk approve deterministic CSV-derived candidates while inspecting generative recommendations one by one.

Uploads

Files become evidence before they become work.

SMB accounting inputs are not clean API objects. They are receipts, statement PDFs, phone screenshots, bank CSVs, XLSX exports, pasted instructions, and occasionally malformed files. SafetyClaw treats uploads as evidence, not imports.

  1. Attach one or more files.
  2. Extract bounded previews or structured rows where possible.
  3. Present those materials to the selected Agent source.
  4. Show draft candidates in the chat.
  5. Let the user choose which candidates become proposals.
  6. Send only selected candidates into Review & Publish.

Connectivity

OpenClaw and direct models serve different roles.

OpenClaw is best treated as an external agent environment. The broadest setup path is to register SafetyClaw as an MCP server under OpenClaw's MCP server configuration. Native OpenClaw plugin configuration can exist, but it should remain advanced or fallback because it requires plugin availability and schema compatibility on the OpenClaw box.

Direct model providers can power the in-app Agent interface. Whether the source is ChatGPT OAuth-style auth, an OpenAI API, Claude, Gemini, or a future provider, the output still enters the same proposal path. Provider convenience must not create provider-specific write privileges.

The Agent should present the safety boundary as a normal workflow, not as a scolding refusal. A helpful answer can start by drafting or explaining the work, then remind the user that Review & Publish is the final human step before QuickBooks is touched.

Publishing and rollback

Approval is not the same as writing to QuickBooks.

Publishing is deliberately the narrowest part of the system. Approval means a human accepted a proposal as staged work. Publishing means SafetyClaw may attempt a real QuickBooks write for that approved item now.

The publish process uses a one-time confirmation that expires and is consumed. It checks the current approved set, current policy, live-write configuration, and supported adapter. During publish, the change is claimed as publishing so it cannot be simultaneously undone from the review queue.

Three structural binders join approval to publish:

  1. Hash binding. Approval stamps the canonical-JSON write_plan_hash as approved_with_hash. Publish recomputes and refuses to write if the value has changed. Mutation between approval and publish fails closed.
  2. Realm binding. The proposal carries the QBO realm_id it was created against. If the user reconnects to a different company before publish, the proposal is stale; publish fails closed.
  3. SyncToken binding. Each adapter re-reads the live QBO record immediately before the write and compares its SyncToken against the value recorded at proposal time. Drift fails closed.

All three checks fail with HTTP 409 and emit a structured audit event. The proposal returns to approved (not committed) so the human can re-review. Prior approval does not survive a state change underneath it.

Capability enforcement runs in parallel. The capability registry classifies actions by the human_only flag; LLM callers, agent tokens, and MCP clients cannot acquire approve, publish, or rollback capabilities by construction. This is enforced at the policy gate, not the audit layer.

The current user-facing flow names this path explicitly: QuickBooks Sync makes the read-only snapshot current, Review holds proposed work, Approval stages the batch, and Final Publish is the only step that can attempt a supported QuickBooks write.

Rollback is also guarded. SafetyClaw stores inverse state for supported actions and refuses rollback if QuickBooks no longer matches the committed state expected by the rollback adapter.

Security model

Untrusted generation is separated from trusted actuation.

Evaluation

The current evidence base is engineering-grade, not formal verification.

SafetyClaw is evaluated through deterministic local tests, browser tests, stress tests, security regressions, and live QuickBooks sandbox tests. The suite covers signup and login, per-user boundaries, encrypted token paths, OpenClaw proposal intake, MCP tools, Agent source switching, OpenClaw-only operation without QuickBooks, upload bounds, draft candidate confirmation, source labels, malformed request rejection, duplicate source-key races, bulk review, no-QuickBooks publish blocking, final publish confirmation, policy rechecks, publish/undo race protection, visual coverage, and live QuickBooks sandbox publish and rollback across the nine supported action types (purchase-category, customer / vendor / item creates, invoice / bill / sales-receipt creates, customer payment apply, expense / purchase create from already-paid receipts). It now also covers native structured intent planning for ChatGPT subscription/OAuth, OpenAI API, Claude, and Gemini, plus long messy invoice/payment workflows with duplicate invoice-number pushback, clean-number follow-up, bad-math holdback, paid-on-delivery payment receipts, and repeated Customer: invoice blocks.

One recent stress run submitted 260 realistic OpenClaw proposals with 24 workers and verified that duplicate active sourceKey submissions collapsed to a single active change. Live sandbox testing has verified actual QuickBooks publish and rollback while restoring live writes to disabled afterward.

Recent real-user Agent testing also covered Gemini and ChatGPT source behavior, including warmer safety-boundary language that preserved Review & Publish as the final gate without turning ordinary bookkeeping help into a refusal. On May 1, 2026, the active-account model bakeoff passed 8/8 for Claude, Gemini, OpenAI API, and ChatGPT OAuth against duplicate invoice numbers, clean-number follow-up, repeated Customer: invoice blocks, delayed creation, corrected bad math, and misspelled publish requests. All generated proposals were rejected after verification.

A broader live-Chrome trust measurement drives realistic prompts through the actual Review & Publish UI and scores the result. Recent canonical runs land in the low-to-mid 9s out of 10, with no P0/P1 clusters, no forbidden behavior, and no fabricated Review & Publish IDs — every cited proposal ID resolves to a real row, and cleanup returns the queue to zero pending and zero approved. The remaining partials are data-precondition rows that assumed fixture data absent from the synced sandbox, not safety failures.

The deterministic sanity layer covers more than 600 cases across the dispatch graph, the validators, the catalog, the resolver, the parsers, and the hold-reason taxonomy. These run locally in seconds and are the first gate every change must pass before deployment.

The Phase 6 safety upgrade (May 2026) added explicit coverage for the write-plan hash binding, the capability gate, and the stale-proposal guard:

Limitations

The guarantee is architectural, not omniscient.

SafetyClaw can prevent an agent from bypassing the proposal and publish gate through SafetyClaw. It cannot guarantee that every approved proposal is accounting-correct.

Roadmap

More usefulness without weakening the gate