Skip to main content

User and tenant lifecycle

This page covers what happens — and when — across the two lifecycles that matter most: the people you put on your org, and the org itself. Both lifecycles guarantee destruction is irreversible only when it must be (post-retention-window); every step before that is reversible.

User lifecycle

A user only exists in fremforge as a member of one or more orgs. Removing them from an org is a per-tenant action; deactivating their entire account only happens when they’re removed from their last org.

Removal from a single org

You remove a member from your org either manually (the Seat-management section in Billing lists every member with last-activity, supports bulk-remove, and offers an opt-in auto-cleanup policy) or via SCIM deprovisioning from your IdP. The behaviour is the same either way:

  1. The Forgejo org_member.removed event fires.
  2. fremforge recomputes seats_in_use from Forgejo’s live member list and emits a tenant.seat.released audit event. The freed slot becomes immediately available to your next invite or SSO-JIT sign-in.
  3. Any org-scoped PATs the leaving user holds for this tenant are revoked.
  4. If this was their last org, the orphan-deactivation flow below kicks in.

Your contracted seat cap is never reduced by cleanup. seats_in_use drops; the cap stays whole. To reduce the cap, use the Billing page (seat reductions take effect at the next renewal — annual prepayment is non-refundable per terms §4.7).

Auto-cleanup of stale accounts

The Members page lets an Owner set an inactivity threshold (default: disabled). Once set, a daily job removes members whose Forgejo last_login is older than the threshold. Owners, Operator accounts, and Bot accounts are never auto-removed; a per-tenant floor (default 30 days) prevents accidentally kicking members on long absences. The daily job is capped at 50 removals per tenant per day — a safety brake so a misconfigured policy can’t wipe a whole org in one tick.

Orphan deactivation (last org)

When the per-tenant removal above leaves a user with zero org memberships across all tenants, fremforge automatically:

  1. Sets prohibit_login=true on the Forgejo user (no more sign-in via SSO or local credentials).
  2. Revokes every Forgejo PAT the user owns, anywhere.
  3. Archives — does NOT delete — any personal-namespace repositories the user owns. Archive preserves history; the customer can restore by re-inviting before the 14-day hard-delete window closes.
  4. Records the deactivation in the orphaned_users book with cleanup_due_at = today + 14 days.

Re-inviting the user to any org within the 14-day window restores them: prohibit_login is lifted, PATs can be re-minted, and the orphan tombstone gets a restored_at stamp so the hard-delete job skips it.

Hard delete (orphan +14 days)

A daily job hard-deletes the Forgejo accounts that have been orphan-deactivated for more than 14 days and were not re-invited in that window. Fourteen days keeps the whole deletion, including the storage copies that are kept for 7 days after a delete, inside the 30 days the privacy notice §6 promises for account data. The Forgejo DELETE /api/v1/admin/users/{u} call removes the user account; the user’s archived personal repositories are removed transitively. The audit log retains a forgejo.user.orphan_hard_deleted event tied to the originally-triggering tenant for traceability.

After hard-delete, the username + account history are gone. There is no recovery beyond the 14-day window — provision a fresh user from your IdP if needed.

Tenant lifecycle

A tenant transitions through five states. The state machine is monotonic except for the cancellation_scheduled → active undo path and suspended → active reactivation path.

StateTriggerWhat it means
trialSignupFirst 30 days, no card required. Trial-suspension job moves to suspended if not converted by trial_expires_at.
activePaid plan startsNormal operation.
suspendedDunning past T+14, or trial expiredWrite access is paused; read + export + reactivate still work.
cancellation_scheduledCustomer initiated cancelService continues until cancel_effective_at. Undo lifts back to active any time before that date.
cancelledcancel_effective_at reachedForgejo org goes read-only. 90-day export + reactivate window.
deletedcancel_effective_at + 90dForgejo org and PII permanently destroyed. Certificate of destruction issued.

Cancellation initiated

When you click “Cancel” on the Billing page:

  • The status moves to cancellation_scheduled and cancel_effective_at is set (monthly term: +30 days; annual term: the prepaid commitment end-date — annual is non-refundable per terms §4.7).
  • Any in-flight overage is settled to the active payment method.
  • The Mollie SEPA mandate is revoked so no further automatic charges fire.
  • A confirmation email lands at every recipient in billing_emails. The Billing page now shows a countdown banner with the read-only-ends date, an export-data CTA, and an undo button.

The undo button is available right up until cancel_effective_at — and again during the 90-day read-only window via the reactivate button (a fresh payment method is set up through the standard checkout).

Cancellation effective (+0 days)

At cancel_effective_at the daily cancellation-effect job flips the status to cancelled and archives every repository in the Forgejo org (read-only). The org itself stays, the data stays — write access is paused everywhere except Billing (for reactivation) and Data export (for portability).

Permanent deletion (+90 days)

Ninety days after the cancellation took effect, the same daily job performs the physical erasure. Gates the deletion will respect — and any one of them blocks the purge for that day:

  • legal_hold = true on the tenant (an operator-set preservation flag for legal-hold preservation orders).
  • Any export job still queued or running.
  • Any in-flight payment (Mollie webhook status open, pending, or authorized).

When all gates clear, fremforge:

  1. Emits the tenant.physically_deleted audit event (recorded while the tenant scope is still valid so the audit chain captures the action).
  2. Revokes the per-tenant Renovate bot account and revokes its PAT.
  3. Purges any queued webhook-delivery jobs for the tenant.
  4. Deletes the Forgejo org — repositories, LFS objects, and the org’s full state cascade.
  5. Deletes the Mollie customer record.
  6. Erases the tenant’s PII (display name, billing contact email, billing emails, Mollie/Dinero references, VAT number) and marks the tenant deleted.
  7. Generates the certificate of destruction PDF (see below) and archives it in OBS under the legal-hold-protected bucket. Emails the certificate to the captured billing-contact address.

Certificate of destruction

A one-page PDF generated at physical-delete time and emailed to the billing contact. It records:

  • The org slug, display name, VAT number, and tenant identifier.
  • The cancellation-effective date and the permanent-deletion date.
  • The categories of data destroyed (Forgejo content, tenant configuration, scoped member accounts, audit payloads, billing PII).
  • The residual retention carve-outs that still apply (see below).
  • A SHA-256 anchor hash that pairs the certificate with the tenant.physically_deleted audit event for cross-verification.

If you need a re-issued copy later, the archive lives in a WORM-locked OBS bucket — email compliance@frem.sh and we’ll re-issue from the archive.

Retention windows (DPA §9)

Time from cancellationWhat happens
+0 days (cancel_effective_at)Status → cancelled. Forgejo org archived, read-only. Export + reactivate available.
+90 daysStatus → deleted. Forgejo org physically deleted; its files (LFS, attachments, packages, avatars) stay as old versions in object storage for 7 more days and are gone by about +102 days, inside the 30 days DPA §9 allows. PII nulled. Mollie customer removed. Certificate of destruction issued. The same night, the personal data in the tenant’s audit events is pseudonymised (actor and payload erased; the hash chain stays verifiable). If a deletion gate holds the erasure back (an export or payment in flight, legal hold) or it fails, it is retried nightly, and the pseudonymisation follows the night the erasure completes.
+150 days (until 2026-11-03)Until 2026-11-03: backups containing the tenant are purged by +150 days (DPA 1.39 §9).
up to +210 days (from 2026-11-03)From 2026-11-03 (DPA 1.41 §9): backups are not edited per tenant, so deleted data stays in the encrypted backups until they expire, up to 90 days after deletion and by +210 days from cancellation at the latest.

Residual carve-outs

Some categories of data have statutory retention that survives the +90d primary deletion. These are documented in DPA §9 and reflected on the certificate of destruction:

  • Accounting records (invoices, voucher attachments): retained 5 years per Bogføringsloven §10.
  • Audit log metadata: retained 3 years, pseudonymised at deletion (+90 days, or the night a delayed erasure completes); no repository content, no PII.
  • WORM-locked compliance objects (audit-chain anchors, the certificate-of-destruction PDF itself): retained until the lock period expires. Documented carve-out in DPA Annex §9.

Legal hold

Operators can set legal_hold = true on a tenant in response to a preservation order (subpoena, GDPR investigation, dispute). While set, the cancellation-effect job skips the tenant entirely — no physical delete, no PII null-out, no backup purge. The flag is operator-only; customers cannot toggle it.

The hold also suspends deletion of every evidence retention class while the organisation is live: audit payload redaction, access log retention, resolved findings, SBOMs, attestations, export records and the clearing of closed pull requests’ findings (since 2026-10-08; before that, audit payload redaction and SBOM pruning did not check the hold).

Common questions

My seat shows used even though the member left. Forgejo’s member list filters out users with prohibit_login=true, so a deactivated last-org user doesn’t count against your seats_in_use. If your number looks stale, the nightly seat-cap-drift-check job recomputes it.

Will downgrading the seat cap refund me? No — seat reductions take effect at the next billing cycle (monthly) or annual renewal (annual). Annual prepayment is non-refundable per terms §4.7. See Billing for the seat-cap form.

Can I get my data after the 90-day window? No. By the time you read this, it’s already gone (Forgejo org, repos, LFS, members, PII). Copies in the encrypted backups are gone by +150 days until 2026-11-03; from 2026-11-03 (DPA 1.41) they expire with the snapshots, by +210 days at the latest. Plan exports during the read-only window — the Billing page surfaces an export CTA exactly for this reason.

Does GDPR Article 17 erasure happen at cancellation or at +90 days? The +90-day mark is when primary storage is wiped. Backups follow: until 2026-11-03 they are purged by +150 days from cancellation; from 2026-11-03 (DPA 1.41 §9) deleted data expires with the encrypted snapshots, up to 90 days after deletion and by +210 days at the latest. The certificate of destruction is the artifact you can show your regulator.

What if I cancel by mistake? Click the undo button on the Billing page — it works the full grace window (until cancel_effective_at). After that, the read-only window gives you 90 more days to reactivate via the standard checkout. After +90d there is no recovery.