The monthly requisition, closed out in an afternoon and provably correct.
Schedule of values, G702/G703-format applications for payment, change orders, retainage, stored materials and lien waivers — in one auditable engine that a general contractor can run themselves.
See a worked six-month project → Real output from the real engine: G702/G703 applications, live-formula spreadsheets, and the reconciliation page no competitor ships.
No account. No licence key. No shared secret. No phone-home. No network egress.
git clone https://github.com/MassingCloud/massingbill.git
cd massingbill
docker compose upThat is the whole install — http://localhost:8000.
Then seed a worked project to look at:
docker compose exec app massingbill demoSix periods on a $4,850,000 job, exercising a change order that adds a line, stored material that later installs, a deductive change order, and an architect certifying less than was applied for. The same project is published at massingcloud.github.io/massingbill if you would rather just read it.
Massing Bill can connect to massing.cloud for single sign-on, entitlements and vault storage, but every one of those is an optional adapter that is off by default. Three CI jobs enforce it: the test suite runs with outbound sockets blocked, a job deletes the adapter modules and re-runs the suite, and an import contract forbids the core from importing them at all. Coupling is a build failure here, not a code-review opinion.
Pay-app tools produce forms. None of them hands you a proof that the numbers tie — which is what an owner's auditor, a lender's inspector and a suspicious project accountant actually need.
So the architecture is inverted. The durable asset is a money engine plus a tie-out rule set; the G702/G703 layout is one of several renderers over it.
- Money is integer cents in a
BIGINT. Never a float. Percentages are basis points. Rounding happens at exactly one site per computation, followed by a penny-reconciliation pass soΣ lines == header, always. - Retainage is computed per line and summed — never computed on the header and pushed down, which is the origin of the one-cent disagreements that get pay apps rejected.
- Submitted applications are frozen. Each carries a hashed JSON snapshot of the SOV, retainage rule, change-order log and entitlement, so it re-renders byte-identically in five years even after the SOV has moved on.
- Every number is checked before submit.
line3 == line1 + line2,line7 == prior.line6,G == D + E + F,Σ line retainage == line5, plus policy rules for overbilling, statutory retainage caps, double-billed stored materials and missing waivers. The result is a Reconciliation page in the PDF.
v1.2.0 — every phase is built and tested, P9 included. A ten-period golden project with hand-computed G702 headers reproduces every figure exactly, every period passes tie-out, and the published demo is that same engine's real output.
CHANGELOG.md records what each release is and what it is
not — the known limits are listed there rather than left to be discovered. The
short version: statutory waiver text and deadline day counts ship empty and
refuse until you enter them (see below), ERP integration is file-based rather
than API, and no external penetration test has been run.
See SPEC.md for the full plan and the phase-by-phase acceptance
criteria, docs/competitive-upgrades.md for the
Textura / GCPay / Handle review that shaped the later phases, and
docs/massing-integration.md for what folding
this back into massing.cloud actually requires.
| Phase | Scope | State |
|---|---|---|
| P0 | Foundation, adapters, CI, container | done |
| P1 | Money kernel | done |
| P2 | Orgs, RBAC, projects, schedule of values, audit chain | done |
| P3 | The requisition engine (G702/G703, retainage, change orders, stored materials) | done |
| P4 | Tie-out rule engine — 35 rules | done |
| P5 | PDF / XLSX / CSV / JSON documents | done |
| P6 | Requisition UI, lien waivers, e-signature, compliance, subcontracts, payments | done |
| P6.5 | Statutory deadline engine | done |
| P7a | REST API, API keys, webhooks, OpenAPI 3.1, Python SDK | done |
| P7b | OIDC, S3, and file-based ERP / portal exports | done |
| P8 | Ops runbook, security posture, v1.0.0 | done |
| P9 | massing.cloud entitlements, seats and vault storage | done |
| P10 | Optional: WordPress bridge — gated on the tier decision in SPEC.md §14 |
Requires Python 3.11+.
python -m venv .venv
.venv/bin/pip install -e ".[dev]".venv/bin/pytest -qThe four checks CI runs, which should pass before every commit:
ruff check . && ruff format --check . && mypy massingbill && pytest -qPlus the import contracts that keep the core decoupled:
lint-importsPDF rendering needs the WeasyPrint native stack (pango, cairo). Install
.[render] and the system libraries, or just use the container — the image
carries them.
docs/runbook.md covers install, backups (and how to prove a
backup actually restores), upgrades, the cron jobs, what to do when something is
wrong, and key rotation. Written to be followed by someone who did not build
this.
massingbill/core/ is the calculation with nothing attached: zero runtime
dependencies, standard library only. No Flask, no SQLAlchemy, no session.
from massingbill.core import LineEntry, RetainageSpec, cents, compute_application
app = compute_application(
[LineEntry("001", cents(100_000_00), this_period=cents(40_000_00))],
original_contract_sum=cents(100_000_00),
retainage=RetainageSpec(rate_work_bp=500),
)
app.line8_current_payment_due # int, cents
app.ties_out() # the arithmetic identities holdEverything else — models/, services/, blueprints/ — is an adapter over it.
That means the arithmetic deciding what a contractor is paid does not depend on
a web framework being importable, and it can be copied into another codebase
without dragging one along.
tests/test_architecture.py fails the build if the
core gains a dependency, and a CI job installs nothing at all and imports it —
so the claim is measured rather than asserted.
python scripts/make_vendor_kit.pyProduces a copy with a SHA-256 per file, the commit it came from, and a
stdlib-only drift checker. It refuses to run on a dirty tree, because a pin that
might be wrong is worse than none. See docs/vendorable-core.md.
/api/massingbill/v1, documented in
docs/openapi/massingbill-v1.yaml and
CI-checked against Flask's own URL map in both directions — an undocumented
route and a documented route that does not exist are both build failures.
massingbill apikey mint --organization <id> --name "ERP sync" --scope application:readThe token is printed once; only its SHA-256 digest is stored. Keys are organization-scoped, carry explicit scopes drawn from the same vocabulary as roles, and default to read-only when no scope is given.
from massingbill_client import MassingBillClient
client = MassingBillClient(api_key="mbil_...")
for application in client.applications(status="submitted"):
report = client.tieout(application["id"])
if not report["ok"]:
print(application["number"], report["summary"])Every amount is an object carrying cents (integer, authoritative) and
amount (decimal string). There is no floating-point money anywhere in the API,
in either direction.
Webhooks are queued in the same transaction as the change that caused them
and sent by massingbill webhooks drain, never inside a request — an event
announced before its transaction commits may announce something that then rolls
back. Signing is lowercase hex HMAC-SHA256 of the raw body in
X-Massing-Signature, byte-identical to massing.cloud's scheme, and the test
suite verifies it against massing's own published verifier. Failed deliveries
retry with exponential backoff, and the delivery log keeps what was actually
sent — "did you send it?" is the first question in every integration dispute.
Everything has a working default; see .env.example for the
full surface. The three that matter:
| Variable | Default | Meaning |
|---|---|---|
MASSINGBILL_ENTITLEMENT_PROVIDER |
standalone |
standalone enforces nothing at all |
MASSINGBILL_STORAGE_BACKEND |
local |
Protected local filesystem |
MASSINGBILL_OIDC_PROVIDERS |
(empty) | Empty means local password accounts only |
Two values are required in production, and the container refuses to start
without either: MASSINGBILL_SECRET_KEY (signs sessions) and
MASSINGBILL_ENCRYPTION_KEY (encrypts TOTP seeds and integration tokens at
rest). They are separate on purpose — rotating a session key must not lock every
user out of two-factor. Generate a value for each with massingbill gen-secret.
Prescribed lien-waiver forms have empty bodies and every deadline rule has no day count. Both refuse until a person reads the statute and enters them: a waiver that does not substantially conform can be unenforceable, and a mechanics lien filed one day late is simply gone.
That is not a gap to be filled in later. It is the design, and there is no override.
Entering it is a spreadsheet, not six hundred screens:
massingbill statutory export --organization <id> --out statutory.csvFill verbatim_text for waivers and days plus citation for deadlines, then:
massingbill statutory import statutory.csv --organization <id>Nothing in that path can produce statutory content — it only moves what you supplied. A blank row is skipped rather than read as an answer.
AIA G702® and G703® are published by The American Institute of Architects, which asserts copyright in them and licenses their use.
Massing Bill implements the line structure and the arithmetic, and writes every word itself. That distinction is not wishful: under Baker v. Selden and 37 C.F.R. § 202.1(c), "blank forms… designed for recording information" are not copyrightable — a ruled grid follows from the arithmetic it records. What is protected is the expressive prose: certification wording, instructions, artwork. Every certification paragraph in this product was written from scratch, and no AIA artwork, wording or mark appears in it.
Every rendered document carries a disclaimer that cannot be removed from any renderer. A house-style renderer, a custom renderer that maps onto whatever form your GC requires, and a G703-column-ordered XLSX export (so a licence holder can populate their own official document) all ship alongside it.
Massing Bill is not affiliated with, endorsed by, or sponsored by The American
Institute of Architects. See docs/legal-forms-policy.md.
MIT — see LICENSE. The licence covers the software; it grants no
rights in any third-party trademark.