{
  "markdown": "<div align=\"center\">\n  <img src=\"assets/marks/titlepage.svg\" width=\"880\" alt=\"SVMMA DE GOVERNATIONE — Particularis de Computis et Scripturis, for the governance of agents upon the books. Nulla in libro sine contraria — no debit without a credit. Venetiis MCDXCIV, in the workshop of John Broadway, MMXXVI.\"/>\n</div>\n\n**Least-privilege governance for ERPNext, and a governed agent front door built on top of it — MCP and A2A, one spine behind both. A door admits; it never decides.**\n\nIn 1494, in Venice, Luca Pacioli printed the *Summa de arithmetica* — and inside it, the\ntract that taught the world double-entry bookkeeping. He wrote it down not because merchants\ncouldn't count, but because they couldn't **trust**. His rule is one sentence deep: **no debit\nwithout a credit.** Every action gets an equal, opposite, recorded counterpart — and a book\nthat doesn't balance confesses on the spot. Five hundred years of *prove it or it didn't happen*.\n\nThis repo is that tract, written again for a new clerk. The merchant's problem hasn't changed;\nthe clerk has. He balanced books for merchants who couldn't watch every clerk. This balances\nthem for owners who can't watch every agent. Same problem, five centuries apart. Same fix.\n\n<strong>See it before you read the rest: <a href=\"docs/demo/\">the demo</a>.</strong> One command on a\nbare <code>pip install pacioli pacioli-guard</code> shows the floor deciding, and a sealed receipt\nthat will not be quietly rewritten. Every verdict is the shipped code; nothing is staged.\n\n<div align=\"center\">\n  <img src=\"assets/plates/tavola-1-pacioli-portrait.jpg\" width=\"560\" alt=\"Portrait of Luca Pacioli, attributed to Jacopo de' Barbari, 1495\"/>\n  <br/><em>Tavola I — The friar at his slate, attributed to Jacopo de' Barbari, 1495. The glass\n  rhombicuboctahedron hangs at his left; the Summa sits under the dodecahedron at his right.\n  This repo's device is that hanging solid.</em>\n</div>\n\n<img src=\"assets/marks/rule.svg\" width=\"100%\" height=\"10\" alt=\"\"/>\n\n## Distinctio I — Of the Two Instruments\n\n<img src=\"assets/marks/dropcap-T.svg\" width=\"76\" align=\"left\" alt=\"T\"/>\n\n**There are two instruments in this house, and they compose — they do not couple.** The first\nguards every **api-key** credential on the site whether an agent is involved or not; the second is\nthe one governed door an agent may use. The first is the floor under that door; the second stands\non it and submits to it. What follows is each instrument as the treatise would have it: what it is,\nwhat it refuses, and how it is installed.\n\n**What the floor covers, plainly, because a floor that overstates itself is not a floor.** Two\nquestions, two of frappe's own public hooks, each at the altitude where its question is legible.\n\n*Which credential is acting, and what may it call?* The credential gate rides `auth_hooks`, which\nruns when an api-key credential authenticates. It governs `token`/`Basic` api-key requests across\nevery REST mount, and it does **not** govern OAuth2 `Bearer` tokens, desk/cookie sessions,\nbackground jobs, the scheduler, server scripts, or the bench console. \"Which credential is this\"\nonly exists at authentication time, so this gate can only live here, and that boundary is inherent\nto it.\n\n*Was this act consented?* That is a property of a document, so it is enforced on the document, via\n`doc_events` on `before_submit` and `before_cancel`, plus `before_gl_preview` and `before_sl_preview`\nsince guard 0.13.0. The last two gate a **rehearsal** of a posting rather than a posting: ERPNext\npreviews a ledger by performing it and rolling the transaction back, so previewing a submit requires\nthe same marker the submit requires, and does not spend it. Every path that **posts to the ledger** through\nthe ORM passes it: REST, `run_doc_method`, `frappe.client.submit`, `frappe.client.insert` with a\nsubmitted body, the desk Save/Submit endpoint, bulk submit, a raw docstatus field write followed by a\nsave, a background job, a server script, and the bench console. **Consent does not inherit the credential gate's transport boundary.**\n\nWhat still walks around **both**, stated up front rather than discovered later:\n\n1. A write that skips the document lifecycle entirely: raw SQL, and `db_update`/`db_set`-style field\n   writes. ERPNext core does this itself when reposting.\n2. An actor who can break the grant read. The consent handler runs on every document on the site, so\n   if reading the grant raises it returns rather than throwing — a wildcard handler that crashes\n   takes the site with it. A deliberate trade, and a real residual.\n3. **`Document.discard`.** It is whitelisted, so it is reachable, and it sets `docstatus = 2` with\n   `db_set` while firing `before_discard` rather than `before_cancel`. It refuses anything that is\n   not a draft, so **no ledger entry is ever reversed by it** and nothing posts — the exposure is a\n   draft being taken out of play, not books moving. Gating it wants its own act type rather than\n   being folded into \"cancel\", since consenting to reverse a posted entry and consenting to abandon\n   a draft are different permissions. Named here rather than rounded off.\n\nNo single frappe extension point covers all of it, so coverage here is a composition with a stated\nresidual rather than a claim of totality, which is a thing the platform cannot give anyone.\n\n<br clear=\"left\"/>\n\n### The Guard — the counting-house door\n\n*No one writes in the books but the appointed hand, and only in the books appointed to them.*\n\nA Frappe/ERPNext **bench app** (distribution name `pacioli-guard` on PyPI; installs as the\n`pacioli_guard` bench app). It binds any API credential — an integration, a\nZapier/n8n flow, a script, a vendor token, a cron job, an AI agent — to an allowlist of methods\n(and, if granted, DocTypes), enforced at the credential layer through Frappe's public\n`auth_hooks` extension point, deny-by-default. No core fork. It governs **every api-key**\ncredential on the site, agent or not — see the coverage note above for what that does and does not\nreach, and for the separate document-layer gate that carries consent.\n\n```bash\n# from your bench directory: wheel into the bench env, then install the app\nenv/bin/pip install pacioli-guard\nbench --site <your-site> install-app pacioli_guard\n```\n\n### The Broker — hand it the books\n\n*The clerk may propose; only the merchant disposes.*\n\nA standalone, pip-installable broker (`pip install pacioli`) that gives an AI agent a\ngoverned way to touch ERPNext — through the door of your choosing: **MCP or A2A**, one spine\nbehind both. **51 governed doctypes, 265 tools** — the full submittable transaction surface of\nan ERPNext company — every write through PLAN → CONSENT → execute → PROVE, deny-by-default\nbeyond that. The door admits; the spine decides.\n\n```bash\npip install pacioli\n```\n\n### How the instruments compose\n\n**Guard is the floor; the broker is one consumer that binds itself to it.** Guard scopes and\nenforces *any* credential on the site — you don't need to run an agent to need it. The broker\nis the agent-facing front door, and its own ERPNext credential must itself be\n`pacioli_guard`-scoped to exactly the calls it makes (the governed doctypes and their\nsubmit/cancel vectors — shipped as data lists the deploy kit applies). Without that scoping,\nanything holding the broker's raw\ncredential can call ERPNext's REST API directly and bypass PLAN, CONSENT, and PROVE entirely —\nso the broker's own README states that scoping as a hard precondition, not an optional\nhardening step.\n\n**And scoping alone is not sufficient — this was proven against a live bench on 2026-07-25, and\nit is worth stating plainly rather than leaving as an inference.** The broker must be allowed to\nsubmit invoices, because submitting invoices is what the broker is *for*. So its credential is\nallowed to submit invoices, and a direct `run_doc_method` call holding that credential submitted\none with no plan, no marker and no receipt. The ledger moved. The guard behaved exactly as\ndesigned and the allowlist was exactly right: no allowlist can close this, because the call being\nmade is the call the broker exists to make. Possession of the key was permission to post.\n\nThe guard closes it at the floor rather than in the broker: `API Key Scope.require_consent`\n(opt-in, off by default) refuses a docstatus-changing act unless it carries a live, single-use\n`Pacioli Consent Marker` minted for that exact document **and that exact act**, and spends the\nmarker on use. Consent to post is not consent to reverse: cancel moves the same document and\nwrites the opposite GL entries, so a submit marker never spends on a cancel. And the marker must\nbe written by a **different principal** than the credential it authorises — the floor compares the\nminter against the caller and refuses a match, because a credential that can mint its own consent\nis a gate writing its own permission slip. `minted_by` is set by the server from the authenticated\nsession, overwriting anything the caller supplies, so that separation is established rather than\nself-reported.\n\nThis gate is enforced on the **document**, not on the request: `doc_events` `before_submit` /\n`before_cancel`, and since guard 0.13.0 `before_gl_preview` / `before_sl_preview` as well. That is\ndeliberate and it is the difference between governing one door and\ngoverning the act. A credential holder who avoids the api-key REST path entirely, via an OAuth\ntoken, a desk session, a background job, a server script or the bench console, **still meets this\ngate**.\n\nMeeting it and passing it are different things, and the difference is worth stating rather than\nleaving to be discovered. A marker is presented in an HTTP header, so on a path that has no HTTP\nrequest there is no way to present one: a consent-gated principal acting through a background job or\nthe bench console can only ever be **refused** there. That is fail-closed and it is the intended\nposture, but do not read the paragraph above as \"those paths work the same way\" — they are covered\nin the sense that they are stopped, not in the sense that they are usable.\n\nBeing opt-in has a consequence worth stating plainly: installing the app changes nothing by itself.\nAn install is exposed to the bypass above until someone turns `require_consent` on for the\ncredentials that matter.\n\nThis is composition, not coupling: Guard is independently useful, and not only for agents. Its two\nhalves have two different reaches, and the difference is the honest part — credential scope binds\nany **api-key** credential (agent or otherwise) and sees nothing else, while the consent gate runs\nat the document layer and covers every ORM path — desk sessions, background jobs, the scheduler,\nserver scripts, the bench console — for a credential you have gated. The broker is the one piece\nthat chooses to sit on top of it and honor the same floor it enforces on everything else.\n\n<img src=\"assets/marks/rule.svg\" width=\"100%\" height=\"10\" alt=\"\"/>\n\n## Distinctio II — Of the Memorandum, the Journal, and the Ledger\n\n<img src=\"assets/marks/dropcap-D.svg\" width=\"76\" align=\"left\" alt=\"D\"/>\n\n**Double entry is not a metaphor here. It is the design.** The bookkeeping tract of the Summa\n(*Particularis de Computis et Scripturis*) organizes a merchant's truth into three books, and\nslice for slice they are this system. In his journal every entry named its debit with *per* and\nits credit with *a* — nothing moved on one leg. Read the spread the way he ruled it:\n\n<br clear=\"left\"/>\n\n| folio | *per* — his book (1494) | *a* — here |\n|---|---|---|\n| ¹ | The **memorandum** (*memoriale*) — every transaction written down first, roughly, before any formal entry | **PLAN** — `plan_submit` / `plan_cancel` write the memorandum: the projected GL and the risk flags, dated, bound to the draft. Nothing posts. |\n| ² | The **journal** (*giornale*) — each entry rewritten in fixed form, its debit marked *per*, its credit marked *a* | **PROVE** — the hash-chained receipt book. The intent receipt is the *per*, the outcome the *a*; an intent with no outcome is a debit with no credit, and the trial balance surfaces it as an orphan. |\n| ³ | The **ledger** (*quaderno*) — the book of account itself | **ERPNext's GL** — the broker never writes in it directly; every entry reaches the ledger through the journal's discipline. |\n\nAnd the same law runs across every pillar — for every action, its recorded counterpart:\n\n| folio | the action (*per*) | its recorded counterpart (*a*) |\n|---|---|---|\n| ⁴ | An agent wants to write | **PLAN** — the projected GL impact, written down *before* the act. The mirror entry precedes the entry. |\n| ⁵ | A plan exists | **CONSENT** — a human mints the marker, out of band. Agent proposes, human disposes: two hands on every posting, never one. |\n| ⁶ | The write fires | **PROVE** — an *intent* receipt before, an *outcome* receipt after. A write missing its outcome is an **orphan**, and it surfaces exactly the way an unbalanced trial balance does. |\n| ⁷ | A posting stands | **UNDO** — ERPNext's own cancel/amend is double-entry: the reversal posts equal-and-opposite rows. Nothing erased, everything answered. |\n| ⁸ | A credential exists | **Guard** — no capability without its explicit grant line. Deny-by-default is \"no entry without authorization,\" applied at the credential layer. |\n\nSame law at every layer: **nothing moves without its counterpart.** A plan without consent\ndoesn't post. A write without a receipt is flagged. A credential without a grant is refused.\nThe one system you *can't* trust is the one where an action can happen alone — unplanned,\nunconsented, unreceipted. Everything Pacioli refuses is exactly that: the lone entry.\n\n<div align=\"center\">\n  <img src=\"assets/plates/tavola-2-leonardo-polyhedron.jpg\" width=\"360\" alt=\"The hollow rhombicuboctahedron drawn by Leonardo da Vinci for De divina proportione\"/>\n  <br/><em>Tavola II — Vigintisex Basium Planum Vacuum: the same solid, drawn hollow by\n  Leonardo da Vinci for Pacioli's</em> De divina proportione. <em>Leonardo illustrated Luca's\n  book; the debt is repaid here.</em>\n</div>\n\n<img src=\"assets/marks/rule.svg\" width=\"100%\" height=\"10\" alt=\"\"/>\n\n## Distinctio III — Of the Counting-House Door\n\nHis counsel, applied at the credential layer:\n\n- **Two hands on every entry.** The clerk writes; the merchant grants. The consent marker is\n  minted (`pacioli mint`) outside the agent's reach — no posting on one hand's authority.\n- **The counting-house door.** No one writes in the books but the appointed hand, and only in\n  the books appointed to them — that is Guard, per-credential and per-doctype, deny-by-default.\n- **The registered book.** The books take their authority from a mark held outside the\n  bookkeeper's own hand. `pacioli anchor` carries the receipt-book's head off the box — a\n  registration the book cannot rewrite, so a truncated or swapped book confesses against it.\n\n<img src=\"assets/marks/rule.svg\" width=\"100%\" height=\"10\" alt=\"\"/>\n\n## Distinctio IV — Of the Closing of the Books\n\n> ❧ *Do not go to sleep until the debits equal the credits.*\n\nHe gave merchants that rule, and it is the operating rule here too. `pacioli verify` is that\nsleep test — run the trial balance, and if intent and outcome don't pair, the book itself\ntells you.\n\n**The closed books.** Closing the ledger is his own operation: rule off the book, carry the\nbalances forward. The broker refuses to write in a closed book — a closed Accounting Period, a\nPeriod-Closing-Voucher boundary, a frozen-books date — and never slips a backdated or\nfuture-dated entry past the ruling-off.\n\n<img src=\"assets/marks/rule.svg\" width=\"100%\" height=\"10\" alt=\"\"/>\n\n## Distinctio V — Of the Inventory\n\n<img src=\"assets/marks/dropcap-O.svg\" width=\"76\" align=\"left\" alt=\"O\"/>\n\n**Open no books before the survey.** He starts the merchant with a complete inventory before a\nsingle entry — what you hold, what is owed, what is missing. `pacioli doctor` is that survey\nfor this house: what is reachable, what is granted, what refuses and why — and the road does\nnot proceed until the doctor says `ready.` The census (`pacioli close`) then keeps the\ninventory honest for every period after: statement, reconciliation, response — every finding\naccounted for, or the book says so out loud.\n\n<br clear=\"left\"/>\n\n<img src=\"assets/marks/rule.svg\" width=\"100%\" height=\"10\" alt=\"\"/>\n\n## Status\n\n(Current versions live in each package's `pyproject.toml`/`CHANGELOG.md` — this README\ndeliberately does not restate them.)\n\nGuard — deny-by-default credential scoping with the **deny-unknown** posture (an\nunrecognized generic RPC is denied even if granted; per-doctype grants + four curated safe\nmethods are the whole surface), live-proven on a real Frappe v16 bench (Gates 1, 7, 10).\n\nBroker — **51 governed doctypes, 265 tools; 48 of the 51 live-proven end-to-end** on a real\nERPNext v16 bench as a guard-scoped seat (Gates 2–10, envelopes E1–E8, and the 2026-07\nlive-prove sweep): governed submit/cancel/amend across accounts, stock, assets, manufacturing,\nand subcontracting; Workflow-SoD consent; cascade cancel with dependent graphs (a 3-node Asset\ngraph under one consent, live); **governed Payment Reconciliation** (stricter than ERPNext\nitself — the closed-books belt ERPNext skips for reconciliation, proven live, PHASE X); the\narmed-Budget control-plane probe (arm → the bench refuses the PO → disarm → the same PO\npasses, every step disclosed pre-consent); the off-box anchor; a certified least-privilege\nreference seat; and the founding refusals (no debit without a credit — live). The whole arc is\nwritten up leg-by-leg in [`SCOPED-TOKEN-PROOF.md`](./SCOPED-TOKEN-PROOF.md) (PHASES A–X). See\n[`broker/README.md`](./broker/README.md#honest-scope) for exactly what ships and what doesn't.\n\n## License\n\nApache-2.0.\n\n<img src=\"assets/marks/rule.svg\" width=\"100%\" height=\"10\" alt=\"\"/>\n\n<div align=\"center\">\n  <img src=\"assets/marks/device.svg\" width=\"90\" alt=\"The printer's device: the glass solid of the portrait\"/>\n\n  <em>Here ends the Summa of Governance: printed upon the counsel of Fra Luca Pacioli,\n  who taught that no entry stands alone; set in these types for the governance of agents\n  upon the books.</em>\n\n  <strong>VENETIIS MCDXCIV · IN THE WORKSHOP OF JOHN BROADWAY · MMXXVI</strong>\n</div>\n",
  "bytes": 18299,
  "sha": "b3c4e1052ca6e8ce203aa2bc1e3fc49f630434e518cf62ceaa69dfe3a2efa278",
  "repo_slug": "john-broadway/pacioli",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_john_broadway_pacioli_3f910389/readme"
}