The ledger
What Ratchet keeps in its SQLite ledger and what it never keeps, how PII is redacted, and how opt-outs, retention, erasure and export work.
The ledger is the kitchen’s recipe book, plus an order log.
The order log gets one line for every order: when it came in, who cooked it, how sure the apprentice was, and what it cost. It never says what the customer asked for.
The recipe book only gets a page when the head chef from the agency cooks a dish. The page says what was ordered, what the head chef cooked, and a note: “this chef’s contract lets you copy this recipe.” Before the page is written, a clerk blacks out phone numbers, email addresses and card numbers. (The clerk does not recognise people’s names yet, and the page says how many things were blacked out.)
The apprentice chef’s own dishes are never copied into the book. A customer can also say “please don’t keep my order”: the dish is cooked as usual, but no page is written. And a page can be torn out on request: a stub with a fingerprint stays behind, so the same recipe is never written again.
How it really works
Section titled “How it really works”One request on its way into the ledger
Section titled “One request on its way into the ledger”-
The request arrives. The gateway validates it and works out whether it may be stored at all. If the request names an end user (
userorsafety_identifier), that value is turned into a keyed hash at once (HMAC-SHA256 under the deployment’s key, scoped to the API key’s name, 16 hex digits). The raw value goes no further. The broadest storage opt-out that applies is settled here (see opt-outs below). -
The cascade runs (student first, teacher when needed; see the request journey). Whatever happens, the request gets one row in
requests: task, route, who served it, which version, confidence and the threshold it was held to, the audit and shadow flags, costs (with the student and teacher split), latency, the API key’s name, the end user’s hash, the opt-out reason and whether anything was stored. There is no message text and no answer text in this row. -
No teacher answer, nothing more. When the student served the request and the teacher was not called (no audit drawn, no shadow disagreement to settle), the request row is all that is written. The student’s text is not kept anywhere.
-
Every teacher answer goes through one function,
Ratchet._store_example. That covers escalations, cold starts, audits and shadow arbitrations. It is the only caller ofLedger.add_exampleon the request path (feedback corrections and imports have their own single writer,feedback.store_label). It asks again whether an opt-out applies, so an end user who opted out while the request waited for the teacher is still kept out. -
The answer is checked. It is skipped (served, but not stored) when its
finish_reasonis notstoportool_calls, when it is empty once reasoning is stripped, when it is not valid under the task’s spec (a missing field, a label outside the vocabulary), or when the client sent sampling that could choose the label (logit_bias, a non-zero penalty, a temperature above 1, a vendor field). Each skip writes a throttledskip_exampleevent that names the task and the reason, never the answer. -
The text is redacted. With
RATCHET_REDACT_PII=1(the default) the messages, the answer and every string in the stored conditions get[LABEL]placeholders. The answer is checked once more: if a placeholder broke a structured answer, it is skipped asinvalid after redaction. The teacher always saw the original request. -
The example is stored with its provenance: the teacher model that wrote it, that teacher’s license and license URL, the number of placeholders, the conditions it was answered under (
response_format,tools,tool_choice,stop), the client and the end user’s hash. Its golden or train split comes from a hash of the task and the redacted messages. An identical request bumpsseeninstead of adding a row (a client’s retry does not count), and a hash that an erasure tombstoned is refused. -
Later, other parts write their own rows. Night School reads
examplesevery night and writesversions(with each adapter’s bill of materials, signed when a key is set) andversion_leaves. The release gate moves versions and writesevents. Retention, erasure and receipts write their own tables.
The privacy path, in one picture
Section titled “The privacy path, in one picture”flowchart TB
A(["A chat request"]) --> B{{"Did the teacher answer?"}}
B -->|no| M[("requests row<br/>metadata only")]
B -->|yes| C{{"Opt-out, pinned<br/>or tool request?"}}
C -->|yes| M
C -->|no| D{{"Complete and valid?"}}
D -->|no| M
D -->|yes| R["Redact the messages,<br/>answer and conditions"]
R --> E[("examples row<br/>with provenance")]
E --> S{{"Split by hash"}}
S -->|"train, or seen<br/>3 or more times"| T["night/train.jsonl"]
S -->|golden| G["Golden eval only"]
class A neutral
class B,C,D,S gate
class M,E store
class R neutralReading this diagram. Start at the top with one chat request. Every path writes the metadata row; the arrows into it show the three ways a request ends there and nowhere else: the teacher never answered, an opt-out or a pinned or tool request applies, or the teacher’s answer was incomplete or invalid. Only a request that passes all three checks reaches redaction, and only then does text enter the ledger. The last split is by a hash of the redacted text, so it never depends on any model’s opinion.
The main tables
Section titled “The main tables”flowchart LR
EC["example_clients"] --> EX
TS["tombstones<br/>and erasures"] --> EX
LEAF["version_leaves<br/>committed sets"] --> EX
LEAF --> VER
FB["feedback<br/>verdicts"] --> EX
FB --> REQ
NT["no_train<br/>end-user hashes"] -.-> REQ
EX[("examples<br/>redacted text")]
REQ[("requests<br/>metadata")] --> VER[("versions<br/>adapters, BOMs")]
REQ --> RU["daily_rollup<br/>folded sums"]
REQ --> RC["receipts<br/>sealed days"]
VER -.-> EV["events<br/>release log"]
class REQ,EX,VER store
class RC,LEAF ok
class TS dangerReading this diagram. Cylinders are the three core tables, boxes the others, and arrows the column that links them (request_id, example_hash, version_id, student_version). requests is the busiest table and holds no text; examples is the only table with text, redacted. Three tables are special: version_leaves and receipts (green) can only be appended to, and tombstones (red) remembers erased hashes so they can never be stored again. Retention folds old request rows into daily_rollup, and each ledger day is sealed into receipts. Dotted arrows are not stored links: no_train is looked up for each request, and every move of a version is logged in events.
- Core table
- Append-only
- Erased hashes
- Not a stored link
The evidence tables, append-only
Section titled “The evidence tables, append-only”Two tables exist so that someone else can check Ratchet’s claims later. version_leaves holds, for each adapter, one leaf per training and golden example (the example’s hash and a hash of its label, teacher, license and redaction count). The adapter’s signed BOM carries the Merkle roots over those leaves, so a leaf can be proven to be in, or absent from, the set the adapter was trained on. receipts holds one signed savings receipt per ledger day and API client, sealed once the day is over and chained to the previous one. SQLite triggers refuse UPDATE, DELETE and a replacing INSERT on both tables, on every connection, because a changed row would silently break the roots and chains built from it. Evidence you can check shows how to verify them offline.
Human labels: feedback and imports
Section titled “Human labels: feedback and imports”Teachers are not the only source of a stored answer. Every chat response carries ratchet.request_id, and the caller can send POST /v1/ratchet/feedback with a verdict on one of its own requests (any other id is the same 404 as an unknown one).
flowchart TB
V(["A verdict on a request id"]) --> K{{"Which verdict?"}}
K -->|"correct, or incorrect<br/>with no answer"| R["Counted toward human<br/>agreement, nothing stored"]
K -->|"incorrect, with<br/>the right answer"| G{{"Opt-out, pinned task<br/>or corrections off?"}}
G -->|yes| R
G -->|no| P["Check against the spec,<br/>redact, hash"]
I(["python -m ratchet.importer"]) --> A["Attested human labels, or<br/>an open teacher's outputs"]
A --> P
P --> S[("store_label writes the<br/>example, with provenance")]
class V,I neutral
class K,G gate
class R outline
class S storeReading this diagram. A verdict is never a label: “correct” stores nothing, because that would be the student’s own answer coming back as a training target. Only a correction, a human’s own answer, can become an example, and it goes through the same checks and redaction as a teacher’s answer. The importer enters at the same point with an attestation from the operator, or with an open teacher’s outputs, and refuses closed-model families. A stronger label replaces a weaker one for the same prompt (teacher, then a client’s correction, then an operator’s import). Human corrections are stored with the provenance human; an open teacher’s imported outputs keep that teacher and its license. Verdicts feed each task’s human agreement and each version’s human precision, which are reported and alerted on, and never promote or roll back a version.
- A check
- Counted, nothing stored
- Stored as an example
Where each kind of data lives, and who can read it
Section titled “Where each kind of data lives, and who can read it”flowchart TB
A1["<b>An API key</b><br/>its own lookups,<br/>receipts, feedback"]
B1["<b>A dashboard key</b><br/>stats, cost, BOMs,<br/>redaction preview"]
C1["<b>An admin key</b><br/>erase, export, no-train,<br/>proofs, all receipts"]
D[("The ledger file, private<br/>requests: metadata<br/>examples: redacted text")]
A1 --> D
B1 --> D
C1 --> D
class D storeReading this diagram. The three groups at the top are access levels, from narrowest to widest. An API key sees only what it sent. The dashboard key reads aggregates and never writes. Only an admin key can export, erase or open proofs. All three read from the same private file, which is why the file and its backups must be protected as much as the keys.
What is stored
Section titled “What is stored”| Table | One row per | Holds text? |
|---|---|---|
requests |
Gateway call | No. Metadata only: task, route, version, confidence, threshold, costs, flags, key name, end-user hash, opt-out reason |
examples |
Distinct teacher-labelled request | Yes, redacted: the prompt, the teacher’s answer and the stored conditions, with teacher, license, license URL, split, seen, redaction count, client, end-user hash and origin |
versions |
Adapter | No. Base model, adapter name, train size, dataset hash, eval, state, traffic share and the adapter’s signed BOM |
events |
Release decision or notable event | No. A kind (trained, skip, canary, rollback, …) and a short detail; never request text or keys |
version_leaves |
Example in an adapter’s training or golden set | No. The example hash and a hash of its label, teacher, license and redaction count; append-only |
receipts |
Ledger day and API client | No. A signed savings receipt over request metadata; append-only |
spend |
Spend outside requests | No. Scout’s runs and Tavily credits keep usd 0 here (their model calls are already in requests) |
failures |
Failed call | No. The stage, the exception’s class and status, what it spent |
example_clients, no_train, tombstones, erasures, erasure_items, daily_rollup, feedback |
Governance records | No. Hashes, key names, counts and flags |
The origin of an example says where it came from. Teacher answers from the gateway are recorded without one and read as live; human labels carry feedback (a correction) or import (an attested file). Each example’s provenance travels into Night School’s rows and from there into the adapter’s signed BOM, so the license of every training target can be checked offline.
What is never stored
Section titled “What is never stored”Teacher answers and human labels only. Training targets are teacher answers or customer-attested human labels, never the student’s own answers.
| Never stored | Why | Where it is enforced |
|---|---|---|
| The student’s answers, as training targets or at all | Training on a model’s own outputs causes collapse | Only _store_example (teacher answers) and feedback.store_label (attested human labels) may call add_example; a test fails on any other caller |
| Pinned tasks’ answers | Open-ended tasks (Scout’s planner and writer) stay on the teacher for good | _teacher_only never calls add_example, and Night School leaves out rows of a task that is pinned now |
| Tool requests, multimodal requests | A tool request is answered by the teacher alone (route tools) until tool-call distillation exists |
|
| Opted-out requests | The client, its key, the task, the end user or the operator said so | Ratchet.store_opt_out, asked again before every write: opt-outs only reduce storage |
| Incomplete or invalid teacher answers | A cut-off or malformed answer would teach the wrong thing | Ratchet._store_example checks finish_reason and the task’s spec first |
| Answers written under label-altering client sampling | A client may choose its own answer, not the label everyone’s student learns | GenParams.unstorable, threat model T2 |
| Raw end-user identifiers, API keys | Privacy and key safety | Key names and keyed hashes only |
Request text in requests, failures, logs or /metrics |
Metadata is enough to run the system | requests holds metadata only; logs and metrics carry ids, names and numbers |
A fallback teacher’s answers are stored only with RATCHET_STORE_FALLBACK_TEACHER=1, and then under that model’s own license. A request for n choices stores only the teacher’s choice 0.
PII redaction
Section titled “PII redaction”ratchet/redact.py replaces identifiers with placeholders before anything is stored. It runs in a fixed order, so the specific formats with checksums win over the broad phone pattern:
| Placeholder | Matches | Extra check |
|---|---|---|
[SECRET] |
API secrets such as sk-…, AKIA…, ghp_…, tvly-…, xox…-…, and the token after Bearer |
|
[EMAIL] |
[email protected] |
|
[IBAN] |
Country code, 2 check digits, then groups | mod-97 |
[CARD] |
13 to 19 digits with optional spaces or dashes | Luhn |
[AADHAAR] |
12 digits, first digit 2 to 9 | Verhoeff |
[SSN] |
123-45-6789 style, invalid areas excluded |
|
[PAN] |
Indian PAN, ABCPE1234F style |
|
[IP] |
IPv4 addresses | |
[PHONE] |
Optional country code and area code, digit groups | 10 to 15 digits |
For example, this text:
Hi, I am Priya. Email [email protected], phone +91 98765 43210, card 4111 1111 1111 1111, order 1234.is stored as:
Hi, I am Priya. Email [EMAIL], phone [PHONE], card [CARD], order 1234.with redactions = 3. The name stays: names and street addresses are not detected yet. That needs a named-entity model (Presidio is the planned option), which would add a large dependency and latency, so v0 uses patterns. Redaction covers message content (strings and text parts), name, an assistant’s refusal, tool and function call arguments, and every string value in the stored conditions. It is on by default; RATCHET_REDACT_PII=0 stores examples verbatim. You can test it on your own text with POST /ratchet/redact or the dashboard’s Try the redactor box; nothing is stored by the preview.
By default a placeholder is just the label ([EMAIL], RATCHET_REDACT_STYLE=label). Extraction tasks need to know which of two addresses an answer names, so a task can ask for another style (privacy.redact_style in its spec, or the setting for all tasks): numbered writes [EMAIL_1] and [EMAIL_2], one number per distinct value across the messages, the conditions and the answer; surrogate writes reserved look-alike values such as [email protected]. Indian GSTINs and UPI ids are matched too (as [GSTIN] and [UPI]), and RATCHET_REDACT_PLUGIN (module:function) can add a detector for what patterns cannot find, such as names. If the plugin fails when an example is being stored, nothing is stored.
Two consequences worth knowing. The example hash, the golden split and the frequent-request count are computed on the redacted text, so two requests that differ only in an email address become one example. And the student learns to write a placeholder where the teacher repeated an identifier from the request; for label tasks that does not matter, for free-text replies it can.
Storage opt-outs and no-train end users
Section titled “Storage opt-outs and no-train end users”Five opt-outs, broadest first. The first one that applies is logged in requests.store_opt_out and returned in the response as ratchet.store_opt_out:
| Reason | Set by |
|---|---|
global |
RATCHET_STORE_EXAMPLES=0 (zero retention; Night School then skips every night) |
task |
store: false in the task’s spec, or RATCHET_NO_STORE_TASKS |
key |
store=0 for the key’s name in RATCHET_KEY_LIMITS |
request |
store: false, the header X-Ratchet-Store: none (or 0, false), or metadata.ratchet_store set to "false" (or "0") |
end_user |
A no-train registration: POST /v1/ratchet/no-train with {"end_user": "..."} |
An opted-out request is handled exactly like any other: the student may serve it, audits still run and shadows are still arbitrated, so release decisions never depend on what was stored. Only the write to examples is skipped, and a duplicate does not bump seen. Nothing in a request can undo an opt-out: store: true is the default spelled out. An unknown header value is a 400, so a typo cannot store by accident. Ratchet’s default is to store, the opposite of OpenAI’s store default, because distilling is its purpose.
A no-train registration keeps only the hash, scoped to the caller’s API key name, so the same identifier from two clients is two people as far as the ledger can tell. An admin key can register an end user for every client at once (scope *), which also covers keys added later. DELETE /v1/ratchet/no-train removes a registration (an API key cannot remove one an admin made). Events record counts and names, never the end user or its hash.
Retention, tombstones, erasure and export
Section titled “Retention, tombstones, erasure and export”Opt-outs keep new data out; these remove or hand over what is there.
- Tombstones, not deletes. A purge sets an example’s
messages,completionandparamsto NULL and keeps the row: hash, task, split,seen, teacher, license and client stay. That matters because Night School’s rules read more than text: the frequent-request count addsseenover every variant of a prompt, and the new-data count compares with past versions. A purged row is in neither training set, but itsseenstill counts, so a purge never moves another variant of a frequent request back to golden. - Retention windows.
RATCHET_EXAMPLE_RETENTION_DAYSpurges examples not seen again for that many days (reasonretention; a later storable duplicate brings the text back).RATCHET_REQUEST_RETENTION_DAYSfolds old request rows intodaily_rollupsums and deletes them; every statistic adds the sums back, so figures stay the same except the count of distinct end users. Rows that a release in flight still needs as evidence are never folded. Both default to 0 (keep forever). Retention runs at most hourly in the live gateway on a background thread, at the start of every live night, and on demand withpython -m ratchet.live purge. - Erasure is final.
privacy.eraseselects byclient,end_user,hashesorcontains(each narrows the scope), purges the text and the end-user hash, and writes each hash totombstones:add_examplerefuses it from then on, from any client. The record (erasures,erasure_items, aneraseevent) holds hashes, counts, names and a redacted reason, never text.RATCHET_ERASURE_SHAREDdecides what happens to an example another client also sent (delete, the default, orkeepand unlink). Admin erasures are dry runs unless asked otherwise. - Adapters are reported, never edited. An erasure lists
affected_versions: exactly, from a version’s committed training set when it has one; otherwise every version created after the example was stored while it counted as a training row. Their signed BOMs stay as written. Ratchet cannot remove what an existing adapter memorised; the next night trains without the purged rows, and retiring an affected version is the operator’s decision. - Export.
GET /ratchet/admin/export/{kind}(admin key) orpython -m ratchet.admin exportstreams NDJSON: examples that still have text with their provenance and every client that sent them, request metadata, versions with their BOMs, events, erasures. End-user hashes are left out.
An erased example goes through a short life cycle:
stateDiagram-v2
[*] --> Stored: a teacher answer
Stored --> Purged: retention
Purged --> Stored: a duplicate
Stored --> Erased: erased by client,<br/>end user, hash or text
Purged --> Erased: erased
note right of Erased
add_example refuses
the hash from any client
end note
class Stored ok
class Purged pending
class Erased dangerReading this diagram. A purge is reversible in one way only: if the same request arrives again and may be stored, its text comes back. An erasure is not. From the moment the hash is a tombstone, no client can store it again, and the row keeps only hashes and counts. The adapters that may have learned from it are reported, but their signed BOMs are never rewritten.
- Text kept
- Text purged, may come back
- Erased for good
What can go wrong
Section titled “What can go wrong”| Failure | What the system does | Where you see it |
|---|---|---|
| A teacher answer is cut off, empty or invalid for the task | Served, not stored | skip_example event, requests.stored = 0 |
| A placeholder breaks a structured answer | Served, not stored | skip_example with invalid after redaction |
| The redaction plugin raises at store time | Nothing is stored for that request | skip_example, the request still succeeds |
| An erased request is sent again | Served as usual, never stored | requests.stored = 0 |
| A name or street address is in the text | It is stored as written (not detected by patterns) | Use an opt-out, a retention window or a detector plugin |
| Two gateway processes share one ledger | Not supported; caches and the gate are per process | Run one worker |
Power cut with RATCHET_DB_SYNC=NORMAL |
The last commits may be lost, the file is intact | Use FULL if that matters |
| Something tries to change a leaf or a receipt | The trigger refuses it | A SQLite error |
SQLite, WAL and one process
Section titled “SQLite, WAL and one process”The ledger is a single SQLite file (RATCHET_DB, default ratchet.db; mock mode uses an in-memory ledger) behind the Ledger class. Every write commits at once. One re-entrant lock serialises writes, because Scout’s agents call the router from several threads over one connection.
A file ledger runs in SQLite’s WAL journal with synchronous=NORMAL (RATCHET_DB_SYNC=NORMAL, the default). Readers no longer block the writer, dashboard reads use a second, read-only connection, and the gateway and a python -m ratchet.live cron job on the same file only contend writer against writer for one commit. The price: a power cut or OS crash can lose the last commits (the file is not corrupted). WAL needs every process on the same host with the file on a local disk; never put RATCHET_DB on the bucket mount or another network filesystem (if you must, set RATCHET_DB_SYNC=FULL for every process).
Backups
Section titled “Backups”The ledger is the source of truth for provenance, versions and training data. Back it up daily with SQLite’s backup API, never cp: in WAL mode recent commits live in ratchet.db-wal until a checkpoint, and a copy of the main file alone misses them.
sqlite3 /var/lib/ratchet/ratchet.db ".backup '/var/backups/ratchet-$(date +%F).db'"On the demo VM, deploy/vm/backup.sh does the same with an integrity check, keeps 14 copies and can upload them to Object Storage. To restore: stop the gateway and cron jobs, remove any ratchet.db-wal and ratchet.db-shm, copy the backup over RATCHET_DB, start the gateway, then reload the live adapter. A backup taken before an erasure still holds the erased text, so run every later erasure again after a restore. See the runbook.
For engineers
Section titled “For engineers”| File | What to read |
|---|---|
ratchet/ledger.py |
SCHEMA (every table), MIGRATIONS, INDEXES; Ledger.add_example (dedup, split, tombstone refusal, retries), _split, log_request, purge_examples, record_erasure, fold_requests, export_page, bill_of_materials |
ratchet/router.py |
Ratchet._store_example (the checks, redaction and provenance), Ratchet.store_opt_out |
ratchet/redact.py |
PATTERNS, redact, redact_messages, redact_json |
ratchet/privacy.py |
OPT_OUTS, EndUsers.hash, erase, apply_retention, export_lines |
ratchet/feedback.py |
store_label, the only writer of human and imported labels |
Schema changes add the column to SCHEMA and MIGRATIONS; an old file is migrated on open. version_leaves and receipts are append-only: triggers refuse UPDATE, DELETE and a replacing INSERT on every connection.
Settings
Section titled “Settings”| Variable | Default | Meaning |
|---|---|---|
RATCHET_DB |
ratchet.db |
The ledger file (live mode) |
RATCHET_DB_SYNC |
NORMAL |
NORMAL: WAL with synchronous=NORMAL. FULL: rollback journal with synchronous=FULL |
RATCHET_GOLDEN_FRACTION |
0.10 |
Share of new examples assigned to the golden split (above 0, at most 0.5) |
RATCHET_REDACT_PII |
1 |
Redact before storing; 0 stores verbatim |
RATCHET_REDACT_STYLE |
label |
label, numbered or surrogate placeholders |
RATCHET_REDACT_PLUGIN |
empty | module:function detector for names and addresses |
RATCHET_STORE_EXAMPLES |
1 |
0 is zero retention: nothing stored, Night School skips |
RATCHET_NO_STORE_TASKS |
empty | Comma-separated tasks whose answers are never stored |
RATCHET_KEY_LIMITS |
empty | Per-key settings by key name; store=0 keeps a key’s requests out |
RATCHET_END_USER_KEY |
empty | HMAC key for end-user hashes (16+ characters). Empty: a random key is generated once and kept in the ledger’s meta table |
RATCHET_EXAMPLE_RETENTION_DAYS |
0 |
Days an example is kept after it was last seen; 0 keeps it |
RATCHET_REQUEST_RETENTION_DAYS |
0 |
Days a request row is kept before folding; 0 keeps it |
RATCHET_ERASURE_SHARED |
delete |
delete or keep for examples another client also sent |
RATCHET_SEEN_DEDUP_S |
300 s live, 0 mock | A repeat from the same client within this window counts once toward seen |
RATCHET_STORE_FALLBACK_TEACHER |
0 |
1 stores a fallback teacher’s answers, under its own license |
RATCHET_FEEDBACK_CORRECTIONS |
1 |
0 keeps verdicts but stores no human correction |
Full list: Configuration, privacy and security.
Endpoints
Section titled “Endpoints”| Endpoint | Access | What |
|---|---|---|
GET /ratchet/bom |
admin or dashboard key in live mode; public with RATCHET_PUBLIC_BOM=1 |
Ledger-wide bill of materials: counts by teacher, license and split, examples_sha256, purged_examples |
GET /ratchet/versions/{id}/bom |
as above | One adapter’s signed BOM |
POST /ratchet/redact |
dashboard | Preview redaction; stores nothing |
POST and DELETE /v1/ratchet/no-train |
API key (admin for every client) | Register or remove a no-train end user |
DELETE /v1/ratchet/examples |
API key (admin for any client) | Erase one end user’s stored examples |
POST /ratchet/admin/privacy/erase |
admin | Erase by client, end user, hashes or text; dry run by default |
GET /ratchet/admin/export/{kind} |
admin | NDJSON export of examples, requests, versions, events or erasures |
Details: End users and storage and Erasure and export.
python -m ratchet.admin erase --reason "contract ended" --client acme # dry run: counts and affected versionspython -m ratchet.admin erase --reason "contract ended" --client acme --apply # do itpython -m ratchet.admin export examples --client acme --out acme.ndjsonpython -m ratchet.live purge --dry-run # what the retention windows would removeEvents
Section titled “Events”skip_example (a teacher answer not stored, with the reason), no_train, no_train_unmatched, end_user_key, erase, purge, purge_failed. Meanings are in the runbook.
Common questions
Section titled “Common questions”Does Ratchet keep my users’ prompts?
Section titled “Does Ratchet keep my users’ prompts?”Only the prompts of requests the teacher answered (an escalation, a cold start, an audit or an arbitration), only when no opt-out applies, and only after redaction. Requests the student served without an audit leave a metadata row and nothing else.
Is the student’s answer ever stored?
Section titled “Is the student’s answer ever stored?”No. Not as a training target, and not as text anywhere. Training on the student’s own outputs would make it collapse onto its own mistakes.
What about names and street addresses?
Section titled “What about names and street addresses?”They are not caught by the redactor yet. If that matters for your traffic, use an opt-out for those requests or tasks, set a retention window, and erase anything that leaked (PII leak playbook). Named-entity detection is planned.
A customer asks us to delete their data. Can we?
Section titled “A customer asks us to delete their data. Can we?”Yes. Erase by client or end user; the text is gone for good and its hash can never be stored again. The adapters that may have been trained on it are listed, but Ratchet cannot make an existing adapter forget. The next night trains without the rows, and taking an affected version out of service is your decision. Backups made before the erasure still hold the text.
Can I run two gateway workers on one ledger?
Section titled “Can I run two gateway workers on one ledger?”No. Run one gateway process with one worker. Night School’s cron job may share the file on the same host. A Postgres ledger for several workers is planned, not built.
Does request retention change my statistics?
Section titled “Does request retention change my statistics?”No, except the count of distinct end users. Folded rows become daily sums that every statistic adds back, and rows a release in flight still needs are never folded.
Can someone tell whether a text was stored or erased?
Section titled “Can someone tell whether a text was stored or erased?”Not from a live gateway’s public endpoints: the training-set proof endpoints need an admin key, and a keyless call to the erasure endpoint gets its counts as null. Whoever holds both the database file and a candidate text can test it against the stored hashes, which is also what makes erasures checkable. Keep the file and its backups private, and set RATCHET_END_USER_KEY outside the file.
Related
Section titled “Related”Where the teacher answers in the ledger come from, one request at a time.
More detail: the threat model and the architecture’s data model.