How fepli encrypts participant data end to end
A tenant can encrypt its participants' data so that only the browsers of the team, the organizers and the families can read it. fepli's server stores and sends the data without knowing it.
What the encryption protects against
fepli holds the names and dates of birth of children, notes on allergies or medication and the addresses of their families. Without encryption, this information lies readable in the database, in its backups and in every archive. Whoever gets hold of one of them can read it.
Encryption on the server helps little against that. The server then needs the key, and whoever takes over the server reads the key along with everything else. fepli therefore encrypts in the browsers of the people who work with the data. The information reaches the server only in encrypted form.
The encryption is the tenant's decision, like Advanced Data Protection in iCloud or FileVault on a Mac. A tenant that does not switch it on works as before. A tenant that switches it on takes on a responsibility: if all keys and the emergency kit are lost, nobody can recover the data, fepli included.
What does the server see, and what not?
- Pseudonym and age of every registration
- Email addresses, phone numbers, first names of accounts
- Offers and who is registered where and when
- public keys and signatures
- Names, dates of birth, important information, answers
- Families' last names and billing addresses
- the content of archived receipts and proofs
- Passwords, private keys, the emergency kit
The browser encrypts every value before it sends it and decrypts it only when it shows it. In between, the server holds an envelope: the encrypted value and the keys with which authorised browsers open it. The server cannot open the envelope itself.
So that lists, sorting and mail merges have something to show, the server stores a pseudonym instead of the name, e.g. “Kind K7Q2”. Instead of the date of birth, it keeps the year of birth and, for every registration, the age. If the browser sends the date along at registration, the server computes exactly with it and discards it afterwards. The year of birth itself is changed only by whoever may edit the participant.
The server still sees what it needs for its work: email addresses and phone numbers for notifications, the first names of accounts for greetings, the offers and who is registered where and when. Passwords, private keys and the recovery key never reach it.
Which data fepli encrypts
fepli encrypts a participant's first name, last name, date of birth and Important information for the organizers. It also encrypts the answers to additional questions, the last name of a participant account and the billing address of a receipt. The server encrypts receipts and proofs of consent before it stores them in the archive. The complete list is in the reference.
Two kinds of answers deliberately stay readable, because the server calculates with them. A choice with a surcharge determines the participation fee and appears on the receipt. A yes/no question of the season can decide the priority for working parents in the lottery.
The names of the accounts of admins and organizers stay readable. What is encrypted is what tells something about children and families.
Which keys are there?
Every record has its own random record key. With it, the browser encrypts every field with AES-256-GCM. The method binds every encrypted value to its record and its field: whoever copies it into another record gets an error instead of a value when opening it. Before encrypting, the browser pads short values so that their length reveals little.
The browser encrypts the record key for everyone who is to read the record, following the HPKE pattern with X25519 and HKDF. Readers are groups and persons. The tenant's team key reads everything. Every organizer profile has a group key of its own for the children registered for its offers. If a family has a key of its own, the browser encrypts the record key of its children for the family as well.
The private part of the team key is nowhere stored in plain text. It is encrypted for every member, with that member's personal key, and additionally for the recovery key, whose code is printed on the emergency kit. The personal key is stored encrypted on the server as well; only the person's passkey or password can open it.
Then there is the team signing key, an Ed25519 key. With it, the team signs which public keys belong to the tenant, and the statement that the tenant encrypts. The Encryption page shows its fingerprint, and the fingerprint is printed on the emergency kit.
Signing in and unlocking are two steps
Whoever signs in proves to the server who they are. Whoever unlocks opens their personal key in their own browser. A sign-in link signs in but unlocks nothing: until unlocking, the admin panel shows pseudonyms, and the lock in the top bar stays closed.
The browser holds unlocked keys as objects that cannot be read out. Within one sign-in, the browser opens the keys by itself on reload and in further tabs. Signing out deletes what it kept for that. Sign in as opens no keys either: whoever works in another person's account sees that person's data as a pseudonym.
With a passkey
With the PRF extension, a passkey can produce a secret that belongs to it and to fepli. The browser derives from it the key that opens the personal key. The secret does not leave the browser. Not every passkey can do this; passkeys stored in iCloud, Google or 1Password can.
With a password
Where a tenant encrypts, no browser sends a password to the server any more. From the password, the email address and a random value of the account, the browser computes a master key with Argon2id and derives two values from it. It sends the sign-in key to the server, which checks it as it used to check the password. It keeps the unlock key; neither can be computed from the other.
A password the server has seen before no longer counts for this. Whoever has one sets a new one after a sign-in link or a passkey. A forgotten password restores only the sign-in; the account gets a new key.
That is why registration on the website asks for no password. Families register with a passkey or a login link and set a password afterwards in their account. Whoever wants to delete their account confirms it with a link from the mailbox, because the server cannot check the password.
Why the team unlocks only with a passkey
Whoever has a copy of the database can guess a password offline, attempt by attempt. Argon2id makes every attempt expensive, but a short password falls all the same. With a passkey, there is nothing to guess. Because the team key opens all of the tenant's data, admins unlock only with a passkey.
Organizers may unlock with their password as well. Their key opens only the children of their own offers. Many come to the admin panel rarely, and some devices have no suitable passkey. Whoever has no passkey therefore also sets up their own key with the password; it is confirmed with the same emoji comparison.
Why there is no Secret Key
1Password adds a Secret Key to the password, which lives only on the person's devices. In a browser, such a key gets lost too often: cleared browser data, a new computer, an office PC that many people use. Organizers would have to look for a kit on almost every visit. fepli therefore protects the key with the widest reach, the team's, with passkeys rather than with a second secret.
Why a person confirms every new key
A browser encrypts only for keys the team has signed. Otherwise the server could pass off a key of its own as a new team member, and the next unlocked browser would give it the team key. That is why a person who knows the new colleague decides on a new key.
- 1Kim Beispiel, newCreates a personal key with the passkey. The request appears for the team.
- 2Robin Beispiel, teamSelects Compare at the request.
- 3Kim Beispiel, newCommits to a one-time key before seeing the other one.
- 4BothCompare the 7 emojis and confirm.
- 5Robin Beispiel, teamSigns the new key and encrypts the group key for it.
- Butterfly
- Key
- Bicycle
- Pizza
- Moon
- Guitar
- Anchor
The confirmation is an emoji comparison, as the Matrix messenger uses it. Through the server, the browsers of the two people agree on a shared value without revealing it to the server, and show 7 emojis derived from it. The two compare them on the phone or side by side. Only when both confirm and the browsers' check values match does the team member's browser sign the new key and encrypt the team key for it.
The new person commits to their part before seeing the other side's. A server in between therefore has exactly one attempt at matching emojis: the chance is 1 in 2⁴², about 1 in 4 trillion. With the comparison, the new person's browser also receives the team signing key and remembers it. It rejects a statement with a different one from then on.
For organizers, the team confirms the first account of an organizer profile. In doing so, the team member's browser creates the organizer profile's group key, signs it and gives it to the new account; it does not keep it. Every further account is confirmed by a colleague from the same organizer profile.
How organizers get the data of their children
The server knows which child is registered for which offer, but it cannot give anyone access: for that, someone has to open the record key. A team member's browser does this as soon as it unlocks. In the background, it encrypts the record keys of the registered children for the organizer profile's group, but only for a group key that carries the team's signature.
If a child withdraws or an offer moves to another organizer profile, the server deletes the group's key. The server can take a key away at any time, but it can never give one. Until a team member has unlocked, organizers see the pseudonym.
In the same way, the group gets the keys of the answers to additional questions that belong to registrations at its offers. When the registration ends, the server deletes these keys of the group as well.
What families see
On the website, the family's browser encrypts what the family enters about its children for the team key. Before that, it checks the team's signature on the key. On the first visit, it remembers the website's team signing key; if the key changes later, it stores no names and shows “The names cannot be saved”.
If the family's account has a key of its own, the browser also encrypts the record key for it. The website still cannot show the encrypted names. In their place stand the child's avatar and “Name on file”, in sentences “your child”. Whoever wants to change a name enters first and last name again. Other encrypted details such as the date of birth are locked in the family's forms; the team can change them.
Pseudonyms and avatars
The pseudonym consists of “Kind” and 4 characters that fepli derives from a hash of the record's identifier. Siblings can be told apart this way, but nobody recognises a person by it. The characters are no key and reveal nothing about the child.
With the app Avatars, every participant gets a drawn face that fepli also creates from the identifier. It looks the same on every page and in every email. Where the name is missing, families recognise their children by it.
What changes in everyday work
The server can neither search nor sort by encrypted names; it knows only the pseudonym. Exports that the server creates leave encrypted columns empty. It checks an offer's age limit with the date of birth if the browser sends it along at registration, otherwise with the year of birth. The year leaves two ages open; if one of them fits, the offer takes the child. If no age is known, an offer with an age limit does not take the child.
An email names a child only when a person with an open lock triggers it, for instance a newsletter or the admission notices. Their browser sends the first names along, and the server discards them after sending. What notifications send by themselves says “your child” and shows the avatar. In the outbox, placeholders stand in for the names, and the admin panel fills them in after unlocking.
Receipts and proofs of consent lie locked in the archive so that nobody can delete or overwrite them. Until now, they could be read. Now the server encrypts every file with the public keys of the team, the emergency kit and the account concerned before it stores it. The file is opened in the browser. Whatever was in the archive before the encryption was switched on stays unencrypted until its lock ends.
Through the API, fepli hands out the names only sealed. An integration that needs them makes a key of its own, and a super admin confirms it with the same emoji comparison as for colleagues. In doing so, they decide whether it reads every participant or the children of one organizer profile. An integration that cannot encrypt by itself gets the API connector beside it, a Docker image that holds its key.
The integration gets no group key. A team browser seals every record key shared with it one by one. That is why the server can take its access back at any time on its own, without the team needing new keys. How an integration implements this is described in fepli developers.
What support sees
In an installation with several tenants, global admins look after every tenant. They see the personal data of another tenant masked until its admins disclose a record for a limited time. For a tenant that encrypts, that is not enough: the server has only the pseudonym, and a disclosure alone shows support “Kind K7Q2”.
That is why every person in support has a support key of their own. They create it once with their passkey; it applies to every tenant. Each tenant confirms it once with the same emoji comparison as for colleagues and signs it with the team signing key. Only then does a team browser encrypt anything for it.
When an admin with an open lock discloses a record, their browser also encrypts the record key for every confirmed support key, and only for the one record whose name it decrypted in the dialog itself. A record has exactly one key, and it opens all encrypted fields. The scopes of a disclosure therefore differ only in details that are in plain text anyway. When the disclosure ends, fepli deletes the key for support. What support has read until then, the encryption cannot take back.
What the emergency kit is for
If all admins lose their passkeys, the team key is lost without the emergency kit, and with it all data. The kit is a sheet modelled on the 1Password Emergency Kit: code, QR code, fingerprint and room for where it is kept. The browser creates it; the code is never uploaded. Every tenant has exactly one valid kit.
The code has a prefix and a check digit so that a typo shows before anything is tried. When switching on, the person types 4 characters from the kit back; this establishes that they have downloaded it. fepli never sees the key and keeps no copy. If every copy of the kit is lost and no admin has access any more, the data stays locked.
Unlike 1Password's kit, this one needs no password: the code alone opens the recovery key. So that a stolen printout is not enough, the recovery also requires a link from the mailbox of the account that enters the code. The server learns only an identifier of the kit, never the code.
Before the browser connects the opened team key to a new passkey, the person confirms that its fingerprint matches the one on the kit, character by character. Only then does the browser remember this key as the team's.
After a recovery, the kit counts as spent. All super admins and all accounts with access to the team key learn of it by email; that is how a team notices that its kit has left the safe. The person who recovered creates a new kit in the same dialog.
A new kit prints the fingerprint of the team key the account's browser knows, not the one the server names. If the two do not match, no kit is made. Likewise, after unlocking, the Encryption page shows the fingerprint of the key the account knows; before that, it marks the one shown as the server's statement.
Limits
The encryption no longer protects what leaves fepli: emails in the family's mailbox, downloaded PDFs, printouts. The server also still sees that a record exists, when it changed and which offer a registration belongs to. Database backups from before the encryption was switched on keep the data readable until they expire.