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