Waypoint Ledger

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.
Solid arrows: what you send. Dashed: what comes back, such as the link to a saved ledger or the unit name the AI reader chose.

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 is kept, where, why and for how long. Each row is stated in full in the sections below.
WhatWhere it is keptWhyHow long
Your journeyThis browser only, in local storageSo the ledger works with no accountUntil you press “Start over” or clear your browser data
Your coverage and where you liveThis browser only, under waypoint-ledger.ctx.v1. Not attached to a correction, a gap report, a share link or a saved ledgerTo pick which published figure each line shows. The fit is worked out on your deviceUntil 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 typedSo the same sentence is not read twiceOne day, then it is gone
A ledger you Save: units, counts and your short phrasesOur database. The delete code only as a one-way hashSo the link you get back opens the ledger againAn anonymous save stops opening after 180 days; a save on an account has no expiry. The delete code removes it
A correctionOur database. No name, diagnosis or IP address on the rowTo count how each published figure is marked; the aggregate is publicStays counted. Deleting your account removes the link to you
A gap reportOur database. Context published only as counts, any value under 11 not shown. The note is never publishedTo record care you needed and did not getNo end date is set
A burden survey answerOur database. Your note encrypted at rest and never publishedTo rank which burden weighed mostNothing points back to you, so this site has no way to delete one
A written interviewOur database, encrypted at rest. Only our team reads itYour own story, quoted only the way you choseUntil you ask us to remove it, through Contact
An account, only if you make oneOur database. Your password and recovery code only in a scrambled formSo a ledger follows you between devicesUntil you delete it from /account
The sign-in cookieYour browser holds one random value; the session is on our sideTo keep you signed inUntil you sign out
Daily counts of a few actionsOur server: one number per day, nothing about whoTo count site use, with no cookie, no address, no identifier and nothing you typed. Public on the live registerNo end date is set
Every table, its fields counted from the schema. Open one for each field in plain English.public row by rownot public, encrypted or a one-way hashnot public
  • corrections12 fields: 6 public, 1 encrypted or hashed, 5 other
  • gap_reports10 fields: 6 public, 0 encrypted or hashed, 4 other
  • survey_responses14 fields: 10 public, 1 encrypted or hashed, 3 other
  • interviews11 fields: 6 public, 3 encrypted or hashed, 2 other
  • journeys10 fields: 0 public, 1 encrypted or hashed, 9 other
  • users8 fields: 0 public, 2 encrypted or hashed, 6 other
  • credentials6 fields: 0 public, 0 encrypted or hashed, 6 other
  • sessions4 fields: 0 public, 0 encrypted or hashed, 4 other
  • changes8 fields: 5 public, 0 encrypted or hashed, 3 other
  • events3 fields: 0 public, 0 encrypted or hashed, 3 other
  • agg5 fields: 1 public, 0 encrypted or hashed, 4 other
  • canary2 fields: 0 public, 0 encrypted or hashed, 2 other
  • annotations6 fields: 4 public, 0 encrypted or hashed, 2 other
  • integrity_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

Every column of corrections, written by cf/functions/api/corrections.js
FieldKeptEncryptedPublicIn the CSV
id A random row number for the correction.alwaysnonono
price_id Which published federal figure you marked. The row is about a figure, not about you.alwaysnoyesyes
verdict Right or wrong, as you pressed it.alwaysnoyesyes
believed_usd The amount you said you actually paid, if you chose to give one.only when it appliesnoyesyes
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 appliesnonono
table_version Which version of the price table the figure came from when you marked it.only when it appliesnoyesyes
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 appliesnonono
received_at When it arrived.alwaysnoyesyes
prev_hash The hash of the row before yours. This is what makes the public count tamper-evident.only when it appliesnonono
row_hash The hash of the published fields of your row, chained to the row before it.only when it appliesnoyesyes
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 appliesone-way hashnono
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 appliesnonono

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

Every column of gap_reports, written by cf/functions/api/gap.js
FieldKeptEncryptedPublicIn the CSV
id A random row number for the report.alwaysnonono
counts_json The counts you entered for care you needed and did not get, by category.alwaysnoyesyes
ranking_json Your ranking of which of those weighed most.alwaysnoyesyes
note An optional note in your own words. Never published, never exported.only when it appliesnonono
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 appliesnonono
table_version Which version of the price table was live when you sent it.only when it appliesnoyesyes
received_at When it arrived. The CSV publishes the day only (received_on), never the time.alwaysnoyesyes
prev_hash The hash of the row before yours.only when it appliesnoyesyes
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 appliesnoyesyes
user_id The account that sent it, only if you were signed in. Blank on every anonymous send.only when it appliesnonono

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

Every column of survey_responses, written by cf/functions/api/survey.js
FieldKeptEncryptedPublicIn the CSV
id A random row number for the response.alwaysnonono
ranking_json Your ranking of the five burdens, heaviest first.alwaysnoyesyes
unasked Which burden you said nobody ever asked you about.alwaysnoyesyes
lead Which burden you said a tool should lead with.alwaysnoyesyes
decide Who you said should decide how burdens are weighed.alwaysnoyesyes
clinicians How many clinicians you saw before a diagnosis, if you gave a number.only when it appliesnoyesyes
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 appliesnonono
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 appliesencrypted at restnono
channel The slug on the link you arrived through, so the sample can be described honestly as what it is.alwaysnoyesyes
survey_version Which version of the instrument you answered.only when it appliesnoyesyes
received_at When it arrived. The CSV publishes the day only (received_on), never the time.alwaysnoyesyes
prev_hash The hash of the row before yours.only when it appliesnoyesyes
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 appliesnoyesyes
user_id The account that sent it, only if you were signed in. Blank on every anonymous send.only when it appliesnonono

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

Every column of interviews, written by cf/functions/api/interview.js
FieldKeptEncryptedPublicIn the CSV
id A random row number for the interview.alwaysnonono
consent How you said your writing may be used: notes only, quote anonymously, or quote by name.alwaysnoyesno
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 appliesencrypted at restnono
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.alwaysencrypted at restnono
follow_up Whether you ticked the box asking to hear when there is a new version.alwaysnoyesno
email_enc Your address, encrypted at rest, kept only if you ticked that box, and used for nothing else.only when it appliesencrypted at restnono
channel The slug on the link you arrived through.alwaysnoyesno
received_at When it arrived.alwaysnoyesno
reviewed_at When we read it.only when it appliesnonono
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 appliesnoyesno
row_hash The hash of those four facts: that an interview arrived, when, under which consent, through which channel.only when it appliesnoyesno

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

Every column of journeys, written by cf/functions/api/journeys.js
FieldKeptEncryptedPublicIn the CSV
id A random identifier for a ledger you pressed Save on.alwaysnonono
user_id The account that saved it, if you were signed in. Blank for an anonymous save, which is the default.only when it appliesnonono
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 appliesnonono
title The title you typed for the saved ledger, if you typed one.only when it appliesnonono
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.alwaysnonono
table_version Which version of the price table the ledger was priced against, so the figures can be reproduced later.only when it appliesnonono
created_at When it was saved.alwaysnonono
updated_at When it was last changed.alwaysnonono
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 appliesone-way hashnono
expires_at When an anonymous save stops opening: 180 days from saving. A save on an account has no expiry.only when it appliesnonono

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.

Every column of users, written by cf/functions/api/auth/password/signup.js
FieldKeptEncryptedPublicIn the CSV
id A random account number. Nothing about you is derived from it and nothing about you is stored in it.alwaysnonono
created_at When the account was made.alwaysnonono
display_name A name you typed for yourself, if you chose one. It is never shown to anyone else.only when it appliesnonono
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 appliesnonono
password_hash A scrambled form of your password that cannot be turned back into it.only when it appliesone-way hashnono
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 appliesnonono
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 appliesone-way hashnono
updated_at When the account was last changed.only when it appliesnonono

POST /api/auth/register/verify credentials

6 fields

You add a passkey to that account.

Nothing here is public.

Every column of credentials, written by cf/functions/api/auth/register/verify.js
FieldKeptEncryptedPublicIn the CSV
id The identifier your passkey handed us when you added it.alwaysnonono
user_id Which account the passkey belongs to.alwaysnonono
public_key The public half of your passkey. It can check a signature and nothing else; it cannot unlock anything, here or anywhere.alwaysnonono
counter A number your passkey increases each time it is used, which is how a cloned key is caught.alwaysnonono
transports How the key was presented, as your browser reported it: this device, a USB key, a phone.only when it appliesnonono
created_at When the passkey was added.alwaysnonono

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.

Every column of sessions, written by cf/functions/api/auth/password/login.js
FieldKeptEncryptedPublicIn the CSV
id The random value in the one cookie you get after signing in. It says nothing about you.alwaysnonono
user_id Which account that cookie signs in.alwaysnonono
created_at When you signed in.alwaysnonono
expires_at When the cookie stops working. Signing out deletes the row before then.alwaysnonono

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.

Every column of changes, written by cf/functions/api/admin/changes.js
FieldKeptEncryptedPublicIn the CSV
id A row number for a change we made because someone told us something.alwaysnoyesno
date The date we made the change.alwaysnoyesno
said What a person told us, written in the form they consented to.alwaysnoyesno
changed What we changed in the product because of it.alwaysnoyesno
who Who is credited. Anonymous unless the person asked to be named.alwaysnoyesno
source_interview_id Which interview it came from, so we can find it again. Never published.only when it appliesnonono
published Whether the entry is shown on the public register.alwaysnonono
created_at When the entry was written.alwaysnonono

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.

Every column of events, written by cf/functions/api/_http.js
FieldKeptEncryptedPublicIn the CSV
day The date the count belongs to. Nothing else about that day is kept.alwaysnonono
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:.alwaysnonono
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.alwaysnonono

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.

Every column of agg, written by cf/functions/api/_counters.js
FieldKeptEncryptedPublicIn the CSV
kind Which register the cached total belongs to.alwaysnonono
rows_folded How many rows went into that total, so a total that has fallen behind is spotted and recomputed rather than served.alwaysnonono
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.alwaysnonono
payload_json The public totals themselves, exactly as the register already serves them. Counts, never a person.alwaysnoyesno
updated_at When the total was last recomputed.alwaysnonono

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.

Every column of canary, written by cf/functions/api/health.js
FieldKeptEncryptedPublicIn 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.alwaysnonono
at The moment of that test write.alwaysnonono

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.

Every column of annotations, written by cf/functions/api/admin/annotations.js
FieldKeptEncryptedPublicIn the CSV
id A random id for one mark.alwaysnonono
table_name Which register the marked row is in: corrections, gap reports, survey answers or interviews.alwaysnoyesno
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.alwaysnoyesno
kind What the mark says. Only one kind exists: team-test, an answer we wrote ourselves while testing the form.alwaysnoyesno
note A short note we write when we mark a row, for our own records. Never public.only when it appliesnonono
at When we marked the row. Published as the day.alwaysnoyesno

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.

Every column of integrity_heads, written by cf/functions/api/_hash.js
FieldKeptEncryptedPublicIn the CSV
table_name Which register the head belongs to.alwaysnoyesno
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.alwaysnoyesno
row_count How many rows that register holds.alwaysnoyesno
updated_at When the head last moved. Published as the day only.alwaysnoyesno

Who runs it

Precision Federal, Ames, Iowa.

Precision Federal, Ames, Iowa. Questions: Contact.