from __future__ import annotations
import argparse
import calendar
import json
import sys
from datetime import date
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
LEDGER = ROOT / "sponsors.json"
SPONSORS_MD = ROOT / "SPONSORS.md"
README = ROOT / "README.md"
START_MARK = "<!-- sponsors:start -->"
END_MARK = "<!-- sponsors:end -->"
SYMBOLS = {"USD": "$", "EUR": "\u20ac", "GBP": "\u00a3"}
KIND_LABEL = {
"cash": "Cash",
"in-kind": "In kind",
"credits": "Credits",
}
EARNINGS_SOURCE = "Account earnings"
GENERATED_BY = (
"Generated by `scripts/sync-sponsors.py` from [`sponsors.json`](sponsors.json). "
"Do not edit by hand, run the script."
)
def money(amount: float, currency: str) -> str:
sym = SYMBOLS.get(currency, "")
body = f"{amount:,.0f}" if float(amount).is_integer() else f"{amount:,.2f}"
return f"{sym}{body}" if sym else f"{body} {currency}"
def tokens(count: int) -> str:
if count >= 1_000_000_000:
return f"{count / 1_000_000_000:g}B"
if count >= 1_000_000:
return f"{count / 1_000_000:g}M"
return f"{count:,}"
def parse_start(raw: str) -> tuple[int, int, int | None]:
parts = raw.split("-")
year, month = int(parts[0]), int(parts[1])
day = int(parts[2]) if len(parts) > 2 else None
return year, month, day
def billings_to_date(start: str, as_of: date, fallback_day: int) -> int:
year, month, day = parse_start(start)
day = day or fallback_day
count = 0
while True:
safe_day = min(day, calendar.monthrange(year, month)[1])
if date(year, month, safe_day) > as_of:
break
count += 1
month += 1
if month > 12:
month = 1
year += 1
return count
def row_sort_key(row: dict) -> tuple[int, int, int]:
year, month, day = parse_start(row["date"])
return (year, month, day or 1)
def human_date(raw: str, precision: str) -> str:
year, month, day = parse_start(raw)
name = calendar.month_name[month]
if precision == "month" or day is None:
return f"{name} {year}"
return f"{name} {day}, {year}"
def load_ledger() -> dict:
return json.loads(LEDGER.read_text())
def validate(ledger: dict) -> None:
sponsors = ledger.get("sponsors") or []
ids = {s.get("id") for s in sponsors}
if not ids:
raise SystemExit("sponsors.json lists no sponsors")
known_kinds = {"cash", "in-kind", "credits"}
seen: set[str] = set()
for entry in ledger.get("contributions") or []:
eid = entry.get("id") or "<no id>"
if eid in seen:
raise SystemExit(f"{eid}: duplicate contribution id")
seen.add(eid)
if entry.get("sponsor") not in ids:
raise SystemExit(f"{eid}: unknown sponsor {entry.get('sponsor')!r}")
kind = entry.get("kind")
if kind not in known_kinds:
raise SystemExit(
f"{eid}: unknown kind {kind!r}, expected one of {sorted(known_kinds)}"
)
if kind == "cash":
if not isinstance(entry.get("amount"), (int, float)):
raise SystemExit(f"{eid}: cash entry needs a numeric amount")
if not entry.get("currency"):
raise SystemExit(f"{eid}: cash entry needs a currency")
if not entry.get("source"):
raise SystemExit(
f"{eid}: cash entry needs a source, the place the money came from"
)
if kind == "in-kind" and not isinstance(entry.get("rate"), dict):
raise SystemExit(f"{eid}: in-kind entry needs a rate")
if kind == "credits" and entry.get("amount") is not None:
raise SystemExit(f"{eid}: credits must not carry a money amount")
raw = entry.get("start") or entry.get("date")
if not raw:
raise SystemExit(f"{eid}: needs a start or a date")
try:
parse_start(str(raw))
except (ValueError, IndexError):
raise SystemExit(f"{eid}: unparseable date {raw!r}")
provider = ledger.get("provider")
if provider and provider.get("disclosed") is not False:
raise SystemExit(
"provider.disclosed must be false: the built-in provider is a trade secret"
)
def self_test() -> int:
day = date
billing_cases = [
("2026-02-14", day(2026, 2, 13), 14, 0),
("2026-02-14", day(2026, 2, 14), 14, 1),
("2026-02-14", day(2026, 10, 14), 14, 9),
("2026-02-14", day(2026, 10, 17), 14, 9),
("2026-02-14", day(2026, 11, 13), 14, 9),
("2026-02-14", day(2026, 11, 14), 14, 10),
("2026-01-31", day(2026, 2, 28), 31, 2),
]
for start, as_of, fallback, want in billing_cases:
got = billings_to_date(start, as_of, fallback)
assert got == want, f"billings_to_date({start}, {as_of}) = {got}, want {want}"
assert money(30, "USD") == "$30", money(30, "USD")
assert money(20, "EUR") == "\u20ac20", money(20, "EUR")
assert tokens(8_000_000_000) == "8B", tokens(8_000_000_000)
assert tokens(9_000_000_000) == "9B", tokens(9_000_000_000)
fixture = {
"billing_day": 14,
"sponsors": [
{"id": "a", "name": "A", "role": "r", "sponsoring_since": "2026-02-14"},
{"id": "b", "name": "B", "role": "r", "sponsoring_since": "2026-02-14"},
],
"contributions": [
{
"id": "c1",
"sponsor": "a",
"kind": "cash",
"description": "d",
"date": "2026-10-01",
"amount": 8,
"currency": "USD",
"source": "Inference top-up",
"purpose": "p",
},
{
"id": "c2",
"sponsor": "b",
"kind": "cash",
"description": "d",
"date": "2026-10-02",
"amount": 20,
"currency": "EUR",
"source": "Buy Me a Coffee",
"purpose": "p",
},
{
"id": "c3",
"sponsor": "a",
"kind": "credits",
"description": "d",
"start": "2026-05",
"amount": None,
"rate": None,
"purpose": "p",
},
{
"id": "c4",
"sponsor": "a",
"kind": "in-kind",
"description": "d",
"start": "2026-02-14",
"rate": {"amount": 30, "currency": "USD", "period": "month"},
"purpose": "p",
},
],
}
validate(fixture)
built = build(fixture, day(2026, 10, 17))
assert built["cash_totals"] == {"USD": 8, "EUR": 20}, built["cash_totals"]
assert built["kind_totals"] == {"USD": 270}, built["kind_totals"]
assert len(built["rows"]["credits"]) == 1
assert len(built["rows"]["cash"]) == 2
assert built["rows"]["in-kind"][0]["billings"] == 9
for bad, why in [
({**fixture, "sponsors": []}, "no sponsors"),
({**fixture, "provider": {"name": "leak", "disclosed": True}}, "disclosed provider"),
]:
try:
validate(bad)
except SystemExit:
pass
else:
raise AssertionError(f"validate accepted {why}")
for entry, why in [
({**fixture["contributions"][0], "kind": "nonsense"}, "unknown kind"),
({**fixture["contributions"][0], "amount": None}, "cash with no amount"),
({**fixture["contributions"][0], "source": ""}, "cash with no source"),
({**fixture["contributions"][2], "amount": 100}, "credits with a money value"),
({**fixture["contributions"][0], "sponsor": "ghost"}, "unknown sponsor"),
]:
bad = {**fixture, "contributions": [entry]}
try:
validate(bad)
except SystemExit:
pass
else:
raise AssertionError(f"validate accepted {why}")
md = render_sponsors_md(fixture, built)
disclosure = md.split("## Disclosure", 1)[1].split("## How to sponsor", 1)[0]
assert "http" not in disclosure, "the disclosure section must carry no link"
assert "buymeacoffee" not in md.lower(), "no payment link may be rendered"
assert "$8" in md and "\u20ac20" in md, "cash must render per currency"
assert "$270" in md, "in-kind must render as rate times billings"
print("self-test: OK")
return 0
def build(ledger: dict, as_of: date) -> dict:
sponsors = {s["id"]: s for s in ledger["sponsors"]}
billing_day = int(ledger.get("billing_day", 1))
rows = {"cash": [], "in-kind": [], "credits": []}
for entry in ledger["contributions"]:
sponsor = sponsors[entry["sponsor"]]
kind = entry["kind"]
if not sponsor.get("public", True) or not entry.get("public", True):
continue
if kind == "cash":
rows["cash"].append({**entry, "sponsor_name": sponsor["name"]})
elif kind == "in-kind" and entry.get("rate"):
billings = billings_to_date(entry["start"], as_of, billing_day)
rate = entry["rate"]
rows["in-kind"].append(
{
**entry,
"sponsor_name": sponsor["name"],
"billings": billings,
"committed": rate["amount"] * billings,
"currency": rate["currency"],
}
)
elif kind == "credits":
rows["credits"].append({**entry, "sponsor_name": sponsor["name"]})
cash_totals: dict[str, float] = {}
for row in rows["cash"]:
cash_totals[row["currency"]] = cash_totals.get(row["currency"], 0) + row["amount"]
kind_totals: dict[str, float] = {}
for row in rows["in-kind"]:
kind_totals[row["currency"]] = kind_totals.get(row["currency"], 0) + row["committed"]
return {
"sponsors": sponsors,
"rows": rows,
"cash_totals": cash_totals,
"kind_totals": kind_totals,
"as_of": as_of,
}
def render_sponsors_md(ledger: dict, built: dict) -> str:
as_of = built["as_of"]
rows = built["rows"]
out: list[str] = []
out.append("<!-- This file is generated. See scripts/sync-sponsors.py -->")
out.append("")
out.append("# \U0001f91d Sponsors")
out.append("")
out.append(
"OpenCrabs is community funded. No venture capital, no investors, no "
"company behind it. The people listed here pay for the subscriptions, "
"the inference and the credits that keep the agent running, and some of "
"them also write the code."
)
out.append("")
out.append(
f"Numbers below are derived from [`sponsors.json`](sponsors.json) as of "
f"**{human_date(as_of.isoformat(), 'day')}**. Currencies are never "
"converted, so each one is totalled on its own."
)
out.append("")
out.append("## Totals")
out.append("")
out.append("| Type | Total | How it is counted |")
out.append("|---|---|---|")
for currency, total in sorted(built["cash_totals"].items()):
out.append(
f"| Cash | **{money(total, currency)}** | Transferred, not converted |"
)
for currency, total in sorted(built["kind_totals"].items()):
out.append(
f"| In kind | **{money(total, currency)}** | Subscription rate times billings to date |"
)
for row in rows["credits"]:
usage = row.get("usage") or {}
span = ""
if usage.get("estimate_low") and usage.get("estimate_high"):
span = (
f"{tokens(usage['estimate_low'])} to "
f"{tokens(usage['estimate_high'])} {usage.get('unit', 'tokens')}"
)
out.append(
f"| Credits | **{span or 'granted'}** | {row['description']}. No money value recorded |"
)
out.append("")
out.append("## Who is behind it")
out.append("")
out.append("| Sponsor | Role | Sponsoring since | Code since |")
out.append("|---|---|---|---|")
for sponsor in ledger["sponsors"]:
if not sponsor.get("public", True):
continue
code = sponsor.get("code")
code_cell = human_date(code["since"], "day") if code else "No code"
out.append(
f"| [{sponsor['name']}](https://github.com/{sponsor['github']}) "
if sponsor.get("github")
else f"| {sponsor['name']} "
)
out[-1] += (
f"| {sponsor['role']} | {human_date(sponsor['sponsoring_since'], 'day')} "
f"| {code_cell} |"
)
out.append("")
if rows["cash"]:
out.append("## Cash")
out.append("")
out.append("| Date | Sponsor | Amount | Source | For |")
out.append("|---|---|---|---|---|")
for row in sorted(rows["cash"], key=row_sort_key):
out.append(
f"| {human_date(row['date'], row.get('date_precision', 'day'))} "
f"| {row['sponsor_name']} | {money(row['amount'], row['currency'])} "
f"| {row['source']} | {row['purpose']} |"
)
out.append("")
earned = [r for r in rows["cash"] if r["source"] == EARNINGS_SOURCE]
if earned:
total = sum(r["amount"] for r in earned)
out.append(
f"Of the cash above, {money(total, earned[0]['currency'])} is "
"money the account had already earned, moved back into inference "
"rather than taken out. The rest is paid from a sponsor's own pocket."
)
out.append("")
if rows["in-kind"]:
out.append("## Subscriptions paid directly")
out.append("")
out.append(
"These are recurring costs a sponsor pays out of pocket rather than "
"money handed to the project. They fund both the development loop and "
"model access for everyone using OpenCrabs in Telegram and Discord."
)
out.append("")
out.append("| Sponsor | Subscription | Rate | Since | Billings to date | Committed |")
out.append("|---|---|---|---|---|---|")
for row in rows["in-kind"]:
out.append(
f"| {row['sponsor_name']} | {row['description']} "
f"| {money(row['rate']['amount'], row['rate']['currency'])}/month "
f"| {human_date(row['start'], 'day')} | {row['billings']} "
f"| {money(row['committed'], row['currency'])} |"
)
out.append("")
replacements = [r for r in rows["in-kind"] if r.get("replacement_rate")]
if replacements:
out.append("### Replacement value today")
out.append("")
out.append(
"What the same subscriptions would cost at current list prices. "
"Shown for context. The totals above record what was actually "
"committed, not this."
)
out.append("")
out.append("| Sponsor | Subscription | List price today | Billings to date | Value today |")
out.append("|---|---|---|---|---|")
for row in replacements:
rate = row["replacement_rate"]
value = rate["amount"] * row["billings"]
out.append(
f"| {row['sponsor_name']} | {row['description']} "
f"| {money(rate['amount'], rate['currency'])}/month "
f"| {row['billings']} | {money(value, rate['currency'])} |"
)
out.append("")
if rows["credits"]:
out.append("## Credits and access")
out.append("")
out.append(
"Granted credits carry no money amount here on purpose. Nobody paid a "
"rate for them, so any figure would be invented. Usage is recorded instead."
)
out.append("")
out.append("| Sponsor | Programme | Since | Usage |")
out.append("|---|---|---|---|")
for row in rows["credits"]:
usage = row.get("usage") or {}
span = ""
if usage.get("estimate_low") and usage.get("estimate_high"):
span = (
f"{tokens(usage['estimate_low'])} to "
f"{tokens(usage['estimate_high'])} {usage.get('unit', 'tokens')} burned"
)
out.append(
f"| {row['sponsor_name']} | {row['description']} "
f"| {human_date(row['start'], row.get('start_precision', 'day'))} "
f"| {span or 'Granted'} |"
)
out.append("")
out.append("## Where the money goes")
out.append("")
out.append(
"Subscriptions first, then inference, then GPU. The first cash "
"contributions went straight into the recurring subscriptions. Everything "
"since has gone to inference credits."
)
out.append("")
out.append("## Disclosure")
out.append("")
out.append(
"Two things are kept out of this page on purpose. The inference accounts "
"listed above pay for the built-in provider that serves free model access "
"to every OpenCrabs user, and that provider is not named and not linked "
"here. Payment links are not published either: the place a sponsorship "
"comes from is named, the link is not."
)
out.append("")
out.append("## How to sponsor")
out.append("")
out.append(
"Sponsorship comes in through Buy Me a Coffee, or open an issue and say "
"what you would like to fund. Sponsorship is not required to use "
"OpenCrabs, and every feature stays free for everyone."
)
out.append("")
out.append("---")
out.append("")
out.append(GENERATED_BY)
out.append("")
return "\n".join(out)
def render_readme_block(ledger: dict, built: dict) -> str:
out: list[str] = []
out.append(START_MARK)
out.append("### Community funded")
out.append("")
out.append(
"OpenCrabs has no investors. The people below cover the subscriptions, "
"the inference and the credits, and some of them write the code too. "
f"Totals as of {human_date(built['as_of'].isoformat(), 'day')}."
)
out.append("")
out.append("| Sponsor | Role | Sponsoring since |")
out.append("|---|---|---|")
for sponsor in ledger["sponsors"]:
if not sponsor.get("public", True):
continue
out.append(
f"| {sponsor['name']} | {sponsor['role']} "
f"| {human_date(sponsor['sponsoring_since'], 'day')} |"
)
out.append("")
totals = []
cash_parts = [
f"**{money(total, currency)}**"
for currency, total in sorted(built["cash_totals"].items())
]
if cash_parts:
totals.append(" + ".join(cash_parts) + " in cash")
for currency, total in sorted(built["kind_totals"].items()):
totals.append(f"**{money(total, currency)}** in subscriptions")
for row in built["rows"]["credits"]:
usage = row.get("usage") or {}
if usage.get("estimate_low"):
totals.append(
f"**{tokens(usage['estimate_low'])} to "
f"{tokens(usage['estimate_high'])} tokens** of granted credits"
)
out.append("To date: " + ", ".join(totals) + ".")
out.append("")
out.append("Full ledger: [SPONSORS.md](SPONSORS.md)")
out.append(END_MARK)
return "\n".join(out)
def splice_readme(readme: str, block: str) -> str:
if START_MARK not in readme or END_MARK not in readme:
raise SystemExit(
f"README is missing the sponsor markers. Expected both "
f"{START_MARK} and {END_MARK}."
)
head, rest = readme.split(START_MARK, 1)
_, tail = rest.split(END_MARK, 1)
return head + block + tail
def reference_date(ledger: dict, override: str | None, force_today: bool) -> date:
if override:
return date.fromisoformat(override)
if force_today:
return date.today()
raw = ledger.get("updated")
return date.fromisoformat(raw) if raw else date.today()
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check", action="store_true", help="exit 1 if surfaces are stale")
parser.add_argument("--stdout", action="store_true", help="print, write nothing")
parser.add_argument("--self-test", action="store_true", help="test the math, write nothing")
parser.add_argument("--as-of", help="reference date, YYYY-MM-DD")
parser.add_argument(
"--today",
action="store_true",
help="compute against today instead of the ledger's updated field",
)
args = parser.parse_args()
if args.self_test:
return self_test()
ledger = load_ledger()
validate(ledger)
as_of = reference_date(ledger, args.as_of, args.today)
built = build(ledger, as_of)
sponsors_md = render_sponsors_md(ledger, built)
readme_new = splice_readme(README.read_text(), render_readme_block(ledger, built))
if args.stdout:
print(sponsors_md)
print("\n--- README block ---\n")
print(render_readme_block(ledger, built))
return 0
if args.check:
stale = []
if not SPONSORS_MD.exists() or SPONSORS_MD.read_text() != sponsors_md:
stale.append("SPONSORS.md")
if README.read_text() != readme_new:
stale.append("README.md sponsor block")
if stale:
print("STALE: " + ", ".join(stale))
print("Run: python3 scripts/sync-sponsors.py")
return 1
print(f"OK: sponsor surfaces match sponsors.json (as of {as_of})")
return 0
SPONSORS_MD.write_text(sponsors_md)
README.write_text(readme_new)
print(f"Wrote SPONSORS.md and the README sponsor block (as of {as_of})")
age = (date.today() - as_of).days
if age > 35:
print(
f"note: the ledger reference date {as_of} is {age} days old. "
"Run with --today and bump sponsors.json's updated field to roll the "
"running totals forward."
)
return 0
if __name__ == "__main__":
sys.exit(main())