Privacy
What this tool keeps, and what it never sees
Nothing you type is kept anywhere but this browser unless you press Save, send something, or write a phrase our own rules cannot read.
Each of those is described here, field by field.
- An account is optional
- No third-party analytics, no advertising, no tracking cookie
- 103 fields across 14 tables, every one listed below
Answering the survey or the interview, perhaps for a child
- What is kept about the child
- No name and no date of birth: the forms have no field for either. Only the answers you choose, such as the age bands when it began and when it was named, and the child’s sex if you give it, which are published only as counts.
- Taking a survey answer back
- A survey answer holds no name, and no account unless you were signed in when you sent it (deleting your account cuts that link). There is nothing to look it up by, so this site has no way to delete one.
- Taking an interview back
- Write to us through Contact and it is removed. Until then it is encrypted, and only our team reads it.
In full: what the survey sends · what an interview sends.
At a glance
- Your deviceYour journey stays in this browser until you press Save or send something.
- Field by fieldEverything that reaches our database is listed below, generated from the database itself.
- 4 wordsAt most four words each side of a phrase the rules cannot place reach the AI reader. No price.
- OptionalAn account is optional and changes none of that: your journey still lives in this browser.
- 0third-party requests made by your browser. No third-party analytics, no advertising, no tracking cookie.
- 1 day 180 daysAn AI reader answer is held one day, then it is gone; an anonymous save stops opening after 180 days.
Where your words go
Your device, what crosses the wire, and what our side keeps. Every line is stated in full below.
Stays on your device
This browser, until you press Save or send something
- Your journey, in local storage. Start over removes it.
- Your coverage and where you live. The fit is worked out on your device.
- A shared link’s lines, after the
#.
What crosses the wire
To this site, Precision Federal’s server
- The words in the box, while you type, so our server runs the same rules again.
- A ledger you Save: units, counts and your short phrases.
- What you choose to send: a correction, a gap report, a survey or an interview.
From our server to the AI reader (OpenAI). Our server sends it, never your browser
- Only a phrase the rules cannot place, each on its own.
- At most four words from the phrase before it and four from the phrase after it.
- The list of unit names. No price is sent and none comes back.
What our side keeps
Every field is listed below, generated from the database
- An AI reader answer, under a one-way hash of what you typed, for one day. Then it is gone.
- A ledger you Save. Its delete code only as a one-way hash.
- What you choose to send, field by field. A note is never published.
- An account, only if you make one.
Never stored
- What follows the
#in a share link. A browser never sends it to any server. - Your IP address. Only a salted hash, which stops being comparable the next day.
- Tracking. No third-party analytics, no advertising, no tracking cookie. One first-party count of actions per day, with no cookie and no identifier, published on the live register.
What we store, and for how long
One row for each thing that can be kept. The first two are held only in this browser.
| What | Where it is kept | Why | How long |
|---|---|---|---|
| Your journey | This browser only, in local storage | So the ledger works with no account | Until you press “Start over” or clear your browser data |
| Your coverage and where you live | This browser only, under waypoint-ledger.ctx.v1. Not attached to a correction, a gap report, a share link or a saved ledger | To pick which published figure each line shows. The fit is worked out on your device | Until you clear your browser data |
| An AI reader answer (it carries the phrases you typed) | Our side, under a one-way hash of what you typed | So the same sentence is not read twice | One day, then it is gone |
| A ledger you Save: units, counts and your short phrases | Our database. The delete code only as a one-way hash | So the link you get back opens the ledger again | An anonymous save stops opening after 180 days; a save on an account has no expiry. The delete code removes it |
| A correction | Our database. No name, diagnosis or IP address on the row | To count how each published figure is marked; the aggregate is public | Stays counted. Deleting your account removes the link to you |
| A gap report | Our database. Context published only as counts, any value under 11 not shown. The note is never published | To record care you needed and did not get | No end date is set |
| A burden survey answer | Our database. Your note encrypted at rest and never published | To rank which burden weighed most | Nothing points back to you, so this site has no way to delete one |
| A written interview | Our database, encrypted at rest. Only our team reads it | Your own story, quoted only the way you chose | Until you ask us to remove it, through Contact |
| An account, only if you make one | Our database. Your password and recovery code only in a scrambled form | So a ledger follows you between devices | Until you delete it from /account |
| The sign-in cookie | Your browser holds one random value; the session is on our side | To keep you signed in | Until you sign out |
| Daily counts of a few actions | Our server: one number per day, nothing about who | To count site use, with no cookie, no address, no identifier and nothing you typed. Public on the live register | No end date is set |
corrections12 fields: 6 public, 1 encrypted or hashed, 5 othergap_reports10 fields: 6 public, 0 encrypted or hashed, 4 othersurvey_responses14 fields: 10 public, 1 encrypted or hashed, 3 otherinterviews11 fields: 6 public, 3 encrypted or hashed, 2 otherjourneys10 fields: 0 public, 1 encrypted or hashed, 9 otherusers8 fields: 0 public, 2 encrypted or hashed, 6 othercredentials6 fields: 0 public, 0 encrypted or hashed, 6 othersessions4 fields: 0 public, 0 encrypted or hashed, 4 otherchanges8 fields: 5 public, 0 encrypted or hashed, 3 otherevents3 fields: 0 public, 0 encrypted or hashed, 3 otheragg5 fields: 1 public, 0 encrypted or hashed, 4 othercanary2 fields: 0 public, 0 encrypted or hashed, 2 otherannotations6 fields: 4 public, 0 encrypted or hashed, 2 otherintegrity_heads4 fields: 4 public, 0 encrypted or hashed, 0 other
The details
Each promise in full. Open any row.
Your journey stays in this browser
Held in local storage. An account is optional and changes none of that.
What you type into the ledger is held in this browser’s local storage. Clearing your browser data, or pressing “Start over”, removes it. An account is optional and changes none of that: your journey still lives in this browser.
If you make one, we store a random account number, whichever way in you chose (the public half of a passkey, or an email address and a scrambled form of your password that cannot be turned back into it), a ten-word recovery code we keep only the scrambled form of, and the ledgers you press save on. No mail is ever sent to that address.
Deleting your account from /account erases all of it; corrections you already sent stay in the public register with the link back to you removed.
What the AI reader sees
Only phrases the rules cannot place, with at most four words each side. No price.
While you type, the words in the box are sent to this site so that the same rules your browser just ran can be run again on our server. Only the phrases those rules cannot place go any further.
Each one goes on its own, with at most four words from the phrase just before it and four from the phrase just after it, together with the list of unit names from the price table. The rest of what you wrote stays here, and no price is sent.
On waypointledger.org these phrases go over an encrypted connection to OpenAI, whose model (gpt-5.6-luna, or gpt-5.5 and then gpt-5.4-mini if that one does not answer) may reply only with a unit name the table already holds. No price ever comes back: the published federal table does all of the pricing here.
The answer, which carries the phrases you typed, is held on our side for one day under a one-way hash of what you typed, so the same sentence is not read twice. After that day it is gone.
The code picks the model by which key a copy of this site holds: OpenAI when it has an OpenAI key, which ours does; otherwise Anthropic; otherwise Cloudflare Workers AI, Cloudflare’s own model on the network that served you this page. Our staging copy has no OpenAI key and uses Cloudflare Workers AI. If the model does not answer, the rules’ answer stands and the line is left blank for you to fill in. What OpenAI keeps on its side is set by its API data policy. You can see exactly what is sent and what comes back at POST /api/map (add ?debug=1), and the rules themselves are in the open source.
If you press Save
The one thing that puts your own words on our server. It comes with a delete code.
Saving is the one thing that puts your own words on our server: the units of care, their counts and the short phrase you typed for each line, so the link you get back opens the ledger again.
Two things come back with that link. A delete code, shown once, which is the only way to remove the save and needs no account. We keep only a one-way hash of it, so we cannot use it and cannot recover it for you. And a date: an anonymous save stops opening after 180 days, while a save on an account has no expiry.
Anyone holding the link can open that ledger, so treat the link as the ledger itself and keep names out of what you type.
What you tell the ledger about yourself
Your coverage and where you live stay in this browser and ride on nothing you send.
Choosing your coverage and where you live changes which published figure each line shows you. Both answers are held in this browser under waypoint-ledger.ctx.v1, and the fit of a figure to a person is worked out on your own device: neither answer is attached to a correction, a gap report, a share link or a saved ledger.
A share link carries no names
A copied link holds the ledger after the #, the part a browser never sends.
The link the ledger copies for you carries only the units of care, their counts, the counter a line belongs in and how many months, never your words or a name, inside the link itself, after the #. A browser never sends that part to any server, so a shared journey of this kind is never stored by us and never arrives here.
A link from Save is the other kind: that one is a row in our database, and it is described field by field below.
What a correction sends
The figure, your verdict, an optional amount and note. No name, diagnosis or IP address.
If you mark a published figure right or wrong, we record the identifier of the figure, your verdict, optionally the amount you say you paid, the version of the price table, and an optional note that is never published and never exported. No name, no diagnosis and no IP address on the row, ever.
If you happen to be signed in, the row also records which account sent it, so the site can show you what you have flagged; deleting your account removes that link and leaves the correction counted. If you are not signed in (the default, and how nearly everyone uses this), there is no account and nothing about you on the row. The aggregate is public at /api/corrections.
To stop one person answering the same figure a hundred times, the server also counts sends against a one-way hash of the network address, salted with the date and a secret only the server holds. The address itself is never stored, the hash cannot be turned back into it, it stops being comparable the next day, and nothing derived from it appears in any published row or export. The full description is on the integrity page.
So that one person cannot answer the same figure a hundred times, your browser makes a random identifier for itself the first time you send anything, and keeps it under waypoint-ledger.submitter.v1. It is sent with a correction and the server stores only a truncated hash of it combined with that one price row: a hash that cannot be joined to your answer on any other row, and that we could not turn back into the identifier if we wanted to. It is used for nothing else and sent nowhere else.
What a gap report sends
Counts, ranking, an optional note and context. A group under 11 is never named.
If you report care you needed and did not get, we record the counts and ranking you entered, an optional note, and any optional context you chose to give (an age band, insurance type, region, and how urban or rural).
The counts and the ranking are public at /api/gap and in the open CSV, with the day the report arrived and never the time. The context is published only as counts, and any value fewer than 11 reports gave is not shown: it is counted in one line, “fewer than 11, not shown”, and never named. The note is never published.
When you add the ledger’s “Times you were told it was nothing” count, your browser also sends a random identifier it keeps under waypoint-ledger.gap-key.v1, joined to the number. The server keeps a one-way hash of that pair for one day, in a store separate from the reports, so the same number pressed twice is counted once. It is not written to the report, and it is used for nothing else.
What the burden survey sends
Your ranking and answers go in the open CSV; what you say about yourself, only as counts.
Your ranking of five burdens, three multiple-choice answers, an optional clinician count, any optional self-description you chose (who is answering, age band, sex, coverage, region, state, stage, and the age bands when the illness began and when it was diagnosed), the channel slug on the link you used, and the time it was received. An optional one-sentence note is encrypted at rest and never published.
What is public, and where. The open CSV at /api/export/survey.csv has one row per answer: your ranking, your three answers, the clinician count, the channel, the instrument version and the day it arrived. It never has the time and never has anything you said about yourself.
What you said about yourself is published only as counts, at /api/survey and on the register, and any answer fewer than 11 people gave is not shown there: it is counted in one line, “fewer than 11, not shown”, and never named. The same rule is applied on our server, before anything is sent to a browser.
An answer about a child carries no name and no date of birth, and is read as its own group on the register. If you were signed in when you sent it, the row also records which account sent it, and deleting your account cuts that link. Otherwise nothing on the row points back to you, so there is no way to find one answer again, and this site has no way to delete one.
What a written interview sends
Encrypted at rest, read only by our team, quoted only the way you chose.
The answers you wrote, who is answering if you chose to say (you, or a parent or caregiver), the consent you chose (learn only, quote anonymously, quote by name), a name only if you chose to be quoted by name, and an email address only if you asked to hear about a new version.
Interview answers are encrypted at rest. Only our team reads them, through a private admin screen; no public page, endpoint or export carries them. Quotes appear only in the way you chose. To have an interview removed, write to us through Contact.
No third-party analytics, no advertising, no tracking
Your browser makes no third-party request. The only cookie is a sign-in session.
Your browser makes no third-party request of any kind. The single outside request this product makes is the one above, to the AI reader, and our server makes it: never your browser, and never with a price in it.
The three typefaces are served from this domain (they used to come from Google Fonts, and that was the last outside request left). There is no third-party analytics script, no advertising, no tracking cookie. Our own server does keep one number per day for each of a few actions (a story read, a ledger opened, priced, saved or exported, a sheet printed, a correction, a survey answer, an interview), with no cookie, no address, no identifier and nothing you typed; those counts are public on the live register, and our own tests and known bots are left out of them. The only cookie this site can ever set is the session cookie you get if you sign in, and it holds one random value.
Every field this database has
103 columns across 14 tables, generated from the schema itself.
When you do send something, every field that lands in our database is listed here, and this list is generated from the database itself, so it cannot fall behind.
A privacy page written as prose drifts from the database the first time an engineer adds a column. So this part is not written. It is generated from the schema itself (103 columns across 14 tables, read from 0001_init.sql, 0002_integrity.sql, 0003_users.sql, 0004_journey_privacy.sql, 0006_agg.sql, 0007_annotations.sql), together with the validators as they actually run, the field list inside the tamper-evidence chain, and the header of each published CSV. A column added without a plain-English description here fails our build, so a field cannot ship without appearing on this page.
Generated 2026-10-01 · schema fingerprint 4ed7c6071383
- Kept
- Says whether the value is always there or only when it applies.
- Public
- Means the value is served to anyone row by row (for the three registers with a CSV, that is exactly what the CSV carries); a value published only as a count, like what you say about yourself on the survey, says no here and is explained under its table.
- In the CSV
- Means it is in the open download at /api/export.
POST /api/corrections corrections
12 fields
You mark a published federal figure right or wrong.
The count for each figure is public at /api/corrections and every published field is in the CSV. Your note is not.
What is sent, and where each part lands: priceId → price_id · verdict → verdict · note → note · believedValueUsd → believed_usd · priceTableVersion → table_version · journeyId → journey_id · submitterId → submitter_hash · receivedAt → received_at
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A random row number for the correction. | always | no | no | no |
price_id Which published federal figure you marked. The row is about a figure, not about you. | always | no | yes | yes |
verdict Right or wrong, as you pressed it. | always | no | yes | yes |
believed_usd The amount you said you actually paid, if you chose to give one. | only when it applies | no | yes | yes |
note What you typed in the box, if you typed anything. It is never published, never exported and never served by any endpoint. | only when it applies | no | no | no |
table_version Which version of the price table the figure came from when you marked it. | only when it applies | no | yes | yes |
journey_id A saved-ledger identifier, only if whoever called the API supplied one. This site never sends it, so it is blank on every row this app has written. | only when it applies | no | no | no |
received_at When it arrived. | always | no | yes | yes |
prev_hash The hash of the row before yours. This is what makes the public count tamper-evident. | only when it applies | no | no | no |
row_hash The hash of the published fields of your row, chained to the row before it. | only when it applies | no | yes | yes |
submitter_hash A one-way hash of a random identifier your browser keeps to itself, mixed with this one figure. It refuses a second thumb on the same figure and cannot be joined to your answer on any other figure. | only when it applies | one-way hash | no | no |
user_id The account that sent it, and only if you were signed in, so the site can show you what you have sent. Blank on every anonymous send. Deleting your account clears it and leaves the correction counted. | only when it applies | no | no | no |
POST /api/gap gap_reports
10 fields
You report care you needed and did not get.
The counts and the ranking are public at /api/gap and in the CSV, with the day it arrived but not the time. Your age band, insurance, region and urbanicity are public only as counts at /api/gap, and any value fewer than 11 reports gave is not shown. Your note is not public.
What is sent, and where each part lands: counts → counts_json · ranking → ranking_json · note → note · context → context_json · receivedAt → received_at · tableVersion → table_version
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A random row number for the report. | always | no | no | no |
counts_json The counts you entered for care you needed and did not get, by category. | always | no | yes | yes |
ranking_json Your ranking of which of those weighed most. | always | no | yes | yes |
note An optional note in your own words. Never published, never exported. | only when it applies | no | no | no |
context_json The optional age band, insurance type, region and urbanicity you chose to give. Blank unless you chose them. Published only as counts at /api/gap, where any value fewer than 11 reports gave is not shown; never in the CSV. | only when it applies | no | no | no |
table_version Which version of the price table was live when you sent it. | only when it applies | no | yes | yes |
received_at When it arrived. The CSV publishes the day only (received_on), never the time. | always | no | yes | yes |
prev_hash The hash of the row before yours. | only when it applies | no | yes | yes |
row_hash The hash of your row as it was written, chained to the row before it. Published in the CSV beside prev_hash so the links can be checked row by row. | only when it applies | no | yes | yes |
user_id The account that sent it, only if you were signed in. Blank on every anonymous send. | only when it applies | no | no | no |
received_at: inside the integrity chain as written, and in the CSV only as the day. Published as the day only (received_on): a time to the millisecond beside a row hash lets anyone who guessed the rest of a row confirm the guess.
POST /api/survey survey_responses
14 fields
You rank which burden weighed most.
Your ranking, your three answers, the clinician count, the channel and the day it arrived are in the CSV, one row per answer, with no time of day and no self-description. What you said about yourself (age band, sex, coverage, region, state, stage, who is answering, the ages at onset and diagnosis) is public only as counts at /api/survey, and any value fewer than 11 answers gave is not shown. Your sentence is never public.
What is sent, and where each part lands: ranking → ranking_json · unasked → unasked · lead → lead · decide → decide · clinicians → clinicians · context → context_json · sentence → sentence_enc · channel → channel · surveyVersion → survey_version · receivedAt → received_at
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A random row number for the response. | always | no | no | no |
ranking_json Your ranking of the five burdens, heaviest first. | always | no | yes | yes |
unasked Which burden you said nobody ever asked you about. | always | no | yes | yes |
lead Which burden you said a tool should lead with. | always | no | yes | yes |
decide Who you said should decide how burdens are weighed. | always | no | yes | yes |
clinicians How many clinicians you saw before a diagnosis, if you gave a number. | only when it applies | no | yes | yes |
context_json What you chose to say about yourself: who is answering, age band, sex, coverage, region, state, stage, and the age bands when it began and when it was diagnosed. Blank unless you chose them. Never in the CSV and never served per answer: published only as counts at /api/survey, where any value fewer than 11 answers gave is not shown. | only when it applies | no | no | no |
sentence_enc One optional sentence in your own words, encrypted with a key kept outside this database. No endpoint serves it and no export carries it; only the number of sentences held is public. | only when it applies | encrypted at rest | no | no |
channel The slug on the link you arrived through, so the sample can be described honestly as what it is. | always | no | yes | yes |
survey_version Which version of the instrument you answered. | only when it applies | no | yes | yes |
received_at When it arrived. The CSV publishes the day only (received_on), never the time. | always | no | yes | yes |
prev_hash The hash of the row before yours. | only when it applies | no | yes | yes |
row_hash The hash of your row as it was written, chained to the row before it. Published in the CSV beside prev_hash so the links can be checked row by row. | only when it applies | no | yes | yes |
user_id The account that sent it, only if you were signed in. Blank on every anonymous send. | only when it applies | no | no | no |
received_at: inside the integrity chain as written, and in the CSV only as the day. Published as the day only (received_on): a time to the millisecond beside a row hash lets anyone who guessed the rest of a row confirm the guess.
context_json: inside the integrity chain as written, and in the CSV not at all. Every optional self-description (age band, sex, coverage, region, state, stage, who answered, ages at onset and diagnosis). One row carrying all of them can point at a person, so they are published only as counts at /api/survey, with any value under 11 answers not shown.
POST /api/interview interviews
11 fields
You write out your own story in the interview form.
Only the number of interviews, their dates and how many people chose each consent are public. Nothing you wrote is served by any endpoint or carried by any export.
What is sent, and where each part lands: consent → consent · name → name_enc · answers → answers_enc · respondent → answers_enc · followUp → follow_up · email → email_enc · channel → channel · receivedAt → received_at
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A random row number for the interview. | always | no | no | no |
consent How you said your writing may be used: notes only, quote anonymously, or quote by name. | always | no | yes | no |
name_enc The name you asked to be quoted under, encrypted at rest. Stored only if you chose to be quoted by name. | only when it applies | encrypted at rest | no | no |
answers_enc Everything you wrote, and who is answering if you chose to say, encrypted at rest with a key kept outside this database. Only our team reads it, through a private admin screen; no public endpoint serves it and no export contains it. | always | encrypted at rest | no | no |
follow_up Whether you ticked the box asking to hear when there is a new version. | always | no | yes | no |
email_enc Your address, encrypted at rest, kept only if you ticked that box, and used for nothing else. | only when it applies | encrypted at rest | no | no |
channel The slug on the link you arrived through. | always | no | yes | no |
received_at When it arrived. | always | no | yes | no |
reviewed_at When we read it. | only when it applies | no | no | no |
prev_hash The hash of the row before yours, over the four facts we publish about an interview and nothing you wrote. | only when it applies | no | yes | no |
row_hash The hash of those four facts: that an interview arrived, when, under which consent, through which channel. | only when it applies | no | yes | no |
POST /api/journeys journeys
10 fields
You press Save on a ledger to get a link to it.
Nothing here is public. A saved ledger opens for whoever holds its link, and for nobody else.
What is sent, and where each part lands: entries → entries_json · title → title
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A random identifier for a ledger you pressed Save on. | always | no | no | no |
user_id The account that saved it, if you were signed in. Blank for an anonymous save, which is the default. | only when it applies | no | no | no |
share_slug The short code in the link you got back. Anyone holding that link can open the ledger, so treat it as the ledger itself. | only when it applies | no | no | no |
title The title you typed for the saved ledger, if you typed one. | only when it applies | no | no | no |
entries_json The units of care, their counts and the short phrases you typed for each line, and what you said you paid for a line if you entered it. This is the one place your own words are kept, and only because you pressed Save. | always | no | no | no |
table_version Which version of the price table the ledger was priced against, so the figures can be reproduced later. | only when it applies | no | no | no |
created_at When it was saved. | always | no | no | no |
updated_at When it was last changed. | always | no | no | no |
delete_hash A one-way hash of the delete code you were shown once. It cannot be turned back into the code, so the save can be removed by you and not by us. | only when it applies | one-way hash | no | no |
expires_at When an anonymous save stops opening: 180 days from saving. A save on an account has no expiry. | only when it applies | no | no | no |
POST /api/auth/password/signup users
8 fields
You make an optional account so a ledger follows you between devices.
Nothing about an account is public, ever.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A random account number. Nothing about you is derived from it and nothing about you is stored in it. | always | no | no | no |
created_at When the account was made. | always | no | no | no |
display_name A name you typed for yourself, if you chose one. It is never shown to anyone else. | only when it applies | no | no | no |
email The address you signed up with, if you chose an address rather than a passkey. No mail is ever sent to it. | only when it applies | no | no | no |
password_hash A scrambled form of your password that cannot be turned back into it. | only when it applies | one-way hash | no | no |
password_salt Random bytes mixed into that scrambling, so two people who chose the same password do not look the same here. | only when it applies | no | no | no |
recovery_hash A scrambled form of your ten-word recovery code. We keep no readable copy, so we cannot use it and cannot recover it for you. | only when it applies | one-way hash | no | no |
updated_at When the account was last changed. | only when it applies | no | no | no |
POST /api/auth/register/verify credentials
6 fields
You add a passkey to that account.
Nothing here is public.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id The identifier your passkey handed us when you added it. | always | no | no | no |
user_id Which account the passkey belongs to. | always | no | no | no |
public_key The public half of your passkey. It can check a signature and nothing else; it cannot unlock anything, here or anywhere. | always | no | no | no |
counter A number your passkey increases each time it is used, which is how a cloned key is caught. | always | no | no | no |
transports How the key was presented, as your browser reported it: this device, a USB key, a phone. | only when it applies | no | no | no |
created_at When the passkey was added. | always | no | no | no |
POST /api/auth/password/login sessions
4 fields
You sign in, and the browser holds one cookie until you sign out.
Nothing here is public.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id The random value in the one cookie you get after signing in. It says nothing about you. | always | no | no | no |
user_id Which account that cookie signs in. | always | no | no | no |
created_at When you signed in. | always | no | no | no |
expires_at When the cookie stops working. Signing out deletes the row before then. | always | no | no | no |
POST /api/admin/changes changes
8 fields
We publish "someone said this, so we changed that" on the register. Written by us, not by you.
The date, what was said, what changed and who is credited are shown on /register. Which interview it came from is not.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A row number for a change we made because someone told us something. | always | no | yes | no |
date The date we made the change. | always | no | yes | no |
said What a person told us, written in the form they consented to. | always | no | yes | no |
changed What we changed in the product because of it. | always | no | yes | no |
who Who is credited. Anonymous unless the person asked to be named. | always | no | yes | no |
source_interview_id Which interview it came from, so we can find it again. Never published. | only when it applies | no | no | no |
published Whether the entry is shown on the public register. | always | no | no | no |
created_at When the entry was written. | always | no | no | no |
every request events
3 fields
A daily count of how many times each endpoint was called, and of a few actions your browser reports with one word and no body (a ledger opened, a sheet printed, a file exported). One number per day, nothing about who.
The counts of actions are public, by week, on the live register and at /api/use, with our own tests and known bots left out. The other totals are not public. Neither has anything about anyone in it.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
day The date the count belongs to. Nothing else about that day is kept. | always | no | no | no |
name Which endpoint was called or which action was taken, by its name: corrections, survey, price, use:ledger. A use count from our own tests or a known bot is filed under synthetic: or bot:. | always | no | no | no |
count How many times it was called that day, across everyone. There is no row per person, no address and no session in this table. | always | no | no | no |
every accepted row agg
5 fields
Nobody sends this. It is the register's own public totals, kept ready so the page reads as fast at a hundred thousand rows as at ten.
These are the same counts the register already serves. The table is a cache of them and holds nothing else.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
kind Which register the cached total belongs to. | always | no | no | no |
rows_folded How many rows went into that total, so a total that has fallen behind is spotted and recomputed rather than served. | always | no | no | no |
head_hash The head of the integrity chain the cached total was folded from, so a total can be checked against the register it claims to describe. | always | no | no | no |
payload_json The public totals themselves, exactly as the register already serves them. Counts, never a person. | always | no | yes | no |
updated_at When the total was last recomputed. | always | no | no | no |
POST /api/health canary
2 fields
Nobody sends this either. We write one row and delete it in the same breath, to prove the database is accepting writes.
Nothing here is public, and nothing here comes from anyone using the site.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A random row we write and delete in the same breath to prove the database still accepts writes. It holds nothing else. | always | no | no | no |
at The moment of that test write. | always | no | no | no |
POST /api/admin/annotations annotations
6 fields
We mark an answer we wrote ourselves while testing the form, so it is not counted. Written by us, not by you, and never deleted.
Every mark is public at /api/annotations and in the team_test column of each CSV: which table, the row_hash it marks, the kind and the day. Our note on a mark is not public. The row it marks is not changed.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
id A random id for one mark. | always | no | no | no |
table_name Which register the marked row is in: corrections, gap reports, survey answers or interviews. | always | no | yes | no |
row_hash The published row_hash of the row we marked, so anyone can find it in the CSV. The row itself is not changed. | always | no | yes | no |
kind What the mark says. Only one kind exists: team-test, an answer we wrote ourselves while testing the form. | always | no | yes | no |
note A short note we write when we mark a row, for our own records. Never public. | only when it applies | no | no | no |
at When we marked the row. Published as the day. | always | no | yes | no |
every accepted row integrity_heads
4 fields
The head of each tamper-evidence chain, so an outsider can check the register was not edited.
Every head is public at /api/integrity. Publishing it is the whole point: it is what an outsider recomputes to check we did not edit the register.
| Field | Kept | Encrypted | Public | In the CSV |
|---|---|---|---|---|
table_name Which register the head belongs to. | always | no | yes | no |
head_hash The hash of the newest row. Anyone can recompute it from corrections.csv, and reach it on survey.csv and gap.csv by following the links row by row. | always | no | yes | no |
row_count How many rows that register holds. | always | no | yes | no |
updated_at When the head last moved. Published as the day only. | always | no | yes | no |
Who runs it
Precision Federal, Ames, Iowa.
Precision Federal, Ames, Iowa. Questions: Contact.

