Files
member-portal/CloudronManifest.json
T
inference-bot 70ae435867 metrics: daily snapshot of members and usage, backfillable, plus a read route
The co-op had no longitudinal data: every number was an ad-hoc query, so
nothing compounded and no single source owned "how many members do we have,
and when did that change". That cost us a wrong figure in the newsletter ("six
new members" — actually 21; the six were forum joins).

New: metrics_snapshot.py writes one row per day into the portal's SQLite store
(the same members.db), and GET /admin/metrics/series returns them oldest-first
for the panel and dashboard to chart later.

  - Cumulative columns (members_active, members_total, members_new,
    newsletter_subs, matrix_space_members) are counts AS OF that day;
    spend_usd/requests are THAT day's traffic.
  - FAIL CLOSED PER METRIC: an unreadable source yields NULL, never a zero — a
    zero reads as "nobody" and would bake a lie into a chart. Failures print to
    stdout, and a scheduler task's stdout lands in the app log.
  - Listmonk's cached subscriber_count is NOT trusted (it reads 142 for a list
    holding 71): the count comes from paging the subscribers endpoint.
  - Member growth comes from welcome_sent_at, the provisioning timestamp —
    the closest thing the portal stores to a join date. Manual/founder members
    have no welcome email, so backfilled days are a documented LOWER BOUND;
    today's row carries the exact count.
  - Runs as an in-app scheduler task (addons.scheduler, 03:15 UTC) rather than
    an agent cron: member-facing plumbing should not depend on the agent VM.
  - Backfill is idempotent; member growth is reconstructible back to
    September and spend/requests back to the gateway's first request.

Charts land later, on the surfaces, once the shape of the series is reviewed.
2026-10-28 03:52:49 +00:00

36 lines
1.0 KiB
JSON

{
"id": "coop.inference.member-portal",
"title": "Member Portal",
"author": "Inference Cooperative",
"description": "Membership middleware: syncs Open Collective members with Cloudron users and LiteLLM keys, and injects per-member keys into inference requests.",
"version": "0.1.0",
"healthCheckPath": "/health",
"httpPort": 8000,
"addons": {
"localstorage": {},
"oidc": {
"loginRedirectUri": "/",
"logoutRedirectUri": "/"
},
"sendmail": {},
"scheduler": {
"membership_sweep": {
"schedule": "0 3 * * *",
"command": "/app/code/sweep.sh"
},
"rag_refresh": {
"schedule": "30 3 * * *",
"command": "/app/code/rag_refresh.py"
},
"metrics_snapshot": {
"schedule": "15 3 * * *",
"command": "/app/code/metrics_snapshot.py"
}
}
},
"manifestVersion": 2,
"website": "https://inference.coop",
"contactEmail": "info@inference.coop",
"dockerImage": "registry.inference.coop/co-op/member-portal:20261027-452a27a"
}