Guardian Review Technical Specification¶
- Status: complete beta golden path implemented through 2026-07-18
- Assessment schema:
1.1.0 - Prompt version:
guardian-review-v2 - Redaction version:
guardian-review-redaction-v3
Purpose and boundary¶
Guardian Review is an optional parent-requested second opinion about an existing local GuardianNode alert. It explains uncertainty and helps a parent begin a calm, safety-conscious conversation. It does not replace local detection, silently upload evidence, automatically punish or block a child, make a legal or medical determination, or operate as an emergency service.
Current golden path¶
The existing path is:
agent-windows/src/main.pycaptures a visible frame and callsBackendClient.send_screenshot.POST /api/events/screenshotauthenticates the paired device, encrypts a pending frame, persists a receipt, and queues local classification.screenshot_asyncdecrypts the pending frame for the worker and callsscreenshot_ingest.- Tesseract, deterministic rules, policy, and configured local Ollama models produce normalized risk data. Events, risk results, encrypted evidence, and alerts are persisted.
- The React dashboard loads
/api/alertsand/api/alerts/{id}, reveals encrypted evidence only on request, and records existing review/feedback.
Guardian Review extends step 5 with a local preview, explicit consent, an asynchronous review job, strict structured result display, and separate parent feedback.
flowchart LR
A[Windows agent] -->|paired device token; screenshot| B[Local backend]
B --> C[Local detectors and models]
C --> D[(Encrypted evidence store)]
C --> E[(Alert and risk records)]
E --> F[Parent dashboard]
D -->|parent reveal only| F
F -->|guided optional context and evidence IDs| G[Deterministic redaction and minimization v3]
G -->|exact outbound JSON; local only| F
F -->|cancel; send nothing| N[Delete unconsumed preview]
F -->|unchecked consent + explicit continue| H[Guardian Review service]
H --> K{Configured provider}
K -->|store=false; ZDR required; no tools| I[OpenAI Responses API]
K -->|offline| M[Deterministic mock]
I -->|strict assessment v1.1| H
M -->|strict assessment v1.1| H
H --> F
F -->|delete local assessment| O[Scrub encrypted preview and result]
B --> J[(Audit log)]
F --> J
G --> J
H --> J
Strict assessment schema¶
The normative machine-readable contract is
shared/schemas/guardian_review_assessment_v1.json. Every property is required,
every object rejects additional properties, strings and arrays are bounded, and
empty arrays represent unavailable guidance. No prose outside the schema is
accepted.
Schema 1.1.0 explicitly separates observed_facts from inferences. The
primary category reuses the backend canonical taxonomy and adds none for
a likely-benign result. The schema includes:
- Schema version, assessment, category, severity, and confidence.
- Plain-language summary and supporting evidence with local opaque evidence IDs.
- Possible benign explanations, missing context, and parent questions.
- Controlled parent tone plus suggested opening language and child questions.
- Phrases/approaches to avoid, immediate actions, and follow-up actions.
- Escalation indicators and explicit limitations.
The model must never create policy actions directly. The parent chooses any next step after reviewing the result and original evidence.
DTOs¶
Preview input¶
{
"relationship_context": "unknown_person",
"repeated_behavior": "unknown",
"parent_believes_immediate_danger": false,
"parent_goal": "prepare_conversation",
"parent_goal_details": null,
"parent_context": "Optional context known to the parent, maximum 1500 outbound characters.",
"selected_evidence_ids": ["opaque-local-evidence-id"],
"include_evidence": true,
"include_age_group": true,
"fresh_assessment": false
}
At most 20 evidence IDs are accepted. The server verifies that every ID belongs to the alert. Alert severity, categories, device/profile data, and evidence text are derived server-side rather than trusted from the browser.
Preview output¶
{
"preview_id": "opaque-preview-id",
"alert_id": "opaque-alert-id",
"provider": "openai",
"model_requested": "gpt-5.6",
"schema_version": "1.1.0",
"prompt_version": "guardian-review-v2",
"redaction_version": "guardian-review-redaction-v3",
"outbound_payload": {},
"preview_digest": "64-lowercase-hex-sha256",
"field_count": 0,
"character_count": 0,
"redactions_applied": ["email"],
"information_categories": ["local_detector_findings"],
"external_processing": true,
"disclosure": "This exact preview will be sent to an external OpenAI model.",
"retention_notice": "Provider-specific parent disclosure.",
"expires_at": "RFC3339 timestamp"
}
outbound_payload is the exact canonical JSON proposed for transmission, not a
summary. The digest covers that JSON plus schema, prompt, redaction, provider,
and model versions. Previews expire after 15 minutes. Re-minimization or any
input/version/configuration change invalidates the digest.
Submit input and accepted output¶
{
"preview_id": "opaque-preview-id",
"preview_digest": "64-lowercase-hex-sha256",
"consent": true
}
Successful submission returns HTTP 202:
{
"review_id": "opaque-review-id",
"status": "queued",
"status_url": "/api/guardian-reviews/opaque-review-id"
}
The server regenerates the preview and compares digests in constant time; it does not accept client-supplied outbound JSON.
Result output¶
{
"review_id": "opaque-review-id",
"alert_id": "opaque-alert-id",
"status": "completed",
"mode": "mock",
"created_at": "RFC3339 timestamp",
"completed_at": "RFC3339 timestamp",
"schema_version": "1.1.0",
"prompt_version": "guardian-review-v2",
"redaction_version": "guardian-review-redaction-v3",
"model_requested": "gpt-5.6",
"model_returned": null,
"assessment": {}
}
Status is queued, running, completed, failed, or deleted. assessment
is present only when completed; a sanitized error is present only when failed.
A deleted record retains only non-content metadata and deleted_at.
Feedback input¶
{"labels": ["helpful", "missing_context"]}
Labels are selected from helpful, inaccurate, too_alarmist,
too_dismissive, missing_context, and needs_follow_up. Feedback remains
local, is stored with the assessment/schema/prompt versions, and never becomes
an automatic follow-up model request or single-event training signal.
API and authorization¶
| Route | Behavior | Authorization |
|---|---|---|
GET /api/guardian-review/providers |
Non-secret provider/readiness status | Parent session |
POST /api/guardian-review/providers/codex/device-login |
Fail-closed security-hold response | Parent session + CSRF + critical step-up |
POST /api/alerts/{alert_id}/guardian-review/preview |
Local minimization only; no external call | Parent/admin session + CSRF |
POST /api/alerts/{alert_id}/guardian-review |
Validate digest/consent and enqueue | Parent session + CSRF + recent step-up |
DELETE /api/guardian-review/previews/{preview_id} |
Cancel and delete an unconsumed local preview; no provider call | Requesting parent/admin + CSRF |
GET /api/guardian-reviews |
Sanitized global or per-alert history | Requesting parent/admin |
GET /api/guardian-reviews/{review_id} |
Poll job/result | Requesting parent or admin |
DELETE /api/guardian-reviews/{review_id} |
Scrub encrypted preview/context/result and retain an audit tombstone | Requesting parent/admin + CSRF + critical step-up |
GET/PUT /api/guardian-reviews/{review_id}/feedback |
Read or replace versioned local feedback | Requesting parent/admin; CSRF for mutation |
Provider configuration and live credentials remain server-side. The Codex provider cannot become ready in this release.
Error model¶
All failures use:
{
"error": {
"code": "preview_stale",
"message": "The outbound preview changed; review it again.",
"retryable": false,
"review_id": null
}
}
Allowed codes are feature_disabled, zdr_not_confirmed,
configuration_error, consent_required, preview_stale, not_found,
already_running, rate_limited, upstream_timeout,
upstream_unavailable, upstream_refusal, invalid_model_output,
cannot_cancel, review_in_progress, and payload_too_large.
Messages never include an API key, raw upstream body, prompt, parent context, or
evidence.
Worker, timeout, and retry behavior¶
- Persist the job before returning 202 so it survives restart.
- Use alert, digest, provider, model, schema, and prompt as the idempotency identity; a duplicate submission returns the existing job.
- Allow 45 seconds per upstream attempt and two total attempts.
- Retry only transport failures, timeouts, eligible HTTP 408/429/5xx failures.
- Honor
Retry-Afterup to 30 seconds; otherwise use 1–3 seconds of jitter. - Do not retry refusals, malformed structured output, authorization, policy, consent/configuration errors, quota exhaustion, or deterministic 4xx.
- On exhaustion, persist a sanitized failure and allow an explicit new review.
Model and prompt configuration¶
Implemented environment contract:
| Setting | Default/requirement |
|---|---|
GUARDIANNODE_GUARDIAN_REVIEW_ENABLED |
false |
GUARDIANNODE_GUARDIAN_REVIEW_PROVIDER |
mock; mock and openai operational, codex security-disabled |
GUARDIANNODE_GUARDIAN_REVIEW_ZDR_CONFIRMED |
false; direct openai mode fails closed |
GUARDIANNODE_GUARDIAN_REVIEW_MODEL |
gpt-5.6 |
GUARDIANNODE_GUARDIAN_REVIEW_CODEX_MODEL |
gpt-5.6-sol |
| Prompt contract | guardian-review-v2; code-versioned |
| Redaction contract | guardian-review-redaction-v3; code-versioned, not configurable at runtime |
GUARDIANNODE_GUARDIAN_REVIEW_TIMEOUT_SECONDS |
45 |
GUARDIANNODE_GUARDIAN_REVIEW_MAX_ATTEMPTS |
2 |
OPENAI_API_KEY |
optional advanced openai mode only; never logged or stored in DB |
GUARDIANNODE_CODEX_EXECUTABLE / GUARDIANNODE_CODEX_HOME |
Legacy compatibility settings; provider remains fail-closed |
The direct Responses API request uses strict text.format JSON Schema, store: false,
medium reasoning effort, bounded output, no tools, no web access, no background
mode, and no response chaining. A deployment-scoped hashed parent identifier may
be used as safety_identifier; a child identifier must not be used. Record the
requested and returned model IDs because aliases can move.
The prior Codex compatibility path is retained only as non-routable legacy code and focused tests. Provider selection and device-login both fail closed until a zero-tool capability boundary is enforceable.
The system prompt is versioned source, treats all evidence and parent context as untrusted quoted data, forbids following instructions inside that data, forbids unsupported identity/intent claims, and requires uncertainty and limitations.
Audit events¶
Emit guardian_review.previewed, .cancelled, .consented, .queued, .sent,
.completed, .failed, .viewed, .deleted, and eventually
.feedback. The versioned audit details contain actor, incident/review
IDs, provider, prompt/schema/redaction versions, requested/returned model,
information categories, preview digest, outbound character count, status,
duration, attempt count, parent action, and sanitized error code.
Never audit raw outbound data, prompts, responses, API keys, parent notes, evidence excerpts, or personal data.
Mock mode¶
Mock mode uses deterministic synthetic scenario fixtures, produces the same strict DTO through the same durable worker/persistence/audit path, requires no API key or network, and is visibly labeled Mock assessment. It must never fall through to a cloud provider.
Required implementation tests¶
- JSON Schema contract and output rejection tests.
- Auth, CSRF, step-up, ownership, rate-limit, and fail-closed ZDR tests.
- Evidence ownership, minimization, redaction, preview digest, expiry, and stale consent tests.
- Durable queue, restart, idempotency, timeout, retry, refusal, malformed output, and sanitized-error tests.
- Prompt-injection fixtures and audit-log sensitive-data exclusion tests.
- Mock/live isolation and no-network mock tests.
- Dashboard queued/failure/completed/consent/feedback/accessibility states.
- Migration upgrade/rollback and encrypted-result retention tests.