Storage add-on: per-user 10 GB increments via Kanidm attribute + per-user quota #185

Öppen
öppnade 2026-07-21 18:17:04 +00:00 av supernaut · 1 kommentar
Ägare

Follow-up to #184 (LFS included / single shared 10 GB paid quota). The paid storage add-on was deferred there — this issue is its design + build.

Requirement

Paying users can buy unlimited extra storage in 10 GB increments (not a single fixed add-on).

Why it can't be groups

Forgejo assigns quota via group membership only (no per-user rule assignment) and merges overlapping rules most-permissively (the reason #125 needed the two-rule split). So "base + N×10 GB" cannot be N stacked add-on groups that sum. The only Forgejo-native way to give each user an arbitrary total is a per-user group + per-user size:all rule whose limit the reconciler computes = 10 GiB × (1 + increments).

Design

  • Increment count = a LIVE Kanidm per-user attribute (never checked-in group_vars — operator decision 2026-07-21). Needs Kanidm schema work (a custom attribute on the person) + the reconciler reading it via GET /v1/person/<user>.
  • Payments-coupled (epic bitborg-docs#5): the payments webhook writes the increment count on purchase/change; admin can set it manually in the interim.
  • Reconciler: for each paid user with increments > 0, ensure a per-user paid-<user> group + size:all rule at the computed limit and assign the user; when the count returns to 0, move them back to the shared paid group and delete the per-user group/rule (self-healing).

Also in scope — Kanidm cleanup

kanidm-provision is additive and never pruned the now-unused groups. Delete from the live instance: ent_lfs, ent_lfs_large, ent_storage (kanidm group delete), and drop them from the provisioning state if still referenced.

Relates: #184; epics bitborg-docs#5 (payments), #26 (pricing enforcement).

Follow-up to #184 (LFS included / single shared 10 GB `paid` quota). The paid **storage add-on** was deferred there — this issue is its design + build. ## Requirement Paying users can buy **unlimited extra storage in 10 GB increments** (not a single fixed add-on). ## Why it can't be groups Forgejo assigns quota via **group membership only** (no per-user rule assignment) and merges overlapping rules **most-permissively** (the reason #125 needed the two-rule split). So "base + N×10 GB" **cannot** be N stacked add-on groups that sum. The only Forgejo-native way to give each user an arbitrary total is a **per-user group + per-user `size:all` rule** whose limit the reconciler computes = `10 GiB × (1 + increments)`. ## Design - **Increment count = a LIVE Kanidm per-user attribute** (never checked-in `group_vars` — operator decision 2026-07-21). Needs Kanidm **schema work** (a custom attribute on the person) + the reconciler reading it via `GET /v1/person/<user>`. - **Payments-coupled** (epic bitborg-docs#5): the payments webhook writes the increment count on purchase/change; admin can set it manually in the interim. - **Reconciler:** for each paid user with increments > 0, ensure a per-user `paid-<user>` group + `size:all` rule at the computed limit and assign the user; when the count returns to 0, move them back to the shared `paid` group and delete the per-user group/rule (self-healing). ## Also in scope — Kanidm cleanup `kanidm-provision` is additive and never pruned the now-unused groups. Delete from the live instance: **`ent_lfs`, `ent_lfs_large`, `ent_storage`** (`kanidm group delete`), and drop them from the provisioning state if still referenced. Relates: #184; epics bitborg-docs#5 (payments), #26 (pricing enforcement).
Upphovsperson
Ägare

The Kanidm-attribute half of this issue is not implementable

Verified while researching identity architecture for ADR 0040.

This issue proposes per-user 10 GB increments "via Kanidm attribute". Kanidm has no admin-facing
schema extension
, so there is no supported way to add a custom per-user attribute:

  • The SCIM schema endpoints (/scim/v1/Class, /scim/v1/Attribute) are registered GET-only.
  • The client library exposes only scim_schema_class_list and scim_schema_attribute_list.
  • The CLI schema verb offers only class list|search and attribute list|search.
  • Schema is compiled in and migrated in-tree. Kanidm 1.11.0 moved it out of the database into
    memory
    , which makes runtime user-defined schema less likely rather than more.

The upstream request is kanidm#3493, open since
March 2025 with no milestone. Its own motivating example is, almost word for word, this use case:
setting up a per-user quota for a cloud service. A second request,
kanidm#256, has been open since 2020. Given that
history, this should not be planned around.

Kanidm also documents entry schema as an unstable API, so even if custom schema were reachable
by some unsupported route, it would not be a safe place for billing-relevant state.

What replaces it

Two options exist. Only one survives contact with the requirement.

Group per increment. OAuth2 claim maps can carry arbitrary operator-chosen values, but they are
keyed group to value, not user to value. So each distinct increment needs its own group
(quota_increment_10gb, quota_increment_20gb, and so on). Values merge correctly when a user is
in several such groups. Workable for a small fixed set. It does not scale to an arbitrary per-user
number, and it puts commercial state in the identity provider, which ADR 0040 has since decided
against.

Hold the number in the billing service. ADR 0040 moves entitlement authority out of the
identity provider. Under that decision a per-user storage increment is an integer column, and the
reconciler projects it onto a per-user Forgejo quota rule exactly as this issue describes. No
Kanidm attribute is required and no group-per-value proliferation happens.

The second is the direction ADR 0040 sets, and it is recorded there as a consequence.

Status of this issue

Leaving open. The capability is still wanted and the Forgejo half of this issue is unchanged: a
per-user quota rule is still the enforcement mechanism.

What needs rewriting when this is picked up is the source of the number. The title and body should
drop "via Kanidm attribute". Cross-repo sequencing is tracked separately.

## The Kanidm-attribute half of this issue is not implementable Verified while researching identity architecture for ADR 0040. This issue proposes per-user 10 GB increments "via Kanidm attribute". Kanidm has **no admin-facing schema extension**, so there is no supported way to add a custom per-user attribute: - The SCIM schema endpoints (`/scim/v1/Class`, `/scim/v1/Attribute`) are registered **GET-only**. - The client library exposes only `scim_schema_class_list` and `scim_schema_attribute_list`. - The CLI `schema` verb offers only `class list|search` and `attribute list|search`. - Schema is compiled in and migrated in-tree. Kanidm 1.11.0 moved it **out of the database into memory**, which makes runtime user-defined schema less likely rather than more. The upstream request is [kanidm#3493](https://github.com/kanidm/kanidm/issues/3493), open since March 2025 with no milestone. Its own motivating example is, almost word for word, this use case: setting up a per-user quota for a cloud service. A second request, [kanidm#256](https://github.com/kanidm/kanidm/issues/256), has been open since 2020. Given that history, this should not be planned around. Kanidm also documents entry schema as an **unstable API**, so even if custom schema were reachable by some unsupported route, it would not be a safe place for billing-relevant state. ## What replaces it Two options exist. Only one survives contact with the requirement. **Group per increment.** OAuth2 claim maps can carry arbitrary operator-chosen values, but they are keyed **group to value, not user to value**. So each distinct increment needs its own group (`quota_increment_10gb`, `quota_increment_20gb`, and so on). Values merge correctly when a user is in several such groups. Workable for a small fixed set. It does not scale to an arbitrary per-user number, and it puts commercial state in the identity provider, which ADR 0040 has since decided against. **Hold the number in the billing service.** ADR 0040 moves entitlement authority out of the identity provider. Under that decision a per-user storage increment is an integer column, and the reconciler projects it onto a per-user Forgejo quota rule exactly as this issue describes. No Kanidm attribute is required and no group-per-value proliferation happens. The second is the direction ADR 0040 sets, and it is recorded there as a consequence. ## Status of this issue Leaving open. The capability is still wanted and the **Forgejo half of this issue is unchanged**: a per-user quota rule is still the enforcement mechanism. What needs rewriting when this is picked up is the source of the number. The title and body should drop "via Kanidm attribute". Cross-repo sequencing is tracked separately.
Logga in för att delta i denna konversation.
Ingen milstolpe
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-infra#185
Ingen beskrivning angiven.