Sustainability Metrics

Bead: beads-hub-n56 | Date: 2026-02-20 | Author: PltOps

LOOPY Model Nodes & Metrics

Each node from the LOOPY sustainability model maps to concrete, trackable metrics.

Node Definitions

#NodeMetricsCollection MethodFrequency
1Donation VolumeTotal € donated, donor count, avg donation sizeOpen Collective API, GitHub Sponsors API, bank statementsMonthly
2Compute SpendTotal € infra cost, cost per service, cost per userCloud billing APIs, invoicesMonthly
3Active UsersMAU, DAU, session count, retention rateApplication logs, auth provider statsMonthly (MAU), Weekly (WAU)
4Community ContributorsUnique contributors/month, new contributors, returning contributorsGit log analysis (git shortlog), Codeberg/GitHub APIMonthly
5PR CountPRs opened, merged, closed, avg time-to-mergeCodeberg/GitHub API (/repos/{owner}/{repo}/pulls)Monthly
6Ops Drag (B2)Toil hours, incident count, manual deployment countTime tracking, incident log, deployment logMonthly
7Community EngagementForum posts, chat messages, event attendanceSignal/Discord message counts, event logsMonthly
8Project VelocityIssues closed, story points completed, release frequencyBeads-hub stats (bd CLI), git tagsMonthly

Metric Details

1. Donation Volume

donation_total_eur = sum(all donations in period)
donor_count = count(distinct donors in period)
avg_donation = donation_total_eur / donor_count
donation_growth_rate = (this_month - last_month) / last_month

2. Compute Spend

compute_total_eur = sum(all infra invoices in period)
cost_per_user = compute_total_eur / active_users
cost_per_service = compute_total_eur / service_count

3. Active Users

mau = count(distinct users with activity in 30d window)
retention = returning_users / previous_month_users
churn = 1 - retention

4. Community Contributors

contributors = count(distinct git authors in period)
new_contributors = contributors NOT IN previous_period_contributors
bus_factor = min N contributors covering 50% of commits

5. PR Count

prs_opened = count(PRs created in period)
prs_merged = count(PRs merged in period)
avg_ttm = mean(merge_date - open_date) for merged PRs
review_turnaround = mean(first_review_date - open_date)

Dashboard Specification

Recommended tool: Grafana dashboard or static markdown report (start simple).

Dashboard Layout

PanelTypeData
Sustainability Ratio (SR)GaugeCurrent SR with color thresholds
SR TrendLine chartSR over last 12 months
Revenue vs CostStacked barMonthly donations vs compute spend
Active UsersLine chartMAU over time
ContributorsBar chartMonthly unique contributors
PR VelocityLine chartPRs merged/month, avg TTM
Cost per UserLine chartTrend over time

Data Collection Script (Skeleton)

#!/bin/bash
# collect-sustainability-metrics.sh
# Run monthly, outputs JSON for dashboard ingestion

MONTH=${1:-$(date +%Y-%m)}
OUTPUT="metrics/${MONTH}.json"

# Donations (manual input or API)
DONATIONS_EUR=${DONATIONS:-0}

# Compute cost (manual input or billing API)
COMPUTE_EUR=${COMPUTE:-0}

# Active users (from logs)
ACTIVE_USERS=$(grep -c "unique-session" /var/log/app/${MONTH}*.log 2>/dev/null || echo 0)

# Contributors (from git)
CONTRIBUTORS=$(cd /path/to/repos && git shortlog -sn --since="${MONTH}-01" --until="${MONTH}-31" | wc -l)

# PRs (from API)
PRS_MERGED=$(curl -s "https://codeberg.org/api/v1/repos/ORG/REPO/pulls?state=closed&sort=updated&limit=50" | jq "[.[] | select(.merged)] | length")

cat > "$OUTPUT" <<EOF
{
  "month": "${MONTH}",
  "donations_eur": ${DONATIONS_EUR},
  "compute_eur": ${COMPUTE_EUR},
  "active_users": ${ACTIVE_USERS},
  "contributors": ${CONTRIBUTORS},
  "prs_merged": ${PRS_MERGED},
  "sustainability_ratio": $(echo "scale=2; ${DONATIONS_EUR} / ${ACTIVE_USERS} / (${COMPUTE_EUR} / ${ACTIVE_USERS})" | bc 2>/dev/null || echo 0)
}
EOF

echo "Metrics written to ${OUTPUT}"

Implementation Phases

  1. Phase 1 (Now): Manual monthly data collection into markdown report (see sustainability-ratio.md template)
  2. Phase 2 (Month 2): Automate git-based metrics (contributors, PRs) via script
  3. Phase 3 (Month 3): Connect billing APIs for compute cost automation
  4. Phase 4 (Month 4+): Grafana dashboard with automated data pipeline