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 — and each of those is described here, field by field.
On this page — 12 sections
Your journey stays in this browser
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
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. Any phrase those rules cannot read is passed on — that phrase, the list of unit names from the price table, and nothing else — over an encrypted connection to a model run by OpenAI, which may answer only with a unit name the table already holds. No price is ever sent to a model and no price ever comes back: the published federal table does every bit of the pricing on this side of the wire. The answer, which carries the phrases you typed, is held for one day under a one-way hash of what you typed, so the same sentence is never read twice; after that day it is gone.
If the OpenAI key is absent the same request goes to Anthropic, or to Cloudflare’s own model on the network that served you this page. If no model answers, the rules’ answer stands and the line is simply left blank for you to fill in yourself. You can see exactly what is asked and what comes back at POST /api/map, and the rules themselves are in the open source.
If you press Save
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
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
The link the ledger copies for you carries the units of care, their counts and your short phrases 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
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 told the government; 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
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). Nothing identifies you. The aggregate is public at /api/gap.
What the burden survey sends
Your ranking of five burdens, three multiple-choice answers, an optional clinician count, any optional self-description you chose (age band, coverage, region, state, stage), 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 word for word. The aggregate and a de-identified CSV are public, and a cell holding too few people to be safe is withheld from both.
What a written interview sends
The answers you wrote, 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 and are never served by any endpoint or included in any export. Quotes appear only in the way you chose. To have an interview removed, write to bo@precisionfederal.com.
No analytics, no advertising, no tracking
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 analytics script, no advertising, no tracking cookie. 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
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 — 97 columns across 13 tables, read from 0001_init.sql, 0002_integrity.sql, 0003_users.sql, 0004_journey_privacy.sql, 0006_agg.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-09-10 · schema fingerprint 892edf4dbbb8 · Kept says whether the value is always there or only when it applies · Public means the value is served to anyone and, where the register is chained, is inside the hash chain · In the CSV means it is in the open download at /api/export.
POST /api/corrections corrections
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 | yes | 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
You report care you needed and did not get.
The counts and the ranking are public at /api/gap and in the CSV. Your note is not.
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. | 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. | always | no | yes | yes |
prev_hash The hash of the row before yours. | only when it applies | no | yes | no |
row_hash The hash of the published fields of your 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 |
POST /api/survey survey_responses
You rank which burden weighed most.
The counts are public at /api/survey and in the CSV. Your sentence is not, and a cell too small to be safe is withheld.
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 The optional age band, coverage, region, state and stage you chose to give. Blank unless you chose them, and a count too small to be safe is withheld from the published table. | only when it applies | no | yes | yes |
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. | always | no | yes | yes |
prev_hash The hash of the row before yours. | only when it applies | no | yes | no |
row_hash The hash of the published fields of your 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 |
POST /api/interview interviews
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 · 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, encrypted at rest with a key kept outside this database. No 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
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. 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
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
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
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
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
A daily count of how many times each endpoint was called. One number per day, nothing about who.
These totals are not public. They are a daily count with nothing about anyone in them.
| 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, by its name: corrections, gap, survey, price. | 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
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
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 |
every accepted row integrity_heads
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, which is the number anyone can recompute for themselves from the published CSV. | always | no | yes | no |
row_count How many rows that register holds. | always | no | yes | no |
updated_at When the head last moved. | always | no | yes | no |
Who runs it
Precision Federal, Ames, Iowa. Questions: bo@precisionfederal.com.