Data Handling Statement
- Version
- 1.2
- Effective
- Cited as
- legal:data-handling@1.2
This platform records real conversations and holds documents about people's careers. That is a strong claim to make on someone's trust, so this document states what the architecture actually does, in enough detail that a reader could disagree with it.
Everything here describes behaviour that is implemented. Where something is a choice rather than a guarantee, it says so and names the choice.
Audio never leaves the device
Recording and transcription happen entirely inside the desktop application on your consultant's own machine. Speech recognition and speaker separation run as local models loaded into that application — there is no cloud transcription provider in the product, and none can be configured.
No audio is ever uploaded to this platform. What the platform receives is text: transcript turns, summaries, and notes. Audio is not among the kinds of material the platform's device interface will accept, so a recording cannot be sent to us even by mistake.
When the recording is deleted
The audio file is deleted from the consultant's machine, and the deletion is then verified — the code re-checks that the file is gone rather than assuming it, and records an error if it is not.
Deletion is triggered by whichever of these comes first:
- the sitting ends and its material is checked in;
- the consultant discards the sitting;
- a fifteen-minute window closes. The recording is deliberately kept for that short period after a session for one reason: so that a consultant who wants to export the audio for their own records can still do so. After it, the file goes;
- the application is shut down;
- the application starts up and finds an orphaned recording left by an earlier run, which it sweeps.
We would rather describe that window than round it off. The recording does not vanish the instant the transcript is written; it survives for at most fifteen minutes, on that one machine, and then it is removed and the removal is checked.
Local session material is encrypted at rest
While session material waits on the consultant's machine to be checked in, it is encrypted with AES-256-GCM. The key is held in the operating system's own credential store, minted there on first use.
Two properties are worth stating because they are the ones that usually are not true:
- It fails closed. If the key cannot be read or cannot be minted, the application refuses to write client material rather than falling back to plaintext. The refusal message is literal: refusing to write client material in the clear. A stored key that is present but malformed is not silently replaced either.
- Older files are not left behind. A file written before encryption existed is recognised as such and sealed the next time it is written.
What is sealed: the session transcript and its sidecar, the readable session log, and the stored Coaching Mode configuration.
What is deliberately not sealed
Three things on the consultant's machine are plaintext, and a reader should not assume otherwise:
- The audio file, during the fifteen-minute export window described above. It is written unencrypted and then purged.
- The Obsidian vault export. If a consultant publishes notes into an Obsidian vault, that copy is written in the clear. This is a user-initiated export to a folder the user chose, and Obsidian has to be able to read what is in it — encrypting it would mean handing someone a file their own note-taking application cannot open. Once it is in the vault it is an ordinary file on that person's disk, under their control and their backups, and outside this platform's protections.
- The knowledge-base cache, which holds chunks of prepared material and their search vectors.
Identifying details are replaced when material becomes case data
This happens on the platform, on receipt — not on the device before sending. We state the order plainly because it is the part that is easy to imply wrongly: The transcript travels to us encrypted, and it is de-identified here, on receipt, when it is taken into your case: names and identifying details are replaced with consistent stand-ins, so that the same person is the same stand-in every time. The map from a stand-in back to the real detail is held on our side, encrypted per case, and is never sent to a device.
So the pass runs after check-in, over material that has already crossed to us over an encrypted connection. It is not a filter standing between the consultant's machine and this platform, and it should not be read as one.
The pass fails closed. If the detection service cannot be reached, the result is a blocked outcome carrying the reason — never a document that looks as though it contained no identifiers. "Could not check" and "checked and clean" are never allowed to look the same to the code that reads the answer.
Two further properties:
- The alias map cannot be resolved by a device. It is held on the platform and encrypted per case. A consultant's laptop cannot turn a stand-in back into a name offline, because it does not hold the key.
- There is a second pass at the boundary. Text on its way from the platform to an outside model is re-scanned, and the send is refused if anything identifying is still in it. This exists because the first pass could be bypassed by a mistake in code; the second one cannot be, because it re-reads the actual bytes about to leave.
The two places that pass does not stand between you and a third party
Stated here rather than buried, because a guarantee with unnamed exceptions is not a guarantee. In the second of the two, a weaker pass on the device stands there instead — described below rather than counted as equivalent:
PDF extraction. A PDF has no text until a model has read it, so there is nothing for a detector to run over first. Instead of pretending the guard applies, the crossing is made explicit and recorded: a named consultant grants egress on one document with a stated reason, the grant is audited, and nothing can be submitted without it. The extracted text is consultant-only until it has been de-identified. Other document formats are read on our own server and never cross.
Cloud providers selected on a consultant's device. The desktop application can be configured to use a cloud service for live suggestions and for search vectors. The shipped setting is a model running on your consultant's own machine, which reaches nothing outside it. Which providers exist, and what each receives, is named in the subprocessor list — and if you would rather your engagement ran on the local providers, say so.
The platform's own de-identification pass runs after check-in, so it does not stand between the device and such a provider. A second, separate pass on the device does, and it is weaker than the one on the platform. Before any text goes to an outside model, the application replaces the identifying details it already holds — the names on the case and in the material checked out for it, along with email addresses, phone numbers, web links, and runs of six or more digits — and it abandons the request rather than send anything it could not replace. That refusal is enforced at the client boundary rather than at each call site, and a destination that cannot be proved to be on the machine is treated as remote.
It is a protective measure against a list of details we already hold, not a guarantee of anonymity: a person or organisation first mentioned aloud, whom the application has never been told about, is sent as spoken, and so are addresses, dates, ages, job titles, and employers. Nor does it catch an inflected, misspelled, or mis-transcribed form of a name that is on its list. The pass on the platform uses a trained recognition model; the one on the device is literal matching against a list, because the desktop cannot currently load a second inference runtime alongside its speech engines. We would rather name that difference than let the two be read as the same protection.
Consent, withdrawal, and retention
This section restates, word for word, what the consent documents in your portal say. It is here so that the terms are readable before you are asked to agree to anything, and it is kept identical on purpose — a test in this repository fails the build if the two ever drift apart.
Withdrawing
You can withdraw your consent at any time from your portal, and you do not have to give a reason. Each consent you give is a separate agreement, and withdrawal is per category: you can withdraw any one of them without touching the others.
Withdrawal takes effect immediately. From that moment:
- Your material here closes to you and stops being shown in your portal.
- Nothing further moves: no new material is taken in, nothing more is loaded onto your consultant's device for a session, and your material stops being lent into the preparation of any other engagement.
- The platform stops drawing on what was already taken in. Material already stored is no longer retrieved, cited, or used to prepare sessions — including anything that had been carried forward into later preparation.
Withdrawal is not retroactive. Work that was already done with your material while your consent stood — notes your consultant wrote, preparation already produced — was done with your agreement and is not unmade.
What we keep, and for how long
- The audio: never kept. It is deleted from your consultant's machine and it never reaches the platform.
- The transcript and the notes drawn from it: kept while your engagement runs. If you withdraw consent, we stop using them immediately, and we keep the stored copy for twelve months from the date it entered the platform's durable storage — as our own working record of a conversation we took part in, and as evidence of work you acted on, which is our legitimate interest in holding it — and then it is permanently deleted. The twelve months govern only how long the copy exists, never whether it is used.
- Your consent history itself: kept permanently, because it is the record of what was agreed and withdrawn, and when.
If you want stored material deleted sooner, say so and we will handle that separately.
One implementation detail worth adding, because it is the difference between a retention policy and a scheduled job: the twelve-month deletion is carried out by a named, audited administrative action, not by an unattended timer. It is always something a person decided to do, and there is a record of who did it.
Consent is versioned
Every consent document is fingerprinted when it is published and cannot be edited afterwards. Changing the wording means publishing a new version, and publishing a new version drops every case whose agreement was against the old text back to pending until the person agrees again. So a consent record always points at the exact text its signer read, and that signer can still read it.
The documents on this site are versioned the same way, and for the same
reason: each carries a version and an effective date at the top, and a
citation such as legal:data-handling@1.0 that a consent record can store.
Separation of the public site from your material
The public pages and the signed-in application are separate route groups with separate layouts, and the separation is one of data access rather than appearance:
- No public page queries any table containing engagement, client, or session data, and none has a privileged database credential anywhere in what it imports. The legal pages you are reading are static text compiled into the site at build time.
- The signed-in surfaces are marked not to be indexed, and the site's
robots.txtdisallows them. - In the browser, the application is permitted to talk to our own origin and to our database and to nothing else. The marketing pages are permitted to submit no forms at all.
- Uploaded files live in a private bucket, reachable only through short-lived signed links, and are never served from the public origin.
Deletion is verified, not assumed
Where the platform deletes something, it re-reads afterwards and reports what actually went. This applies to the local audio purge, to quarantined session material that reaches its expiry, and to stored files whose case no longer exists. A count that says twelve rows were removed while twelve rows are still there is precisely the failure this discipline exists to prevent.
Deleting a case
The strongest instance of that discipline is the one that deletes a whole case. It is available to a founder and to nobody else, it takes one case per call — there is no way to spell delete everything matching this with it — it will not run without a stated reason, and performing it is itself written to the audit trail.
The list of what to delete is read from the database's own catalogue rather than typed out by hand, so a table added next year is covered on the day it is created instead of the day somebody remembers it. Two things are done explicitly, because leaving them to the database would get them wrong:
- Bookings are deleted by name. The database would otherwise merely detach them from the case, leaving your name, email address and phone number sitting in a row with nothing left to explain why it was there.
- A sealed knowledge base is allowed to go with its case. It is otherwise frozen against editing; a narrow exception excuses deletion during this one operation and nothing else.
Then it proves itself. Every column in the database that could hold one of that case's identifiers is scanned for one — every table, including tables nobody thought about when this was written — and if a single row is still standing, the entire deletion is rolled back rather than reported as a partial success. Files are a separate step, because deleting the database row that names a file does not delete the file: the deletion records which files are still owed, and it is not marked finished until something has re-read the bucket and found it empty.
It can also refuse outright. If another case was merged into this one, or a sealed record elsewhere cites a run belonging to this case, it stops and names what is in the way instead of deleting around it.
What a deletion keeps, and why
Three records survive it. Naming them is the point of this section:
- The consent ledger — what you agreed to, against which version of which document, and when, including your withdrawals. It carries the email address that agreed and the IP address and browser it came from.
- The audit trail — who did what and when, carrying the staff email address that acted and before-and-after copies of the rows they changed.
- The record that the deletion itself happened — the case reference, the name it was filed under, who deleted it, when, why, and how many rows went. Counts, never content.
The first two refuse UPDATE and DELETE at the database level, by a rule the application cannot turn off; the third accepts exactly one later change, the stamp confirming the files are gone. That refusal is not incidental to the retention, it is the reason for it. A consent ledger that could be edited entry by entry would be evidence of nothing, and an audit trail that could be pruned would be least trustworthy at precisely the moment it matters most — which is when it records a deletion. The consent record is also what makes holding the material defensible in the first place, so destroying it alongside the material would remove the proof that we were entitled to hold what we just deleted.
The counterweight is disclosure rather than hedging. All three are exported in full when someone asks for a copy of what we hold, so what is kept is at least readable by the person it is about. The privacy policy states the same thing as a retention period.
Asking us anything
Requests to see, correct, export, or delete what we hold — or to ask which providers your own engagement is configured with — go to hello@main-character.me.