Skip to main content

Student Privacy

Use student-privacy for a narrow, deterministic check around common student-record terms and student-ID phrases. It can block a request or mark it for redaction, and it has limited non-streaming response handling.

This policy is not a FERPA or COPPA compliance engine. It does not verify identity, parental consent, school context, jurisdiction, or the legal status of a record.

Outcome

Input evaluation

ConditionVerdictReason code
No student markerallowstudent_privacy.clean
Marker found, default action: redactredactstudent_privacy.redact
Marker found with action: blockblockstudent_privacy.block
Marker found with an under-13 age and age_gate: trueblockstudent_privacy.age_gate

Input details contain action, age_gate, under_13, the first matching keyword_hit if any, and student_id_like.

Output handling

Output behavior depends on action and response transport:

ConfigurationCurrent non-streaming behavior
action: redactThe gateway's response-redaction pass replaces student-ID-like patterns in supported OpenAI JSON fields.
action: blockThe gateway blocks supported responses containing student id, transcript, ferpa, or a student ID/identifier/number phrase with student_privacy.block.

An output block returns an HTTP 400 policy-violation response instead of the provider body.

Prerequisites

  • Add student-privacy to the effective global or route chain.
  • Choose block when delivery must stop on any detected input marker.
  • Use redact only after testing the exact identifiers you expect the gateway to replace.
  • Combine it with pii-detector or dlp-filter when broader personal or institutional data coverage is required.

Configuration

pack:
name: student-privacy-example-1
version: "1.0.0"
enabled: true

policies:
chain:
- student-privacy

policy:
student-privacy:
action: redact
age_gate: true

Supported fields

FieldTypeDefaultNotes
actionstringredactredact or block.
age_gatebooleanfalseWhen enabled, a detected student marker is forced to block if the first matching inline age expression is from 1 through 12.

Exact input detection

The evaluator joins message text and performs case-insensitive checks for:

DetectorValues
Fixed termsstudent id, student_id, transcript, iep, 504 plan, grade, gpa, disciplinary, ferpa
ID phrasestudent id, student identifier, or student number, optional :, #, or -, then at least four letters, digits, or hyphens
Inline ageage N or N years old, where N has one or two digits

The age expression does not trigger the policy by itself. It changes the action only when a fixed term or student-ID-like pattern also matched.

Examples:

TextDefault resultWith age_gate: true
Help an age 12 learner with fractions.allowallow; age alone is not a marker
Student ID: AB-1234redactredact
Student ID: AB-1234, age 12redactblock with student_privacy.age_gate
Send the transcript.redactredact

age_gate reads message text only. It does not consult account profiles, headers, verified dates of birth, or consent records, and it has no separate under-18 tier.

Redaction boundary

The input evaluator returns a redaction verdict for any fixed-term or ID match. The subsequent gateway redaction pass has one student-specific pattern: the student ID/identifier/number expression with an identifier of at least four characters.

Consequently:

  • Student ID: AB-1234 can be replaced by the redaction pass.
  • A keyword-only phrase such as send the transcript produces a redaction verdict but has no student-specific replacement target by itself.
  • The policy result does not expose redaction targets; applied replacements are reported by the gateway redaction path.

Do not interpret a redact verdict as proof that every student-related word was removed. Verify the returned/forwarded payload.

For non-streaming output redaction, the same student-ID pattern is added to the gateway response redactor. If it changes a supported response, the observable gateway result uses nested reason pii.detected and final reason redact.applied, even though the selected policy kind can be student-privacy.

Output boundary

The output block detector is intentionally narrower than input detection. It does not currently check iep, 504 plan, grade, gpa, or disciplinary.

Streaming behavior also differs:

  • action: redact enables buffered streaming redaction for matching student-ID patterns.
  • action: block is not evaluated against accumulated SSE output by the current streaming path.

If output blocking is required, use non-streaming requests and verify stream: false in the client. Input blocking still occurs before either streaming or non-streaming provider calls.

Test input behavior

Create tests/blocks-under-13-student-record.json:

{
"name": "blocks-under-13-student-record",
"input": {
"messages": [
{
"role": "user",
"content": "Student ID: AB-1234 and age 12."
}
]
},
"expected": {
"verdict": "block",
"reason_code": "student_privacy.age_gate"
}
}

Use this config while running that fixture:

policy:
student-privacy:
action: redact
age_gate: true

Then run:

kt policy lint --file policy-config.yaml
kt policy test --json

Add at least these companion cases:

  • clean education content -> allow / ok;
  • a student ID at age 13 -> redact / redact.applied;
  • a keyword-only transcript request -> redact / redact.applied; and
  • the same marker with action: block -> block / student_privacy.block.

kt policy test exercises the input evaluator. Verify response redaction, response blocking, and streaming behavior through a running gateway.

Rollout checklist

  1. Inventory the actual student identifiers and record terms in your traffic.
  2. Decide whether a marker should block or merely enter the redaction path.
  3. Add positive, negative, age-gated, and substring false-positive fixtures.
  4. Test non-streaming responses for both redaction and block behavior.
  5. Test stream: true separately if the application enables it.
  6. Deploy to a limited route or gateway and correlate representative request IDs with kt events tail --since 10m --json.
  7. Review false positives around broad substrings such as grade and gpa.

Troubleshooting

SymptomLikely causeResolution
An age-12 request was allowedNo student marker matched, age_gate was off, or a different earlier age expression was parsed.Include a supported marker and test the exact message text.
A transcript request says redact but text is unchangedKeyword detection caused the verdict, but the student redactor only has an ID-like replacement pattern.Use block if the whole request must stop, or add a separately supported DLP/PII control.
Output with grade was not blockedOutput block detection uses a smaller marker set.Test against the documented output terms or use another supported output control.
Streamed output was not blockedaction: block is not run against current SSE output.Require stream: false for output blocking.
Policy never appears in resultsIt is not in the effective chain or is skipped by conditions/targeting.Inspect the applied route chain and targeting context.
Team expects FERPA/COPPA certificationThe policy is a deterministic text control only.Complete the broader legal, consent, identity, retention, and operational controls outside this policy.

Next steps