Sensitive data
Every field the Advocate keeps is classified, and every build proves where the personal, sensitive and secret ones
actually go. A unique marker is planted in each such field; a student then uses the app by text, on the web,
through the API, with a mentor introduction sent and a webhook registered; and every place data can land is
searched for every marker. A marker found anywhere its class may not go fails the sensitive-data gate, naming
the field and the place. The latest run is on the factory console's Sensitive data trace page.
The classes
| Class |
Means |
Fields |
| public |
Published facts anyone may read |
14 |
| internal |
Ids, times, statuses and settings that say nothing about the student alone |
62 |
| personal |
Identifies or describes the student: name, phone, schedule, what they wrote |
55 |
| sensitive |
Special-category: money, benefits, family, identity, which help they need, sealed facts |
13 |
| secret |
Credentials: stored only hashed or encrypted, never shown |
1 |
pie showData title Fields by class
"public" : 14
"internal" : 62
"personal" : 55
"sensitive" : 13
"secret" : 1
Where each class may go
flowchart LR
P[personal]:::p
S[sensitive]:::s
X[secret]:::x
k0[store]
k1[sealed store]
k2[consent ledger]
k3[evidence]
k4[student export]
k5[student API]
k6[student screens]
k7[texts to the student]
k8[calendar feed]
k9[sent with approval]
k10[network: campus services]
k11[network: webhooks]
k12[app admin]
k13[outcomes]
k14[server logs]
P --> k0
S --> k0
P --> k4
S --> k4
P --> k5
S --> k5
P --> k6
S --> k6
P --> k7
S --> k7
P --> k8
P --> k9
X -. hashed or encrypted only .-> k0
classDef p fill:#dbeafe,stroke:#1d4ed8
classDef s fill:#fde68a,stroke:#b45309
classDef x fill:#fecaca,stroke:#b91c1c
| Place |
Personal |
Sensitive |
Secret |
| store |
yes |
yes |
no |
| sealed store |
no |
no |
no |
| consent ledger |
no |
no |
no |
| evidence |
no |
no |
no |
| student export |
yes |
yes |
no |
| student API |
yes |
yes |
no |
| student screens |
yes |
yes |
no |
| texts to the student |
yes |
yes |
no |
| calendar feed |
yes |
no |
no |
| sent with approval |
yes |
no |
no |
| network: campus services |
no |
no |
no |
| network: webhooks |
no |
no |
no |
| app admin |
no |
no |
no |
| outcomes |
no |
no |
no |
| server logs |
no |
no |
no |
A field may declare one more place for itself, with a reason: the student's first name and the note they approved
go to the mentor platform; their class-site token goes to the class site; their webhook address receives webhooks.
How it stays out
flowchart TB
in[A fact comes in] --> q{Class}
q -- personal or sensitive --> store[(App store, owned by the student)]
q -- sealed fact --> sealed[(Sealed store: encrypted per student, opened only by granted identities)]
q -- secret --> hash[Hashed or encrypted; never shown, not even in the student's export]
store --> own[Back to the student: screens, texts, export, API]
store -. approved exact text only .-> out[Sent with approval]
store -. never .-> ev[(Evidence: keyed-hash subjects, no content)]
store -. never .-> led[(Consent ledger: short hashes, never labels or names)]
store -. never .-> adm[App admin and outcomes: counts of five or more only]Personal, sensitive and secret fields
| Field |
Class |
Note |
students.phone |
personal |
|
students.chosenName |
personal |
|
students.pronouns |
sensitive |
|
students.zip |
personal |
|
students.county |
personal |
|
students.age |
personal |
|
students.goal |
personal |
|
students.household |
sensitive |
size, income, children |
students.enrollment |
personal |
college, credits, work hours |
students.receives |
sensitive |
benefits already received |
students.helpAreas |
sensitive |
|
students.communities |
sensitive |
chosen, never inferred |
deadlines.title |
personal |
|
deadlines.due |
personal |
|
deadlines.course |
personal |
|
deadlines.source |
personal |
where it was found, with the line |
classes.course |
personal |
|
classes.days |
personal |
|
classes.start |
personal |
|
classes.end |
personal |
|
classes.building |
personal |
|
classes.room |
personal |
|
classes.campus |
personal |
|
shifts.date |
personal |
|
shifts.start |
personal |
|
shifts.end |
personal |
|
shifts.employer |
personal |
|
holds.title |
personal |
|
holds.date |
personal |
|
holds.start |
personal |
|
holds.end |
personal |
|
reminders.about |
personal |
|
reminders.at |
personal |
|
reminders.text |
personal |
|
drafts.to |
personal |
|
drafts.recipient |
personal |
|
drafts.body |
personal |
|
messages.text |
personal |
|
playbook_runs.playbook |
sensitive |
|
playbook_runs.history |
sensitive |
|
api_tokens.label |
personal |
|
webhooks.url |
personal |
|
webhooks.secret |
secret |
signs each delivery |
vault.name |
sensitive |
|
money.balance |
sensitive |
|
money.bills |
sensitive |
|
money.pay |
sensitive |
|
household_plan.childCare |
sensitive |
|
household_plan.commuteMin |
personal |
|
household_plan.sleepHours |
personal |
|
household_plan.commuteMode |
personal |
bus, car, walk |
household_plan.homeStop |
personal |
transit stop id the student picked |
commute_plans.date |
personal |
|
commute_plans.course |
personal |
|
commute_plans.classStart |
personal |
|
commute_plans.from |
personal |
stop id |
commute_plans.to |
personal |
stop id |
commute_plans.itineraries |
personal |
|
transit_now.stop |
personal |
|
dispatches.request |
personal |
|
dispatches.result |
personal |
|
meetings.title |
personal |
|
meetings.date |
personal |
|
meetings.start |
personal |
|
meetings.attendees |
personal |
|
meetings.course |
personal |
|
courses.course |
personal |
|
dismissed.subject |
personal |
|
aid.disbursement |
sensitive |
|
Sealed facts
| Fact |
Class |
Why it is sealed |
justice_involvement |
sensitive |
Justice history; used only for the fair-chance path when the student asks. |
foster_care_history |
sensitive |
Foster care history; opens foster-youth aid. |
class_site_token |
secret |
The student's class-site login token; granted only to the class-site sync. |
doc:<keyed hash of the name> |
sensitive |
The contents of documents the student keeps; the name itself stays out of the sealed store and the ledger. |
The record, by sensitivity
Each field's class is shown beside it. A collection is as sensitive as its most sensitive field.
| Collection |
Most sensitive field |
students |
sensitive |
deadlines |
personal |
classes |
personal |
shifts |
personal |
holds |
personal |
reminders |
personal |
drafts |
personal |
messages |
personal |
last_steps |
internal |
playbook_runs |
sensitive |
pulse_state |
internal |
pulse_pending |
internal |
sources |
internal |
api_tokens |
personal |
webhooks |
secret |
vault |
sensitive |
money |
sensitive |
household_plan |
sensitive |
commute_plans |
personal |
transit_now |
personal |
dispatches |
personal |
campus_feeds |
internal |
leases |
internal |
meetings |
personal |
courses |
personal |
dismissed |
personal |
aid |
sensitive |
student
erDiagram
students {
string id PK "internal"
string phone "personal"
string chosenName "personal"
string pronouns "sensitive"
string zip "personal"
string county "personal"
number age "personal"
string goal "personal"
HelpDial dial "internal"
Household household "sensitive"
Enrollment enrollment "personal"
object receives "sensitive"
string_list helpAreas "sensitive"
string_list communities "sensitive"
string account "internal"
date createdAt "internal"
}schedule
erDiagram
deadlines {
string id PK "internal"
string studentId "internal"
string title "personal"
date due "personal"
string course "personal"
enum kind "internal"
object source "personal"
boolean confirmed "internal"
number effortHours "internal"
number durationMin "internal"
boolean done "internal"
}
classes {
string course "personal"
weekday_list days "personal"
time start "personal"
time end "personal"
string building "personal"
string room "personal"
string campus "personal"
}
shifts {
date date "personal"
time start "personal"
time end "personal"
string employer "personal"
}
holds {
string id PK "internal"
string deadlineId "internal"
string title "personal"
date date "personal"
time start "personal"
time end "personal"
}
reminders {
string id PK "internal"
string about "personal"
date at "personal"
string text "personal"
}
meetings {
string id PK "internal"
string title "personal"
date date "personal"
time start "personal"
string_list attendees "personal"
string course "personal"
}
courses {
string course "personal"
enum aiPolicy "public"
string aiPolicyLine "public"
}
deadlines ||--o{ holds : "gets time in"conversation
erDiagram
drafts {
string id PK "internal"
string studentId "internal"
string to "personal"
string recipient "personal"
string body "personal"
string purpose "internal"
enum status "internal"
}
messages {
string id PK "internal"
string studentId "internal"
string author "internal"
string text "personal"
NextStep_list nextSteps "internal"
enum urgency "internal"
string why "internal"
date_time at "internal"
enum status "internal"
string holdReason "internal"
}
last_steps {
NextStep_list steps "internal"
}
pulse_state {
record asked "internal"
number everyDays "internal"
boolean off "internal"
}
pulse_pending {
string pulse "internal"
}
dismissed {
string subject "personal"
date at "internal"
}help and money
erDiagram
playbook_runs {
string playbook "sensitive"
string studentId "internal"
string stepId "internal"
enum status "internal"
date startedAt "internal"
object_list history "sensitive"
date waitingSince "internal"
}
vault {
string name "sensitive"
date addedAt "internal"
}
money {
number balance "sensitive"
Bill_list bills "sensitive"
Paycheck pay "sensitive"
}
household_plan {
ChildCare childCare "sensitive"
number commuteMin "personal"
number sleepHours "personal"
enum commuteMode "personal"
string homeStop "personal"
}
aid {
date disbursement "sensitive"
}connections
erDiagram
sources {
enum source "internal"
date connectedAt "internal"
}
api_tokens {
string id PK "internal"
string label "personal"
enum_list scopes "internal"
date createdAt "internal"
}
webhooks {
string id PK "internal"
url url "personal"
string_list events "internal"
string secret "secret"
}campus
erDiagram
commute_plans {
date date "personal"
string course "personal"
time classStart "personal"
string from "personal"
string to "personal"
Itinerary_list itineraries "personal"
string notice "public"
date_time fetchedAt "internal"
}
transit_now {
string stop "personal"
date_time at "internal"
Arrival_list arrivals "public"
Alert_list alerts "public"
}
dispatches {
string id PK "internal"
string studentId "internal"
enum kind "internal"
object request "personal"
string key "internal"
enum status "internal"
number attempts "internal"
object result "personal"
date_time queuedAt "internal"
}
campus_feeds {
string campus PK "public"
date_time fetchedAt "internal"
DiningLocation_list locations "public"
record hours "public"
record menus "public"
Space_list spaces "public"
Event_list events "public"
Posting_list postings "public"
Mentor_list mentors "public"
Stop_list stops "public"
string_list unknown "internal"
}
leases {
string job PK "internal"
string holder "internal"
date_time until "internal"
number beats "internal"
}Verified by