coffer

Available

Owner-run donor records and giving for a US nonprofit: donors and households, an immutable gift ledger with pledges and soft credits, Stripe card and monthly gifts, emailed receipts, letters and year-end statements, and a checked Little Green Light move-in.

coffer is available as MIT-licensed source for self-hosting. See its public source and release documents for setup. Run Edit Run installation and hosted service are not offered for this project.

Run coffer on your own computer with one line:

npx -y @runeditrun/rer dev just-every/run-coffer

It clones the public source, installs it, sets up a local owner and starts the app; nothing is deployed. To set up without starting, run npx -y @runeditrun/rer setup just-every/run-coffer. Without Node 22 or later, run curl -fsSL https://runeditrun.com/install.sh | sh -s -- dev just-every/run-coffer.

For HappyToHelp: Assisted is in early access; Managed is waitlist only.

Category
status
Licence
MIT
Version
0.1.0
Requirements
9 clauses
Release signature
Signed by just-every · key rer-ed25519-5a42fb9c9c4f8acb2b32386e9e40461c · statement
Target release
v0.1.0 · published
Publisher’s stage
first release; independently gated on a computer and a hosted Cloudflare Worker on 18 October 2026

Outside services

The services the project declares it sends data to, each with its reason.

cloudflare
Worker hosting

Scope and limits

What it will not do

  • US bank (ACH) checkout, PayPal, Venmo and text-to-give
  • Sending notes to the family for tribute gifts
  • A live QuickBooks Online connection
  • Native phone apps
  • Prospect research, grants and planned giving
  • Tax advice or non-cash gift valuation

Declared limits

Status
unmeasured

Requirements

All 9 clauses of coffer’s CONTRACT.md. They say what the project must do; the handover and verification records say what is built and tested.

  1. HTTP-001
    Private app boundary Hono serves /health and owns /api; native owner/operator sessions protect /app and /api/coffer. Every handler checks the actor's role, the exact host and, for cookie changes, the same Origin. Each write batch re-checks the session or key inside the same D1 transaction, so a revoked session, role change or revoked key stops a request in flight. Unknown paths return real 404s. Public donor-facing pages added by the target (GIVE-001, GIVE-005, CAMP-002) live outside /app under their own rules and never widen this boundary. Acceptance: The role-boundary test signs in as owner, member and agent, sends cross-origin writes, revokes a key and deactivates a member, and every refused request returns 401 or 403. Status: done. Evidence: tests/coffer.test.mjs, src/coffer/auth.ts.
  2. DONOR-001
    Donors and households A donor has an email (or none) that no unrelated record shares. A household is found by name or created. Several emails, phones and postal addresses per constituent with one primary of each, and an email shared by members of one household, are DONOR-013; a person's name parts, salutations and envelope name are DONOR-014. Members, admins and the owner add and edit donors; edits are versioned, and a stale version is refused rather than overwritten. Acceptance: A member adds donors through HTTP and the browser journey adds, edits and opens donors; the donor page lists household members. Status: done. Evidence: src/coffer/donors.ts, tests/coffer.test.mjs, tests/browser-journey.mjs.
  3. GIFT-001
    Immutable ledger A gift is an integer-cent line with its date, method, fund, optional pledge and goods-or-services answer. Database triggers refuse changes to amounts, dates, donors or references; a correction is a reversal line of the opposite amount, once per gift. Dollar input is parsed exactly; the API takes <field>Cents for exact integers. Every later gift kind in this contract (online, recurring, split, soft-credited, in-kind, refunded) is written as lines in this one ledger. Acceptance: The ledger test records exact-cent gifts, refuses sub-cent, future-dated and duplicate-reference gifts, reverses one once and reads the lifetime and per-year totals back. Status: done. Evidence: src/coffer/gifts.ts, migrations/core/0016_coffer.sql, tests/coffer.test.mjs.
  4. ACK-001
    Acknowledgement letters Gifts at or over the threshold (default $250) without a live letter are due within the target days (default 14). No letter is prepared until a person has saved the settings; the letter must state donor name, amount, gift date, organization and the goods-or-services statement. A batch freezes its letters and content hash. A person approves the exact hash; export, then a recorded sent date. Imported gifts with an unknown goods answer wait for a person's answer. RCPT-011 and the importers widen this: an imported gift takes its goods answer from the source's non-deductible amount (MOVE-001 to MOVE-006), so only a gift whose source holds no answer waits; and a gift is due only while its acknowledgement status is "not acknowledged", so a gift acknowledged in Coffer or in the incumbent before the move, marked do not acknowledge, or received before the acknowledge-from date (MOVE-007) is never due or overdue, and recording a batch's sent date marks each of its gifts acknowledged by letter with the batch linked. This is the reviewed letter path for printed and hand-mailed acknowledgements; delivering the same approved letters by email is RCPT-002, print layouts are RCPT-003 and one letter per household is RCPT-009. Acceptance: The acknowledgement test has an agent prepare a batch, refuses preparation before settings are reviewed, lets only a person approve, exports the CSV with the exact letter text and records the sent date. Status: done. Evidence: src/coffer/acknowledgements.ts, src/coffer/settings.ts, tests/coffer.test.mjs.
  5. APPEAL-001
    Consent-checked appeals An audience includes only donors whose newest consent for the channel is yes and who have the needed address; exclusions are counted by reason. Each recipient is frozen with the exact consent row; approval, export and sending refuse if any of those rows is no longer current. Sending the approved audience through Mango or Mailchimp is COMM-002; this clause is the consent gate every send passes. Excluding inactive and deceased constituents is DONOR-012; excluding constituents with a solicitation restriction under the reason "do not solicit", and deliberately including one in a run, is DONOR-015; and one piece per household is RCPT-009. Acceptance: The appeal test prepares an audience, counts exclusions by reason, refuses approval after a withdrawal and exports only consenting recipients. Status: done. Evidence: src/coffer/appeals.ts, tests/coffer.test.mjs.
  6. AGENT-001
    Prepare, then a person decides An expiring, revocable bearer key reads records and runs only ack.prepare and appeal.prepare. Approvals keep the preparer's and approver's identity and role with the content hash. Wider, separately granted agent work is DEV-012; no agent key gains an action this clause does not name unless that grant says so. Acceptance: The role-boundary and acknowledgement tests create a key, prepare with it, are refused every other command, record agent and owner roles on the approval and stop working on revocation. Status: done. Evidence: src/coffer/commands.ts, src/coffer/auth.ts, tests/coffer.test.mjs.
  7. IMPORT-001
    Reviewed Stripe move-in Saved Stripe customer and charge list pages go through Base's import kit. Preview writes nothing; apply needs the exact reviewed fingerprint; the journal retains the original pages; undo stops at anything a person has since built on. Failed, refunded, non-USD, duplicate and already-imported objects are skipped with reasons and never enter the plan. This saved-page path stays for history a live connection (PAY-001) is not granted, and for nonprofits that never give Coffer API access. Acceptance: The move-in test previews 15 gifts with every skip reason, refuses a wrong fingerprint, applies, reads the journal and undoes; a second test proves undo stops at a donor a person has since changed. Status: done. Evidence: src/coffer/imports/service.ts, src/coffer/imports/snapshots.ts, tests/coffer.test.mjs.
  8. DATA-001
    Custody and erasure Base portability backs up product records, approvals and import custody, excluding identity and credentials, and restores only into an empty installation. A hosted Coffer backs up and restores the same archive with rer run backup:hosted, authenticated by the owner's session; its restore goes only into a newly deployed, empty environment and keeps that environment's own owner. The owner erases a donor with explicit ERASE: personal data, consent, letter text, gift notes and affected Stripe originals go; gifts move to an anonymous placeholder; hashed suppression stops re-import; a later journal fences older archives. Every record the target adds (interactions, tasks, opportunities, receipts, provider events) joins the same backup and the same erasure. Acceptance: The backup test restores into a fresh installation and compares donor histories and approvals; the hosted-restore test backs up through the command, restores into a fresh installation with large retained originals, keeps the destination owner and refuses a second restore; the erasure tests remove personal data, keep anonymous gifts, refuse re-import and fence an older archive. Status: done. Evidence: src/coffer/archive-policy.mjs, src/coffer/imports/erasure.ts, scripts/backup-hosted.mjs, tests/coffer.test.mjs, tests/hosted-restore.test.mjs.

View as agent

Everything on this page is in these files, copied unmodified from the release. An agent evaluating coffer can read them instead of the page.

seed.json
The manifest: capabilities, outside services, extension points, limits, operating cost and deployment needs.
CONTRACT.md
The requirements on this page, in source form.
AGENTS.md
Instructions for the coding agent that maintains an installation.
LICENSE
RELEASE.json
README.md
Source setup, available commands and implementation status.
OPERATIONS.md
How the project is set up, operated and deployed.
ACCEPTANCE.md
What the publisher accepts as a finished release.

Every project, with its availability, is in /agents/catalogue.json; the agents page describes the machine-readable files.