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:
- The Forgejo
org_member.removedevent fires. - fremforge recomputes
seats_in_usefrom Forgejo’s live member list and emits atenant.seat.releasedaudit event. The freed slot becomes immediately available to your next invite or SSO-JIT sign-in. - Any org-scoped PATs the leaving user holds for this tenant are revoked.
- 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:
- Sets
prohibit_login=trueon the Forgejo user (no more sign-in via SSO or local credentials). - Revokes every Forgejo PAT the user owns, anywhere.
- Archives — does NOT delete — any personal-namespace repositories the user owns. Archive preserves history; the customer can restore by re-inviting before the 90-day hard-delete window closes.
- Records the deactivation in the
orphaned_usersbook withcleanup_due_at = today + 90 days.
Re-inviting the user to any org within the 90-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 +90 days)
A daily job hard-deletes the Forgejo accounts that have been orphan-deactivated for more than 90 days and were not re-invited in that window. 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 90-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.
| State | Trigger | What it means |
|---|---|---|
trial | Signup | First 30 days, no card required. Trial-suspension job moves to suspended if not converted by trial_expires_at. |
active | Paid plan starts | Normal operation. |
suspended | Dunning past T+14, or trial expired | Write access is paused; read + export + reactivate still work. |
cancellation_scheduled | Customer initiated cancel | Service continues until cancel_effective_at. Undo lifts back to active any time before that date. |
cancelled | cancel_effective_at reached | Forgejo org goes read-only. 60-day export + reactivate window. |
deleted | cancel_effective_at + 60d | Forgejo org and PII permanently destroyed. Certificate of destruction issued. |
Cancellation initiated
When you click “Cancel” on the Billing page:
- The status moves to
cancellation_scheduledandcancel_effective_atis 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 60-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 (+60 days)
Sixty 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 = trueon 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, orauthorized).
When all gates clear, fremforge:
- Emits the
tenant.physically_deletedaudit event (recorded while the tenant scope is still valid so the audit chain captures the action). - Revokes the per-tenant Renovate bot account and revokes its PAT.
- Purges any queued webhook-delivery jobs for the tenant.
- Deletes the Forgejo org — repositories, LFS objects, and the org’s full state cascade.
- Deletes the Mollie customer record.
- Erases the tenant’s PII (display name, billing contact email, billing emails, Mollie/Dinero references, VAT number) and marks the tenant deleted.
- 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_deletedaudit 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 cancellation | What happens |
|---|---|
| +0 days (cancel_effective_at) | Status → cancelled. Forgejo org archived, read-only. Export + reactivate available. |
| +60 days | Status → deleted. Forgejo org physically deleted. PII nulled. Mollie customer removed. Certificate of destruction issued. |
| +90 days | Audit-event PII pseudonymisation runs against rows where deleted_at is at least 90 days old. |
| +120 days | Backups containing the tenant are purged. Total termination-to-backup-clear: 120 days. |
Residual carve-outs
Some categories of data have statutory retention that survives the +60d 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 +90d; 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.
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 60-day window? No. By the time you read this, it’s already gone (Forgejo org, repos, LFS, members, PII). Backups containing it are purged at +120 days. 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 +60 days? The +60-day mark is when primary storage is wiped. Backups follow within another 30 days (+90 total from deleted_at, or +120 from cancellation). 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 60 more days to reactivate via the standard checkout. After +60d there is no recovery.