Run a site
Account lifecycle
What can happen to a person's account or a group: disabling, archiving, deleting and purging. It covers who may do each, what each removes, and how a super administrator clears out accounts nobody uses.
Its routes are in the API reference: actor-service for a profile, and the accounts list in auth-service.
The four states
| What it does | Undone by | Who may | |
|---|---|---|---|
| Disable | The account cannot be used or seen. Nothing is removed | Enabling it again | Site administrators |
| Archive | The account stops being live: it cannot sign in or post, and it is no longer suggested to anybody. Everything it posted stays readable at the same addresses | Nothing. It is permanent | The person themselves, a group's administrators, or a super administrator |
| Delete | The account disappears at once and is purged 30 days later | Restoring it within the 30 days | The person themselves, a group's administrators, or a site administrator |
| Purge | Everything the account owned or wrote is removed for good | Nothing | Super administrators, or the 30 days running out |
Deleting, archiving and purging ask the person doing it to prove who they are again first, with their password or a passkey.
Nobody can disable, archive, delete or purge the only super administrator. The answer is 409, and says to make somebody else a super administrator first (see Account types).
Deleting and the 30 days
Thirty days unless the installation says otherwise, in ACCOUNT_GRACE_DAYS
(Account cleanup); the web app says the
site's number.
A deleted account is hidden immediately and its sessions are ended. Nothing it owns is touched, so restoring it brings it back whole.
Hidden, except from those with an edge to it (the user, 2026-10-18): its
followers and its administrators still find it in their lists of who they
follow and what they look after, and on its profile, labelled Scheduled for
deletion with the days left, so an administrator can get back to it and
restore it. Super administrators see every one: in those lists too, and all
together, soonest first, on the Scheduled for deletion tab of the web
app's administration (GET /accounts?state=deleted for each kind: person,
group, event, website). Anybody else no longer finds it. Once purged it is gone from every
list. A disabled account leaves every list until it is enabled again; an
archived one stays.
- A person restores their account by signing in within 30 days. They are emailed when the deletion is scheduled, with the date it becomes final.
- A group cannot sign in, so one of its administrators restores it from the group's settings. The group's administrators are emailed when it is deleted and when it is restored: each whose own account is enabled, but not the one who did it.
After 30 days the sweeper purges it.
What a purge removes
Everything the account owns, everything it wrote, and everything underneath either:
- its posts, wherever they are: on its own profile, in a group, or on somebody else's profile;
- its replies to other people's posts, and whatever was said under those replies;
- other people's replies to its posts, which go with the posts;
- every like, follow, subscription, block and administrator role it had;
- every notification sent to it, or sent to others about it or its posts;
- its uploaded avatar, cover and photos, from object storage;
- its sign-in details, sessions, passkeys and sign-in history;
- the invitations it sent that nobody used, and the signup request it began as, where signing up needed approval;
- the abuse reports it filed.
The numbers other people see are counted again afterwards: the follower and following counts of everybody it was connected to, and the like, reply and subscriber counts on posts and replies that stay.
The confirmation shown before a deletion counts this same set, so the figures there are what will go.
What a purge leaves
- A tombstone. The account's row stays, with the handle and nothing else. It is what keeps the handle from being registered again by somebody who would then inherit the old account's mentions and followers.
- What the account started but does not own: groups, hashtags and locations it created. Other people are in the group and other posts carry the tag.
- The audit trail: who deleted or purged what, and when
(
node_deletion_logs), and the purge request, if a super administrator asked for it (purge_requests). - Abuse cases about it, closed as
actioned,deleted.
Purging now
A super administrator can remove accounts without the 30 days. This is for clearing out accounts nobody uses, not for moderation: for that, disable the account or decide its abuse report.
| Endpoint | What it does |
|---|---|
POST /people/purge-batch |
Removes up to 500 people |
POST /groups/purge-batch |
Removes up to 500 groups |
DELETE /people/:id/purge, DELETE /groups/:id/purge |
Removes one |
GET /people/purge-batch/:batch_id, GET /groups/purge-batch/:batch_id |
How far a request has got |
The body of a batch request names the accounts and repeats their number:
{ "ids": [41, 42, 57], "confirm": "PURGE 3" }
| Answer | Meaning |
|---|---|
| 202 | Queued. The body has the batch_id to ask about, and how many were queued and skipped |
| 200 | Nothing to do: every account named was already purged or queued |
400 confirmation_mismatch |
confirm does not name the number of ids |
403 step_up_required |
Prove who you are first |
409 purge_refused |
One or more of the accounts cannot be purged. Nothing was touched. refused gives the reason for each |
The reasons an account stops a request:
| Reason | Meaning |
|---|---|
not_found |
No such account |
wrong_type |
Not a person, on the people route; not a group, on the groups route |
administrator |
An administrator or super administrator. A batch never removes one: change their account type first (Account types), or remove them by name with the single route |
yourself |
The account asking |
last_super_administrator |
The only super administrator |
A request does not wait for the work. The accounts are hidden before it
answers, and the purge follows in the background, usually within seconds. The
progress endpoint gives total, queued, done and failed, and each
account's state. A purge that fails is tried three times and then marked
failed with the reason; asking again for that account tries again.
Nobody is emailed about a purge.
Removing as spam
A moderator's decision, not a cleanup (roadmap row 102): an account found to be spam is removed at once, as a purge removes it, without the 30 days a deleted account waits, since by the time those run out it may be too late.
| Endpoint | What it does |
|---|---|
POST /people/:id/spam |
Removes a person as spam |
POST /groups/:id/spam |
Removes a group as spam |
- Site administrators and super administrators may, with no proof of identity: the web app asks once more, saying it cannot be undone, and spam is removed often and in a hurry.
- Never an administrator's or a super administrator's account, nor the
viewer's own: refused with 409
purge_refused,administratororyourself, as a batch purge refuses them. - Only a new account, made in the last 30 days (
SPAM_REMOVAL_MAX_DAYS): spam is new, and one here longer is disabled or deleted instead, with the 30 days to bring it back (the user, 2026-10-19). An older one is refused withtoo_old, and the web app offers the action only while it may. - Recorded in
node_deletion_logsasremoved-as-spamwith who decided, when it is asked for; the purge then writes its own rows (purged,hard-deleted). - The answer, and how far it has got, are as for a purge (202 with a
batch_id). - In the web app: Remove as spam in the profile's menu, and on a report's
case, which is then closed as
removed_as_spam(Abuse reports).
The accounts list
GET /accounts lists the site's people or groups for cleanup, least recently
active first. Super administrators only.
| Parameter | Value |
|---|---|
kind |
person (the default) or group |
q |
Part of a name or handle |
tier |
empty, dormant or active |
state |
active, disabled, archived or deleted |
last_active_before |
A date, YYYY-MM-DD |
created_before |
A date, YYYY-MM-DD |
content_max |
At most this many posts and replies. 0 for none |
email_verified=false |
People who never verified their address |
onboarded=false |
People who never finished the welcome steps |
has_admin=false |
Groups nobody administers |
sort, dir |
last_active_at, created_at or name; asc or desc |
limit, offset |
limit is 50 by default and at most 500 |
Each account comes with when it was created and last active, how much it has written, how many follow it, its state and its tier. Purged accounts are not listed.
Last active is the latest of: the last visit, the last sign-in, the last thing written, and the day the account was created. For a group it is the last post in it.
Tiers:
| Tier | Meaning |
|---|---|
empty |
Nothing written, follows nobody and has no followers. For a group: nothing posted in it and at most one follower |
dormant |
Not empty, and last active more than two years ago |
active |
Everything else |
The two years can be changed with ACCOUNT_DORMANT_DAYS; see
Configuration. A tier only sorts accounts
for somebody to look at. Nothing is removed because of one.
The sweeper
The account-sweeper job does the purging that is nobody's request: every
account whose 30 days have run out. It also finishes any requested purge that
was interrupted.
- On Kubernetes it is a CronJob, every ten minutes, and never two at once.
- With docker-compose it is not started by
make up. Run it withdocker compose run --rm account-sweeper.
A purge works through an account in batches of 500 posts, and keeps a list of
what is left (purge_work). If the job is stopped part-way, the next run
carries on from that list. An account of any size finishes.
Uploaded files are removed only where FILE_SERVICE_ADDR is set, for both the
sweeper and graph-grpc-service. Without it the database is cleared and the
files stay in storage.
auth-service and abuse-report-service learn of a purge through an event. If that event is lost, the sign-in details or the filed reports of that account stay until somebody removes them by hand; nothing retries.
Groups left without an administrator
Purging a person who was a group's only administrator leaves the group in
place with nobody running it. Find these with
GET /accounts?kind=group&has_admin=false.
A site administrator can then appoint one of its followers with
POST /groups/:id/admins/, giving that person's id as admin_id.
Where the code is
| What | Where |
|---|---|
| Disabling, archiving, deleting and restoring | actor-service/cmd/api/handlers/person.go (SetEnabled, Archive, Delete), group.go (Archive, Restore); in the graph, graph-grpc-service/data/repositories/nodes_archive.go (ArchiveActor, SetEnabled) and nodes_soft_delete.go (SoftDeleteActor, RestoreActor) |
| Proving who you are again | actor-service/cmd/api/handlers/step_up.go (requireStepUp) |
| The only super administrator | graph-grpc-service/data/repositories/nodes_super_admin.go (GuardLastSuperAdmin) |
| What a deleted person is emailed, and their sign-in details removed on a purge | auth-service/cmd/api/handlers/account_deleted.go (handlePurged), auth-service/cmd/api/notifiers/account_delete.go |
| What a purge removes and leaves | graph-grpc-service/purge/purge.go (Account), graph-grpc-service/data/repositories/nodes_purge_steps.go, nodes_purge.go (TombstoneActor), nodes_deletion_counts.go (ReadDeletionCounts), tables purge_work and node_deletion_logs. Checked by purge_integration_test.go |
| Purging now | actor-service/cmd/api/handlers/person_purge.go, group_purge.go; graph-grpc-service/data/repositories/purge_requests.go (PurgeRequestMaxAttempts), table purge_requests |
| The filed reports of a purged person | abuse-report-service/cmd/api/handlers/events.go |
| The accounts list | auth-service/cmd/api/handlers/accounts.go, auth-service/data/repositories/accounts.go; auth-service/config/bootstrap.go (GetAccountDormantDays), setting ACCOUNT_DORMANT_DAYS |
| The sweeper | graph-grpc-service/sweeper/sweeper.go (Run; the days are anahita-libs/constants AccountGraceDays()), graph-grpc-service/cmd/sweeper/main.go, k8s/base/jobs/sweeper-cronjob.yml, account-sweeper in docker-compose.yml; settings FILE_SERVICE_ADDR and ACCOUNT_GRACE_DAYS |
From anahitasocial/anahita-services, 2026-10-27.
Written by Claude, reviewed by Rastin Mehr · About this