# Design note — data protection

**Dated 2026-08-05. Written before the code, per the provenance practice in
[ROADMAP.md](../../ROADMAP.md#proving-independence).**

> Not legal advice. This is a working note about which mechanisms Convoro
> provides and why. Anybody running a community that processes EU or UK
> personal data should get advice about their own obligations.

---

## Decision 0 — Convoro is not the controller, and that shapes everything

The **operator** of a Convoro forum is the data controller. They decide why
personal data is processed and on what lawful basis; they owe the privacy
notice, the records of processing, the breach process and the answers to
requests.

Convoro is the software they use to do it.

That means the goal is not "Convoro is compliant" — software cannot be. It is
**"an operator using Convoro can discharge their obligations without writing
SQL"**. Every decision below follows from that:

- Anything a member can ask for, an **administrator must be able to do on their
  behalf**, because requests arrive by email and have to be answered within a
  month.
- Anything Convoro stores must be **discoverable**, or nobody can honestly
  answer "what do you hold about me".
- Defaults are the **conservative** ones, because most operators will never
  change them.

## Decision 1 — the strongest position is not collecting it

The audit before writing any of this found the whole personal-data footprint to
be: an email address, a username, whatever somebody puts in their own profile,
their content, and **one** IP column on password resets.

No per-request logging, no user agents, no analytics, no third-party
beacons, no tracking of what anybody reads beyond the read marks they benefit
from.

That is data protection by design (Art. 25) achieved by not building the
alternative, and it is worth protecting deliberately rather than by accident.
**A new column holding personal data should be as hard to add thoughtlessly as
a new cookie.**

## Decision 2 — access and portability are the same export, in two shapes

Art. 15 (access) wants "what do you hold about me" in an intelligible form.
Art. 20 (portability) wants the data somebody **provided**, in a structured,
machine-readable format.

One export satisfies both if it ships two files: **JSON** for portability, and
**HTML** for a person who wants to read it.

It covers everything keyed to that account — profile, posts, topics, private
messages, reactions, follows, notifications, read state, tokens, name history,
consent record. It deliberately does **not** include other people's content,
even in threads they shared, because that is somebody else's personal data and
handing it over would be a disclosure, not a subject access response.

Generated on request, and **queued rather than built inside the request**: a
member with ten thousand posts must not time out, and the file must not be
built twice because somebody clicked twice.

## Decision 3 — erasure anonymises, and destroying threads is not the default

Art. 17 is a right to have **personal data** erased. It is not a right to
withdraw a conversation other people took part in — and Art. 17(3) exempts
processing needed for freedom of expression and information, which a discussion
archive plainly is.

So deleting an account:

- **Erases the personal data**: email, avatar, signature, title, timezone,
  2FA secrets, recovery codes, tokens, sessions, notifications, read state,
  follows, the IP on any password reset.
- **Keeps the content, unattributed**: posts and topics remain, authored by a
  tombstone.
- **Keeps one tombstone per person**, not one shared "Deleted" account, because
  merging distinct people makes old threads unreadable — "these three replies
  are by the same person" is information the conversation depends on.

There is **no undo**, and the interface says so before it happens rather than
after. A grace period sounds kind, but "we still have your data, we just hid
it" is not erasure.

Somebody who wants their posts gone as well can ask an administrator, who has
that option. It is not on the self-service path because it destroys other
people's threads and is the single most-regretted action in any forum.

## Decision 4 — consent has to be demonstrable, so it is recorded

Art. 7(1): the controller must be able to **demonstrate** that consent was
given. "They must have ticked the box" is not a record.

Registration records **which version** of the terms and privacy notice was
agreed to, and **when**. Version, not just a timestamp — an operator who
updates their privacy notice needs to know who agreed to what.

The record survives account deletion in the same anonymised form as everything
else: it proves an account consented without saying who they were.

## Decision 5 — no cookie banner, because there is nothing to consent to

Convoro sets three cookies: the session, the remember-me token, and the theme
choice. All three are either strictly necessary or set only because somebody
explicitly asked for them.

Under ePrivacy, strictly-necessary cookies do not require consent. Adding a
banner would be theatre, and banner fatigue is itself a harm.

**This is only true while it stays true.** A test asserts the cookie inventory,
so adding a fourth cookie fails the build and forces the question rather than
quietly changing the site's legal position.

## Decision 6 — storage limitation is a command, not a promise

Art. 5(1)(e): personal data kept no longer than necessary. Things that
accumulate and are pruned on a schedule:

| What | Kept | Why |
|---|---|---|
| Password reset tokens (carry an IP) | 7 days | expired ones are useless |
| Sent mail | 7 days | already; enough to answer a delivery question |
| Read marks | 30 days | already; older ones cannot change an answer |
| Released usernames | 90 days | already; the archive has settled |
| Data exports | 7 days | it is a copy of everything, sitting on disk |
| Sessions | session lifetime | already, by the session handler |

`php tools/convoro privacy:prune`, run from cron. A retention policy nothing
enforces is a paragraph in a document.

## Decision 7 — an age question, because the alternative is worse

Art. 8 sets a digital-consent age between 13 and 16 depending on the member
state. Convoro cannot know which applies, so the operator sets it and
registration asks.

A date of birth would be more data than the question needs. Registration asks
for **confirmation**, not a birthday, and stores only that it was given.

## What this does not do

- **No pseudonymisation of content.** Somebody who wrote their address in a post
  has published it; erasure removes the account, not their prose. An operator
  can edit or delete individual posts, which is the right tool.
- **No automated request workflow.** An operator gets an email and acts on it.
  A ticketing system inside a forum is a different product.
- **No data-processing agreement generator, records of processing, or privacy
  notice text.** Those are the operator's, about their own hosting and purposes,
  and software that guessed at them would be worse than software that says
  plainly they are needed.
- **No consent for things that are not consent-based.** Delivering a forum
  somebody signed up for runs on contract, not consent, and pretending
  otherwise gives people a withdrawal right that would break the service.
