Skip to content

Repository files navigation

Massing Bill

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.

CI License: MIT Python 3.11+

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.


Standalone. Actually standalone.

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 up

That is the whole install — http://localhost:8000.

Then seed a worked project to look at:

docker compose exec app massingbill demo

Six 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.

Why it exists

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.

Status

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

Development

Requires Python 3.11+.

python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest -q

The four checks CI runs, which should pass before every commit:

ruff check . && ruff format --check . && mypy massingbill && pytest -q

Plus the import contracts that keep the core decoupled:

lint-imports

PDF rendering needs the WeasyPrint native stack (pango, cairo). Install .[render] and the system libraries, or just use the container — the image carries them.

Running it

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.

The core is vendorable

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 hold

Everything 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.py

Produces 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.

The API

/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:read

The 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.

Configuration

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.

Statutory content ships empty

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.csv

Fill 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.

A note on the AIA forms

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.

Licence

MIT — see LICENSE. The licence covers the software; it grants no rights in any third-party trademark.

About

Standalone AIA-style GC billing engine: G702/G703 pay applications, schedule of values, change orders, retainage, stored materials and lien waivers — with a provable tie-out. Self-hostable, MIT.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages