Show HN:Magpie,一款将 AI Agent 变成记账员的 CLI
英文标题:🔶 Show HN: Magpie, a CLI to Turn Your AI Agent into a Bookkeeper
Magpie 是一款面向人类和 AI Agent 的强约束会计 CLI,通过 RBAC 与会计不变量校验,将加密、不可变的事件写入 Jaybase。它支持复式记账、科目表、客户、发票、付款、笔记、快照和审计读取,并默认以 JSON 输出,便于 Agent 集成。项目要求 Go 1.26.5 或更高版本,当前仍处于 pre-1.0 阶段,明确不覆盖 QuickBooks 原生导入、银行对账、税务、期末结账等功能。对开发者而言,Magpie 提供了可审计、可重试、具备幂等与乐观并发控制的账务写入路径,旨在避免 Agent 直接操作数据库或自行推断会计规则。
798.3 KB
90.4 KB
中文
Magpie 是一款面向人类和 AI Agent、带有明确设计取向的会计 CLI。它为科目表管理、复式记账凭证、客户、发票、付款、笔记、快照和审计读取提供了一条受保护的操作路径。CLI 会在向 Jaybase 追加加密、不可变事件之前,检查 RBAC 和会计不变量。
需要 Go 1.26.5 或更高版本。更早的 Go 版本包含已知的标准库漏洞,绝不能用于构建发布二进制文件:
git clone https://github.com/kyle-visner/magpie.git
cd magpie
go install ./cmd/magpie
magpie --store .magpie init
初始化后的本地账簿使用 cash(收付实现制)会计基础,并创建一个具有 Owner 角色的 owner 执行者。在添加其他执行者或录入财务活动前,请先阅读首次设置说明。
Magpie 面向希望让 Agent 协助记账、但不希望授予其原始数据库访问权限或允许其自行编造会计行为的小型团队。它适用于源文件导入、受控迁移、发票和付款工作流、运营笔记及其他需要写入可归属、可审计、可安全重试,并在违反策略时被拒绝的任务。
Magpie 是 CLI 和领域引擎,而不是托管式会计 SaaS,也不能替代专业会计判断。开发环境可使用本地后端,或在受信任的单用户进程中使用。对于共享或生产环境,应让 Magpie 连接独立部署的 Jaybase 服务,并将已认证的调用方绑定至被允许的 Magpie actor ID。
Magpie 目前处于 pre-1.0 阶段。已实现的会计功能可用且经过测试。本次发布包含下列能力;以下功能不在当前范围内:
- 原生 QuickBooks CSV、IIF 或 QBXML 解析;Agent 必须将源数据规范化为 Magpie 的 JSON 合约;
- 账单、银行匹配或对账、期间结账、税务、贷款、转账、固定资产、保留、垃圾回收或时间点恢复命令;
- 笔记搜索、反向链接、强类型跨实体引用、差异比较或图谱导航;
- 面向人类的输出模式、交互式 UI、签名命令封套,或用于证明
--actor身份的认证层。
这些是产品范围边界,而不是隐藏的安装步骤。有关仍由部署者负责的额外生产控制措施,请参阅 docs/SECURITY.md。
- 仓库根目录是会计 CLI 项目。
cmd/magpie包含 CLI,internal/magpie包含会计领域逻辑。- Jaybase 在
github.com/kyle-visner/jaybase单独维护。Magpie 将其锁定为 Go 模块依赖。
AGPL-3.0-or-later。参见 LICENSE。
- 位于
cmd/magpie的标准 CLI,支持本地嵌入式和托管式 Jaybase 后端。 - 通过 Jaybase 提供仅追加、以 SHA-256 寻址的事件历史。
- 通过 HTTPS 使用 Bearer 认证访问托管 Jaybase,支持分页回放、乐观并发、幂等写入和远程命名引用。
- 对存储的节点载荷使用 AES-256-GCM 加密。
- 统一的 RBAC,覆盖分类账、笔记、快照和审计读取。
- 持久化前进行复式账本验证。
- 账簿级会计基础支持:收付实现制、修正收付实现制和权责发生制。
- 用于工作流安全科目选择的科目角色。
- 要求提供审计原因的特权手工凭证调整。
- 一等公民客户、发票和付款工作流,可生成适配会计基础的凭证。
- 分类账科目上的结构化外部来源引用。
- Markdown 笔记的创建、更新、列出和获取操作。
- 带来源标签的凭证条目,用于 Agent 从 QuickBooks 或其他系统映射导出的数据。
- 用于可恢复根节点的命名快照。
- 默认 JSON 命令输出,供 Agent 消费。
- 针对业务不变量、CLI 行为和 BDD 风格核心场景的自动化测试。
使用 Go 1.26.5 或更高版本。在仓库根目录中执行:
go mod verify
go test -race ./...
go vet ./...
go build -o ./magpie ./cmd/magpie
go install golang.org/x/vuln/cmd/govulncheck@v1.6.0
govulncheck ./...
如果环境阻止使用默认 Go 构建缓存,请使用可写的缓存:
GOCACHE=/private/tmp/magpie-gocache go mod verify
GOCACHE=/private/tmp/magpie-gocache go test -race ./...
GOCACHE=/private/tmp/magpie-gocache go vet ./...
GOCACHE=/private/tmp/magpie-gocache go build -o ./magpie ./cmd/magpie
生成的 ./magpie 二进制文件会被 Git 忽略。
带标签的发布版本由 release.yml 从干净的 main 提交构建,并使用可复现的 scripts/build-release.sh 流程。每个 GitHub Release 都包含适用于 amd64 和 arm64 的 macOS、Linux 归档文件,以及一个 SHA256SUMS 文件。
从此仓库运行会计基准测试。Jaybase 基准测试应从单独的 Jaybase 仓库运行。
GOCACHE=/private/tmp/magpie-gocache go test -run '^$' -bench . -benchmem ./...
若要进行基线比较,请采集多次运行结果,并用 benchstat 对比:
mkdir -p .benchmarks
GOCACHE=/private/tmp/magpie-gocache go test -run '^$' -bench . -benchmem -count 5 ./... > .benchmarks/magpie-main.txt
benchstat .benchmarks/magpie-main.txt .benchmarks/magpie-feature.txt
Agent 集成模式
Agent 应将 llm.md 作为其操作契约。以下简短模式展示了所需的调用形式。
为 Agent 提供固定的命令模板,并要求其将 stdout 解析为 JSON:
./magpie \
--store .magpie \
--actor AGENT_USER_ID \
COMMAND...
对于托管 Jaybase 服务,请通过环境变量提供服务源地址和 Bearer 凭据。不要将令牌放入命令行标志、URL、载荷、日志或幂等键中:
export JAYBASE_URL=https://jaybase.example.com
export JAYBASE_TOKEN='writer-token-from-the-secret-manager'
./magpie \
--actor AGENT_USER_ID \
COMMAND...
--jaybase-url 可以覆盖服务源地址,但令牌只能通过 JAYBASE_TOKEN 提供。托管请求仅在回放状态时获取已解密的载荷;审计输出始终仅包含元数据。写入使用 Jaybase 的 expected_root 和 Idempotency-Key 合约;如果发生冲突,会返回冲突结果,而不会覆盖较新的根节点。
Magpie 可与 Martin 共用一条线性的 Jaybase 历史记录。回放会应用旧版 Magpie 节点类型,跳过 martin.* 节点但仍前进到其根节点;对于其他未知节点类型或格式错误的 Magpie 事件,则采取故障关闭策略。magpie init 会在仅含外部节点的历史后添加 Magpie 引导节点,并在该引导节点存在后保持幂等。
若在不预先构建的情况下进行开发,请使用:
go run ./cmd/magpie \
--store .magpie \
--actor AGENT_USER_ID \
COMMAND...
Agent 的操作规则:
- 将 stdout 视为唯一的成功通道。
- 将 stderr 视为 JSON 错误输出。
- 切勿直接编辑
.magpie/文件。 - 切勿自行编造原始存储变更。
- 在录入财务活动前,先读取
book settings get。 - 对整本账簿使用当前的
accounting_basis;不要为每笔交易选择收付实现制、修正收付实现制或权责发生制。 - 判断科目含义时应使用科目角色,而不是科目名称或编号。
- 客户发票活动应使用
invoice create-json、invoice post和invoice mark-paid。 - 不要将通用的
ledger journal create用于日常运营活动。它是特权手工调整或导入路径。 - 对于较长的笔记正文,使用
note put --body-file FILE,以避免 Shell 引号问题。 - 在执行大型 Agent 工作流前,创建
snapshot create --name NAME。
错误示例:
{"code":"permission_denied","message":"role \"Operations\" lacks ledger:read"}
初始化本地存储:
./magpie --store .magpie init
或者,在设置 JAYBASE_URL 和写入权限的 JAYBASE_TOKEN 后,初始化一个空的托管存储:
./magpie --actor owner init
默认初始化的执行者为具有 Owner 角色的 owner。
列出支持的权限:
./magpie --store .magpie --actor owner rbac permissions
创建一个具有受限角色的 Agent 用户:
./magpie --store .magpie --actor owner rbac user set \
--id ops-agent \
--role Operations
内置角色:
Owner:完整的 Phase 1 访问权限。Admin:除恢复操作外的广泛运营权限。Accountant:分类账、笔记读取和审计读取权限。Operations:仅笔记读写权限。Sales Rep:仅笔记读写权限。
在新增内置权限之前初始化的存储,可以在不更改自定义角色或用户的情况下修复默认角色:
./magpie --store .magpie --actor owner rbac defaults repair
修复命令需要 rbac:manage 权限,它会将当前默认权限中缺失的权限添加到内置角色中,并保留这些角色已有的任何额外权限。
定义自定义角色:
./magpie --store .magpie --actor owner rbac role set \
--name "Notes Agent" \
--permissions notes:read,notes:write
然后分配该角色:
./magpie --store .magpie --actor owner rbac user set \
--id notes-agent \
--role "Notes Agent"
创建或更新笔记:
./magpie --store .magpie --actor notes-agent note put \
--title "Ops Handoff" \
--body "Ship daily closeout."
对于较长内容:
./magpie --store .magpie --actor notes-agent note put \
--title "Weekly Review" \
--body-file ./weekly-review.md \
--sensitivity internal
列出笔记:
./magpie --store .magpie --actor notes-agent note list
读取特定笔记:
./magpie --store .magpie --actor notes-agent note get --id note:...
Magpie 对会计方法有明确约束。每本账簿恰好只有一种活跃的会计基础:
cash:在收到现金时确认收入,在支付现金时确认费用。modified_cash:普通收入和费用采用现金处理方式;销售税负债、工资税负债、贷款本金及资本化固定资产采用明确的资产负债表处理方式。accrual:在赚取或开具发票时确认收入,在发生或收到账单时确认费用,并在适用时使用应收账款和应付账款。
新存储默认使用 cash。在 Agent 录入条目前检查当前设置:
./magpie --store .magpie --actor owner book settings get
在录入凭证活动前设置会计基础:
./magpie --store .magpie --actor owner book settings set \
--accounting-basis accrual
更改会计基础需要 settings:manage 权限,默认的 Owner 和 Admin 角色具备该权限。凭证条目存在后,Magpie 会拒绝更改会计基础,因为在已入账后改变会计方法需要受控的迁移或重述工作流。
每条新凭证都会标记当前的 accounting_basis。如果 Agent 提交的凭证条目带有显式 accounting_basis,但与账簿当前设置不一致,写入将被拒绝。
修正收付实现制策略的范围经过刻意收窄:
- 在收到现金时确认收入。
- 在支付现金时确认普通费用。
- 将销售税和工资税作为负债跟踪。
- 将贷款本金作为负债跟踪,并与利息费用分开。
- 固定资产资本化。
- 默认不使用存货、应收账款和应付账款。
对于正常的服务发票,Agent 应根据当前会计基础进行入账:
- 收付实现制或修正收付实现制,在付款时:借记现金,贷记收入;如已代收销售税,则贷记应付销售税。
- 权责发生制,在开具时:借记应收账款,贷记收入,贷记应付销售税。
- 权责发生制,在付款时:借记现金,贷记应收账款。
对于供应商账单:
- 收付实现制或修正收付实现制:在付款时确认费用;固定资产、贷款和税款等明确的修正收付实现制资产负债表项目除外。
- 权责发生制:在收到账单时,借记费用或资产并贷记应付账款;付款时,借记应付账款并贷记现金。
Magpie 会阻止普通 Agent 通过通用手工凭证绕过这些规则,而发票工作流会直接强制执行应收账款与收付实现制的入账语义。
以具有 ledger:write 权限的执行者创建科目。在创建时分配角色还需要 chart:manage 权限:
./magpie --store .magpie --actor owner ledger account create \
--number 1000 \
--name Checking \
--type asset \
--role bank_account
./magpie --store .magpie --actor owner ledger account create \
--number 4000 \
--name "Consulting Revenue" \
--type revenue \
--role default_service_revenue
科目编号可选,但属于一等公民数据。它们与科目 ID 分开存储,因此即使科目重新编号,凭证条目仍会引用稳定的 acct:... ID。
对现有科目重新编号:
./magpie --store .magpie --actor owner ledger account number set \
--account-id acct:CHECKING_ID \
--number 1010
科目编号规则:
- 编号存在时,必须在各科目之间保持唯一。
- 编号可包含数字、连字符和点号,例如
1000、1010.01、2000-10。 - 重新编号会创建带版本的科目更新事件。
列出科目并记录生成的科目 ID:
English / 原文
Magpie is an opinionated accounting CLI for humans and AI agents. It provides one guarded path for chart-of-accounts management, double-entry journals, customers, invoices, payouts, notes, snapshots, and audit reads. The CLI checks RBAC and accounting invariants before appending an encrypted, immutable event to Jaybase.
Requires Go 1.26.5 or later. Earlier Go releases include known standard-library vulnerabilities and must not be used to build release binaries:
git clone https://github.com/kyle-visner/magpie.git
cd magpie
go install ./cmd/magpie
magpie --store .magpie init
The initialized local book uses cash accounting and creates an owner actor with the Owner role. Read First-Time Setup before adding another actor or posting financial activity.
Magpie is for small teams that want agents to help with bookkeeping without giving them raw database access or permission to invent accounting behavior. It fits source-document imports, controlled migrations, invoice and payout workflows, operational notes, and other jobs where writes must be attributable, auditable, safe to retry, and rejected when they violate policy.
Magpie is a CLI and domain engine, not a hosted accounting SaaS or a substitute for professional accounting judgment. Use the local backend for development or a trusted single-user process. For shared or production use, run Magpie against a separately deployed Jaybase service and bind authenticated callers to allowed Magpie actor IDs.
Magpie is pre-1.0. The implemented accounting surface is usable and tested. This release includes the capabilities listed below. The following features are outside its current scope:
- native QuickBooks CSV, IIF, or QBXML parsing; agents must normalize source data into Magpie's JSON contracts;
- bills, bank matching or reconciliation, period close, tax, loan, transfer, fixed-asset, retention, garbage-collection, or point-in-time restore commands;
- note search, backlinks, typed cross-entity references, diff, or graph navigation;
- a human-oriented output mode, interactive UI, signed command envelopes, or an authentication layer that proves
--actoridentity.
Those are product-scope boundaries, not hidden installation steps. See docs/SECURITY.md for additional production controls that remain the deployer's responsibility.
- The repository root is the accounting CLI project.
cmd/magpiecontains the CLI andinternal/magpiecontains the accounting domain.- Jaybase is maintained separately at
github.com/kyle-visner/jaybase. Magpie pins it as a Go module dependency.
AGPL-3.0-or-later. See LICENSE.
- Canonical CLI in
cmd/magpiewith local embedded and hosted Jaybase backends. - Append-only, SHA-256-addressed event history through Jaybase.
- Bearer-authenticated hosted Jaybase access over HTTPS with paginated replay, optimistic concurrency, idempotent writes, and remote named refs.
- AES-256-GCM encryption for stored node payloads.
- Unified RBAC for ledger, notes, snapshots, and audit reads.
- Double-entry ledger validation before persistence.
- Book-level accounting basis support for cash, modified cash, and accrual accounting.
- Chart account roles for workflow-safe account selection.
- Privileged manual journal adjustments with required audit reasons.
- First-class customer, invoice, and payout workflows that generate basis-aware journals.
- Structured external source references on ledger accounts.
- Markdown note create, update, list, and get operations.
- Source-tagged journal entries for agent-mapped exports from QuickBooks or other systems.
- Named snapshots for recoverable roots.
- JSON command output by default for agent consumption.
- Automated tests for business invariants, CLI behavior, and BDD-style core scenarios.
Use Go 1.26.5 or later. From the repository root:
go mod verify
go test -race ./...
go vet ./...
go build -o ./magpie ./cmd/magpie
go install golang.org/x/vuln/cmd/govulncheck@v1.6.0
govulncheck ./...
If your environment blocks the default Go build cache, use a writable cache:
GOCACHE=/private/tmp/magpie-gocache go mod verify
GOCACHE=/private/tmp/magpie-gocache go test -race ./...
GOCACHE=/private/tmp/magpie-gocache go vet ./...
GOCACHE=/private/tmp/magpie-gocache go build -o ./magpie ./cmd/magpie
The generated ./magpie binary is ignored by Git.
Tagged releases are built from clean main commits by release.yml using the reproducible scripts/build-release.sh process. Each GitHub Release includes macOS and Linux archives for amd64 and arm64 plus a SHA256SUMS file.
Run the accounting benchmarks from this repository. Run Jaybase benchmarks from the separate Jaybase repository.
GOCACHE=/private/tmp/magpie-gocache go test -run '^$' -bench . -benchmem ./...
For baseline comparisons, capture multiple runs and compare them with benchstat:
mkdir -p .benchmarks
GOCACHE=/private/tmp/magpie-gocache go test -run '^$' -bench . -benchmem -count 5 ./... > .benchmarks/magpie-main.txt
benchstat .benchmarks/magpie-main.txt .benchmarks/magpie-feature.txt
Agent Integration Pattern
Agents should read llm.md as their operating contract. The short pattern below shows the required invocation shape.
Give your agent a fixed command template and tell it to parse stdout as JSON:
./magpie \
--store .magpie \
--actor AGENT_USER_ID \
COMMAND...
For the hosted Jaybase service, provide the origin and bearer credential through the environment. Do not put the token in a command-line flag, URL, payload, log, or idempotency key:
export JAYBASE_URL=https://jaybase.example.com
export JAYBASE_TOKEN='writer-token-from-the-secret-manager'
./magpie \
--actor AGENT_USER_ID \
COMMAND...
--jaybase-url may override the origin, but the token is accepted only through JAYBASE_TOKEN. Hosted requests fetch decrypted payloads only when replaying state; audit output remains metadata-only. Writes use Jaybase's expected_root and Idempotency-Key contract and return a conflict instead of overwriting a newer root.
Magpie can share one linear Jaybase history with Martin. Replay applies the legacy Magpie node types, skips martin.* nodes while still advancing to their roots, and fails closed for other unknown node types or malformed Magpie events. magpie init adds the Magpie bootstrap after a foreign-only history and remains idempotent once that bootstrap exists.
For development without building first, use:
go run ./cmd/magpie \
--store .magpie \
--actor AGENT_USER_ID \
COMMAND...
Operational rules for agents:
- Treat stdout as the only success channel.
- Treat stderr as JSON error output.
- Never edit
.magpie/files directly. - Never invent raw storage mutations.
- Read
book settings getbefore posting financial activity. - Use the active
accounting_basisfor the whole book; do not choose cash, modified cash, or accrual per transaction. - Use account roles rather than account names or numbers when deciding what an account means.
- Use
invoice create-json,invoice post, andinvoice mark-paidfor customer invoice activity. - Do not use generic
ledger journal createfor ordinary operating activity. It is a privileged manual adjustment/import path. - Use
note put --body-file FILEfor long note bodies to avoid shell quoting issues. - Create a
snapshot create --name NAMEbefore large agent workflows.
Errors look like:
{"code":"permission_denied","message":"role \"Operations\" lacks ledger:read"}
Initialize a local store:
./magpie --store .magpie init
Or initialize an empty hosted store after setting JAYBASE_URL and a writer JAYBASE_TOKEN:
./magpie --actor owner init
The default initialized actor is owner with the Owner role.
List supported permissions:
./magpie --store .magpie --actor owner rbac permissions
Create an agent user with a constrained role:
./magpie --store .magpie --actor owner rbac user set \
--id ops-agent \
--role Operations
Built-in roles:
Owner: full Phase 1 access.Admin: broad operational access except recovery.Accountant: ledger, notes read, and audit read.Operations: notes read/write only.Sales Rep: notes read/write only.
Stores initialized before new built-in permissions were added can repair default roles without changing custom roles or users:
./magpie --store .magpie --actor owner rbac defaults repair
The repair command requires rbac:manage, adds missing current default permissions to built-in roles, and preserves any existing extra permissions on those roles.
To define a custom role:
./magpie --store .magpie --actor owner rbac role set \
--name "Notes Agent" \
--permissions notes:read,notes:write
Then assign it:
./magpie --store .magpie --actor owner rbac user set \
--id notes-agent \
--role "Notes Agent"
Create or update a note:
./magpie --store .magpie --actor notes-agent note put \
--title "Ops Handoff" \
--body "Ship daily closeout."
For longer content:
./magpie --store .magpie --actor notes-agent note put \
--title "Weekly Review" \
--body-file ./weekly-review.md \
--sensitivity internal
List notes:
./magpie --store .magpie --actor notes-agent note list
Read a specific note:
./magpie --store .magpie --actor notes-agent note get --id note:...
Magpie is opinionated about accounting methods. The book has exactly one active accounting basis:
cash: recognize income when cash is received and expenses when cash is paid.modified_cash: cash treatment for ordinary income and expenses, with explicit balance-sheet treatment for sales tax liabilities, payroll tax liabilities, loan principal, and capitalized fixed assets.accrual: recognize revenue when earned or invoiced and expenses when incurred or billed, using accounts receivable and accounts payable where appropriate.
New stores default to cash. Check the current setting before an agent posts entries:
./magpie --store .magpie --actor owner book settings get
Set the accounting basis before entering journal activity:
./magpie --store .magpie --actor owner book settings set \
--accounting-basis accrual
Changing the accounting basis requires settings:manage, which the default Owner and Admin roles have. Magpie rejects basis changes after journal entries exist, because changing accounting method after postings would require a controlled migration or restatement workflow.
Every new journal entry is stamped with the active accounting_basis. If an agent submits a journal entry with an explicit accounting_basis that does not match the active book setting, the write is rejected.
Modified cash policy is deliberately narrow:
- Revenue is recognized when cash is received.
- Ordinary expenses are recognized when cash is paid.
- Sales tax and payroll tax are tracked as liabilities.
- Loan principal is tracked as a liability, separate from interest expense.
- Fixed assets are capitalized.
- Inventory, accounts receivable, and accounts payable are not used by default.
For normal service invoices, agents should post according to the active basis:
- Cash or modified cash, when paid: debit cash, credit revenue, credit sales tax payable when collected.
- Accrual, when issued: debit accounts receivable, credit revenue, credit sales tax payable.
- Accrual, when paid: debit cash, credit accounts receivable.
For vendor bills:
- Cash or modified cash: expense when paid, except for explicit modified-cash balance-sheet items such as fixed assets, loans, and taxes.
- Accrual: on bill, debit expense or asset and credit accounts payable; on payment, debit accounts payable and credit cash.
Magpie prevents ordinary agents from bypassing these rules with generic manual journals, and invoice workflows enforce the A/R versus cash-basis posting semantics directly.
Create accounts as an actor with ledger:write. Assigning a role at create time also requires chart:manage:
./magpie --store .magpie --actor owner ledger account create \
--number 1000 \
--name Checking \
--type asset \
--role bank_account
./magpie --store .magpie --actor owner ledger account create \
--number 4000 \
--name "Consulting Revenue" \
--type revenue \
--role default_service_revenue
Account numbers are optional but first-class. They are stored separately from account IDs, so journal entries continue to reference stable acct:... IDs even if an account is renumbered.
Renumber an existing account:
./magpie --store .magpie --actor owner ledger account number set \
--account-id acct:CHECKING_ID \
--number 1010
Account number rules:
- Numbers must be unique across accounts when present.
- Numbers may contain digits, hyphens, and dots, e.g.
1000,1010.01,2000-10. - Renumbering creates a versioned account update event.
List accounts and capture the generated account IDs:
./magpie --store .magpie --actor owner ledger account list
Account roles tell Magpie what an account means inside accounting workflows. Type alone is not enough: an asset may be cash, accounts receivable, inventory, a fixed asset, or a contra-asset.
List supported roles:
./magpie --store .magpie --actor owner ledger account role list
Assign or update a role as an actor with chart:manage:
./magpie --store .magpie --actor owner ledger account role set \
--account-id acct:CHECKING_ID \
--role operating_cash
Role rules:
- Roles must match account type. For example,
accounts_receivablerequires anassetaccount andsales_tax_payablerequires aliabilityaccount. - Roles such as
operating_cash,accounts_receivable,accounts_payable,sales_tax_payable,retained_earnings, and default revenue roles are unique. - Roles such as
bank_account,fixed_asset,inventory, anddefault_expensecan be assigned to the accounts they represent when allowed by validation. - Workflow commands should require roles, not hard-coded account names or chart numbers.
External Source References
Ledger accounts can carry first-class external source references for bank sync, reconciliation, and migration traceability. This is intentionally stored on the account, not in sidecar notes or name-only conventions.
Example external bank account:
./magpie --store .magpie --actor owner ledger account create \
--number 1010 \
--name "Operating Checking ****1234" \
--type asset \
--role bank_account \
--sensitivity confidential \
--external-source bank_provider \
--external-id bank-account-1 \
--external-type bank_account \
--external-display-name "Operating Checking" \
--external-url https://bank.example.com/accounts/bank-account-1 \
--external-meta account_kind=checking \
--external-meta "nickname=Operating Checking" \
--external-meta 'mask=****1234' \
--external-meta last_four=1234
Stored account JSON includes:
{
"number": "1010",
"role": "bank_account",
"external_refs": [
{
"source_system": "bank_provider",
"external_id": "bank-account-1",
"external_type": "bank_account",
"display_name": "Operating Checking",
"url": "https://bank.example.com/accounts/bank-account-1",
"metadata": {
"account_kind": "checking",
"nickname": "Operating Checking",
"mask": "****1234",
"last_four": "1234"
}
}
]
}
Example non-bank chart account mapping:
{
"number": "2300",
"name": "Sales Tax Payable",
"type": "liability",
"sensitivity": "confidential",
"external_refs": [
{
"source_system": "quickbooks",
"external_id": "42",
"external_type": "chart_account",
"display_name": "Sales Tax Payable",
"metadata": {
"classification": "liability"
}
}
]
}
Submit JSON account definitions with:
./magpie --store .magpie --actor owner ledger account create-json \
--file ./account.json
Add or update an external ref on an existing account:
./magpie --store .magpie --actor owner ledger account external-ref set \
--account-id acct:OPERATING_BANK_ID \
--external-source bank_provider \
--external-id bank-account-1 \
--external-type bank_account \
--external-display-name "Operating Checking" \
--role bank_account \
--external-meta account_kind=checking \
--external-meta "nickname=Operating Checking" \
--external-meta 'mask=****1234' \
--external-meta last_four=1234
--role is optional. When present, the actor must have chart:manage, and the role/type/uniqueness checks are applied in the same account update as the external reference.
If the account already has a ref with the same source_system + external_id, the command replaces that ref. If another account already has that ref, the command fails with a conflict.
Validation rules:
--external-sourceand--external-idare required when any external metadata is provided.--external-sourceis normalized to lowercase.--external-urlmust be an absolute HTTPS URL.metadata.last_four, when present, must contain exactly four digits.- The pair
source_system + external_idmust be unique across ledger accounts.
Customer And Invoice Workflow
Invoices are first-class source documents. Agents should create customers and invoices, then let Magpie generate workflow-originated journal entries according to the active accounting basis. Magpie is bank and financial-institution agnostic: the AI bookkeeping agent interprets source-specific exports and submits normalized JSON with external references.
Minimum account roles for service invoices:
operating_cashorbank_accountfor the payment/deposit account.default_service_revenue,default_product_revenue, orother_incomeon each invoice line.sales_tax_payablewhen the invoice includes sales tax.accounts_receivablewhen the book usesaccrual.
For normalized external imports, line-level revenue_account_id may be omitted. Magpie resolves omitted revenue accounts to the configured default_service_revenue account and fails the import if that role is not configured. Line-level tax_amount_cents may also be supplied; Magpie sums line taxes into invoice-level tax_amount_cents when the invoice-level value is omitted, and rejects mismatches when both are present.
Create or update a customer:
{
"name": "Acme Co",
"external_refs": [
{
"source_system": "billing_platform",
"external_id": "customer-123",
"external_type": "customer",
"display_name": "Acme Co"
}
]
}
./magpie --store .magpie --actor bookkeeping-agent customer create-json \
--file ./customer.json
Create an invoice:
{
"invoice_number": "INV-1001",
"customer_id": "cust:ACME_ID",
"invoice_date": "2026-06-01",
"due_date": "2026-06-30",
"line_items": [
{
"description": "Services",
"revenue_account_id": "acct:SERVICE_REVENUE_ID",
"quantity": 1,
"unit_amount_cents": 100000,
"amount_cents": 100000
}
],
"subtotal_cents": 100000,
"tax_amount_cents": 8500,
"total_cents": 108500,
"external_refs": [
{
"source_system": "billing_platform",
"external_id": "invoice-1001",
"external_type": "invoice"
}
]
}
./magpie --store .magpie --actor bookkeeping-agent invoice create-json \
--file ./invoice.json
For source imports, prefer one normalized external invoice payload. This keeps source-specific parsing in the agent while giving Magpie a first-class, idempotent workflow:
{
"post": true,
"customer": {
"name": "Acme Co",
"external_refs": [
{
"source_system": "billing_platform",
"external_id": "customer-123",
"external_type": "customer",
"display_name": "Acme Co"
}
]
},
"invoice": {
"invoice_number": "INV-1001",
"invoice_date": "2026-06-01",
"due_date": "2026-06-30",
"status": "paid",
"line_items": [
{
"description": "Services",
"quantity": 1,
"unit_amount_cents": 100000,
"amount_cents": 100000,
"tax_amount_cents": 8500
}
],
"subtotal_cents": 100000,
"total_cents": 108500,
"external_refs": [
{
"source_system": "billing_platform",
"external_id": "invoice-1001",
"external_type": "invoice"
}
]
},
"payment": {
"date": "2026-06-15",
"amount_cents": 108500,
"cash_account_id": "acct:OPERATING_CASH_ID",
"external_source": "bank_feed",
"external_id": "txn-123",
"payment_evidence": "external_transaction_match"
}
}
./magpie --store .magpie --actor bookkeeping-agent invoice import-json \
--file ./external-invoice.json
invoice import-json upserts the customer by external reference, creates or reuses the invoice by external reference, posts it when post is true or the external status is open/paid, and marks it paid only when payment evidence and a cash/bank account are provided. A paid import without payment data is rejected instead of recording a paid status without accounting evidence.
Post the invoice:
./magpie --store .magpie --actor bookkeeping-agent invoice post \
--invoice-id inv:...
Posting behavior:
cashandmodified_cash: opens the invoice but does not create A/R or revenue journals yet.accrual: creates a workflow journal for invoice issue: debitaccounts_receivable, credit revenue, and creditsales_tax_payablewhen tax is present.
Mark the invoice paid:
./magpie --store .magpie --actor bookkeeping-agent invoice mark-paid \
--invoice-id inv:... \
--cash-account-id acct:OPERATING_CASH_ID \
--paid-date 2026-06-15 \
--amount-cents 108500 \
--external-source bank_feed \
--external-id txn-123 \
--payment-evidence external_transaction_match
Payment behavior:
cashandmodified_cash: creates a workflow journal that debits cash, credits revenue, and creditssales_tax_payablewhen tax is present. A/R is not used.accrual: requires the invoice to be posted first, then creates a workflow journal that debits cash and creditsaccounts_receivable.
Reverse an incorrect payment:
./magpie --store .magpie --actor bookkeeping-agent invoice reverse-payment \
--invoice-id inv:... \
--payment-id pay:... \
--reversal-date 2026-06-16 \
--reason "invoice was incorrectly marked paid"
Payment reversals create a new workflow journal that exactly offsets the original payment journal. The original payment and journal remain in the audit trail; the payment is marked reversed and the invoice status is recomputed from non-reversed payments.
Workflow journals are stored with origin: "workflow", workflow, posting_semantics, source_document_type, and source_document_id. Invoice workflow writes are idempotent by source key so agents can retry safely.
List source documents:
./magpie --store .magpie --actor bookkeeping-agent customer get --customer-id cust:...
./magpie --store .magpie --actor bookkeeping-agent customer list
./magpie --store .magpie --actor bookkeeping-agent invoice get --invoice-id inv:...
./magpie --store .magpie --actor bookkeeping-agent invoice list
Payout And External Transfer Workflow
Payouts are first-class source documents for provider deposits and other external transfers into a bank account. Magpie does not parse provider exports directly. The AI bookkeeping agent interprets source-specific data, maps it to existing Magpie accounts, and submits normalized JSON with external references.
Minimum account roles:
operating_cashorbank_accountfor the destination bank account.merchant_fees_expensewhen the payout includes processing fees.
The source account is an asset account representing the clearing, processor, or external-transfer balance being reduced. It does not need a provider-specific role.
Import a normalized payout:
{
"date": "2026-06-18",
"description": "Processor batch 2026-06-18",
"source_account_id": "acct:PROCESSOR_CLEARING_ID",
"destination_account_id": "acct:OPERATING_BANK_ID",
"net_amount_cents": 232518,
"fee_amount_cents": 1000,
"fee_expense_account_id": "acct:MERCHANT_FEES_ID",
"external_refs": [
{
"source_system": "payment_processor",
"external_id": "payout-1001",
"external_type": "payout",
"metadata": {
"destination_account_id": "external-bank-1"
}
}
]
}
./magpie --store .magpie --actor bookkeeping-agent payout import-json \
--file ./payout.json
payout import-json stores the payout source document idempotently by external_refs, then creates workflow journals:
payout.receive: debit destination bank account, credit source/clearing account fornet_amount_cents.payout.fee: whenfee_amount_centsis present, debit merchant fee expense, credit source/clearing account.
Workflow journals are stamped with the active accounting basis, source document metadata, and source keys. Agents can retry the same payout import safely without creating duplicate payout documents or journals. The command requires ledger:write, not journal:adjust.
List payout source documents:
./magpie --store .magpie --actor bookkeeping-agent payout get --payout-id payout:...
./magpie --store .magpie --actor bookkeeping-agent payout list
Manual Journal Adjustments
Generic journal creation is restricted. It requires both ledger:write and journal:adjust, and it must include a manual_reason. Default Owner and Admin roles have journal:adjust; ordinary bookkeeping agents should not.
Manual journals are for controlled adjustments, opening/import work, and emergency correction workflows until first-class domain workflows exist. Future bill, bank-match, tax, loan, transfer, and fixed-asset commands should generate workflow-originated journals instead of asking agents to hand-author postings.
Create a balanced manual journal JSON file:
{
"date": "2026-06-29",
"memo": "Opening import adjustment",
"manual_reason": "Owner-approved migration from legacy bookkeeping export",
"source": "quickbooks_export",
"source_key": "qb-row-123",
"postings": [
{
"account_id": "acct:CHECKING_ID",
"debit_cents": 125000
},
{
"account_id": "acct:REVENUE_ID",
"credit_cents": 125000
}
]
}
Submit it:
./magpie --store .magpie --actor owner ledger journal create \
--file ./journal-entry.json
The write is rejected unless total debits exactly equal total credits.
If source and source_key are both present, Magpie uses them as an idempotency key. Re-submitting the same source-tagged entry returns the existing entry instead of creating a duplicate.
Manual entries are stored with:
{
"origin": "manual_adjustment",
"accounting_basis": "cash",
"manual_reason": "Owner-approved migration from legacy bookkeeping export"
}
List journal entries:
./magpie --store .magpie --actor owner ledger journal list
Agent-Mapped External Exports
Magpie does not include a QuickBooks-specific CSV/IIF/QBXML parser in the CLI. The agent is responsible for reading exports from QuickBooks or any other external system and mapping them into Magpie's canonical manual journal JSON when doing a controlled migration or adjustment.
The expected agent flow is:
- Read the external export.
- Use
ledger account listto find exact Magpie account IDs. - Build balanced manual journal JSON with
manual_reason. - Include
sourceandsource_keyfrom the external row or transaction ID. - Submit each canonical entry with
ledger journal create --file FILE.
Example agent-produced journal entry:
{
"date": "2026-06-01",
"memo": "QuickBooks invoice payment INV-1001",
"manual_reason": "Owner-approved migration from QuickBooks export",
"source": "quickbooks_export",
"source_key": "INV-1001-payment",
"postings": [
{
"account_id": "acct:CHECKING_ID",
"debit_cents": 125000
},
{
"account_id": "acct:CONSULTING_REVENUE_ID",
"credit_cents": 125000
}
]
}
Submit it:
./magpie --store .magpie --actor owner ledger journal create \
--file ./agent-mapped-entry.json
This keeps Magpie's CLI narrow and opinionated. The CLI validates permissions, account existence, double-entry balance, source-key idempotency, manual-journal authorization, encryption, and immutable storage; the agent handles source-specific interpretation. Ordinary ongoing bookkeeping should move to domain workflows rather than generic manual journals.
Create a named recovery point before a risky workflow:
./magpie --store .magpie --actor owner snapshot create \
--name before-agent-ledger-workflow-2026-06-29
Read reconstructed state:
./magpie --store .magpie --actor owner state
Read immutable audit nodes:
./magpie --store .magpie --actor owner audit
Both state and audit require audit:read.
init
state
audit
book settings get
book settings set --accounting-basis cash|modified_cash|accrual
customer create-json --file customer.json
customer get --customer-id ID
customer list
invoice create-json --file invoice.json
invoice import-json --file external-invoice.json
invoice post --invoice-id ID
invoice mark-paid --invoice-id ID --cash-account-id ID --paid-date YYYY-MM-DD --amount-cents N
invoice reverse-payment --invoice-id ID --payment-id ID --reversal-date YYYY-MM-DD --reason REASON
invoice get --invoice-id ID
invoice list
rbac defaults repair
rbac permissions
rbac role set --name NAME --permissions p1,p2
rbac user set --id ID --role ROLE
ledger account create --name NAME --type TYPE [--number NUMBER] [--role ROLE]
ledger account create-json --file account.json
ledger account number set --account-id ID --number NUMBER
ledger account role list
ledger account role set --account-id ID --role ROLE
ledger account external-ref set --account-id ID --external-source SOURCE --external-id ID [--role ROLE]
ledger account list
ledger journal create --file entry.json
ledger journal list
note put --title TITLE --body BODY
note put --title TITLE --body-file FILE
note get --id ID
note list
snapshot create --name NAME
Global flags:
--store DIR store directory, default .magpie
--jaybase-url URL hosted Jaybase HTTPS origin, default JAYBASE_URL
--actor USER_ID caller identity, default owner
--role ROLE optional role assertion; must match the actor's assigned role
--store and hosted mode are mutually exclusive. HTTP is accepted only for a loopback development server; non-loopback Jaybase origins must use HTTPS.
In hosted mode, Magpie uses only Jaybase's authenticated /v1 API. It does not read or mount the server's data volume, refs, or encryption key. Jaybase owns transport authentication, concurrent append serialization, encryption at rest, and hosted snapshots/backups.
Each hosted command currently reconstructs state by fetching and decrypting the complete event history. This keeps local and hosted behavior identical, but its latency, bandwidth, and server work grow linearly with history size. Large or high-frequency ledgers will need incremental state caching, compaction, or a server-side materialized-state endpoint before this backend scales efficiently. Hosted replay currently requests payloads for the complete event page, so Jaybase decrypts foreign payloads before Magpie can classify them. A corrupt or key-mismatched foreign payload therefore fails the entire hosted replay page closed and can prevent Magpie state loading or initialization. Metadata-first replay with selective payload fetch for Magpie-owned types remains required to remove that availability coupling. Local replay classifies martin.* nodes from metadata and does not decrypt their payloads.
In local mode, the default store directory is .magpie/:
.magpie/objects/nodes/: immutable JSON node files..magpie/refs/root: current live root hash..magpie/refs/named/: named snapshot roots..magpie/keys/data.key: local AES-256-GCM data key whenJAYBASE_DATA_KEYis not supplied.
Business payloads are encrypted in node files as:
{
"sealed_payload": {
"algorithm": "AES-256-GCM",
"nonce": "...",
"ciphertext": "..."
}
}
The CLI is the only supported Magpie interaction path. Business operations call the same accounting and RBAC layer in local and hosted modes, so ledger invariants and permissions are enforced before persistence.
Hosted Jaybase authenticates and authorizes storage access with bearer credentials. It does not authenticate Magpie's domain-level --actor: the CLI still accepts that caller context and checks it against Magpie RBAC assignments without proving that the token principal is the same person. Run Magpie only in trusted automation or behind a wrapper that binds the authenticated caller to the permitted --actor value.
Keep JAYBASE_TOKEN in a secret manager, grant the lowest hosted role needed, and never pass it on the command line. Local production deployments should provide JAYBASE_DATA_KEY from a managed secret store or KMS and keep local key files out of backups. Hosted deployments manage the data key and encrypted backups on the Jaybase server instead.
Run the release checks in Build and verify before opening a change. Bug reports and focused proposals are welcome in GitHub Issues. Review docs/SECURITY.md before production use, and do not put credentials or confidential financial data in a public issue.
