SOP-PROOF-01: Proof Portal System v1.2¶
Public-facing verification portal for batch safety testing results
Document ID: SOP-PROOF-01-v1.2 Version: 1.2 Status: ✅ Production Ready Last Updated: 2026-04-16 Owner: Protocol Raw Operations Replaces: SOP-PROOF-01-v1.1 Review Date: 2026-07-16
Key Changes in v1.2¶
- ✅ Batch page restructured from campaign-page to certificate-style document. Hero replaced by Verdict section; Results replaced by Evidence section.
- ✅ Verdict section: seal + "All clear." + metadata line + result chips + quiet outline action buttons. Replaces the hero with info card + filled CTAs.
- ✅ Evidence section: "INDEPENDENT LAB ANALYSIS" label, "Full test details" heading, Warm Linen background, white detail cards with expandable rows.
- ✅ Sample page at
/batch/samplewith quiet inline caption ("Sample report. Every batch you receive will have its own verified results."). Replaces the full-width green banner./sampleretained as legacy alias. - ✅
/batch/latestredirect via Cloudflare Workerproof-latest-redirect. Falls back to/batch/samplepre-launch. Supabase custom-domain gateway blocks plain-English slugs (latest,current,now,recent) and rejects responses on those paths, so the redirect is handled at the Cloudflare layer. - ✅ Green pill badges for test status: "NOT DETECTED" (Salmonella, Listeria), "PASS" (Hygiene). Replaces "Clear" + checkmark icon.
- ✅ Seal restored to original design: cream/neutral outer bezel with green jewel center, white checkmark, stamp-in animation with checkmark draw.
- ✅ Entrance animations: seal stamp, checkmark draw, staggered fade-up on headline / metadata / chips / actions / hint.
- ✅ Info card removed. Replaced by single metadata line:
{batch_code} · Tested {date} · {lab_name}. - ✅ Share buttons restyled as outline buttons (1.5px Stone border, Taupe text). Burnt Sienna removed from proof page actions.
- ❌ Removed "Tested before shipping. Results below." subtitle (evidence is now visible above the fold).
Key Changes in v1.1¶
- ✅ Architecture rewritten to DB-owned control flow. All business logic moved into 5 named PostgreSQL functions. Edge Function is now a thin HTTP wrapper only.
- ✅ Security hardened. Revoked all anon/authenticated grants on
proof_page_events. All writes go through SECURITY DEFINER functions with validated inputs. - ✅ Nonce-based CSP. Replaced
script-src 'unsafe-inline'with per-request nonce generation.unsafe-inlineremoved from script-src entirely. - ✅ Batch enumeration leak fixed. Search API and batch page no longer disclose existence of unreleased batches. Non-released and non-existent return identical responses.
- ✅ Server-side page view logging.
proof_page_viewevents logged insidefn_get_batch_proof_v1()on successful RELEASED page render. Client-side tracking removed for page views (kept for clicks/shares only). - ✅ SOP-MON-01 monitoring implemented.
fn_check_proof_portal_health_v2()deployed on 5-minute pg_cron schedule. Checks event volume anomalies, batch freshness, and ingestion errors. - ✅ INNER JOIN contract on lab results. RELEASED batches without lab results are treated as data integrity failures, not graceful degradations.
- ✅ Cloudflare rate limiting.
/api/eventendpoint rate-limited to 10 requests/minute per IP. - ✅ Idempotent migration. All DB changes use existence guards for safe re-deployment.
- ❌ Removed duplicated batch release flow. Cross-references SOP-PROOF-00 and SOP-LAB-01 instead.
- ❌ Removed stale document references. Updated to Brand Voice v1.3, Visual Identity v2.4, SOP-LAB-01.
1. Overview¶
1.1 Purpose¶
The Proof Portal is Protocol Raw's public-facing verification system. It allows customers, vets, and prospects to verify the safety testing results for any released batch. The portal serves as the operational manifestation of "Proof, Not Promises" — transforming trust claims into verifiable evidence.
1.2 Strategic Role¶
The Proof Portal is not a marketing asset. It is:
- A verification tool — Customers confirm their batch passed testing
- A trust-transfer mechanism — Customers share proof with vets and family
- A competitive moat — Time-locked trust capital that compounds with each batch
- A differentiation signal — Proves Protocol Raw operates differently than competitors
1.3 Access Points¶
| Entry Point | URL | Use Case |
|---|---|---|
| QR Code Scan | proof.protocolraw.co.uk/batch/{id} |
Customer scans packaging |
| Portal Homepage | proof.protocolraw.co.uk |
Manual batch search |
| Sample Page | proof.protocolraw.co.uk/batch/sample |
Pre-purchase prospect exploration |
| Latest Redirect | proof.protocolraw.co.uk/batch/latest |
Dynamic 302 to most recent released batch (via Cloudflare Worker proof-latest-redirect). Falls back to /batch/sample pre-launch. |
| Shared Link | proof.protocolraw.co.uk/batch/{id} |
Vet/family receives link |
| Website CTAs | Various site pages | All proof CTAs link to /batch/sample pre-launch, /batch/latest post-launch |
| Legacy alias | proof.protocolraw.co.uk/sample |
Retained alias for the sample page; new links should use /batch/sample |
2. System Architecture¶
2.1 Technical Stack¶
| Component | Technology | Purpose |
|---|---|---|
| Edge Function | Supabase Deno (proof-page) |
Thin HTTP wrapper, HTML rendering |
| Database | Supabase PostgreSQL (raw_ops schema) |
All business logic, validation, analytics |
| Domain | Cloudflare | DNS, SSL, caching, rate limiting |
| Fonts | Google Fonts | Inter, Montserrat |
2.2 Architecture Principle¶
PostgreSQL owns all business logic. The Edge Function is a thin HTTP wrapper.
All data retrieval, search, analytics ingestion, and validation happen in named PostgreSQL functions. The Edge Function's responsibilities are limited to: parsing HTTP requests, calling the appropriate database function via RPC, and rendering HTML from the returned data.
2.3 Edge Function: proof-page¶
Location: Supabase Edge Functions
Auth: Service role key (required because anon access to events table has been revoked)
JWT Verification: Disabled (public endpoint)
Domain: proof.protocolraw.co.uk (Cloudflare DNS → Supabase Edge Function)
CSP: Per-request nonce-based. No unsafe-inline in script-src.
Routes¶
| Route | Method | DB Function Called | Purpose |
|---|---|---|---|
/ |
GET | None (pre-launch redirect) | Redirects to main site |
/batch/{public_batch_id} |
GET | fn_get_batch_proof_v1() |
Individual batch page |
/sample |
GET | None (hardcoded data) | Sample report for prospects |
/api/search |
GET | fn_search_public_batch_v1() |
Batch lookup API |
/api/event |
POST | fn_ingest_proof_event_v1() |
Click/share analytics |
Route Handler Contract¶
Each route handler follows the same pattern:
- Parse and sanitise input from the HTTP request
- Call the named PostgreSQL function via
supabase.rpc() - Map the function result to an HTTP response (HTML page or JSON)
- Return with appropriate headers (CSP, caching, security)
The Edge Function never queries tables or views directly. It never contains business logic, validation rules, or data transformation beyond HTML rendering.
2.4 Database Functions¶
| Function | Purpose | Called By |
|---|---|---|
fn_get_batch_proof_v1(p_public_batch_id, p_referrer, p_user_agent, p_device_type) |
Retrieve batch proof data. Logs server-side page view for RELEASED batches. Returns status-only for QA_HOLD/REJECTED. NULL for not found. | serveBatchPage() |
fn_search_public_batch_v1(p_search_term) |
Search by batch_code or public_batch_id. RELEASED only. No enumeration leak. | handleSearch() |
fn_ingest_proof_event_v1(p_event_type, p_batch_code, ...) |
Validated click/share event ingestion. Rejects proof_page_view (server-side only). | handleEvent() |
fn_get_latest_released_batch_v1() |
Most recently released batch for homepage. Ordered by released_at. |
serveHomepage() |
fn_check_proof_portal_health_v2() |
SOP-MON-01 compliant health monitor. | run-monitor Edge Function |
All functions are SECURITY DEFINER with SET search_path = raw_ops, pg_temp.
2.5 Database Schema¶
View: v_released_batches_verified¶
Retained for Metabase dashboard queries. Not used by the Edge Function (which calls functions directly).
Table: proof_page_events¶
Analytics tracking for proof portal engagement.
CREATE TABLE raw_ops.proof_page_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
event_type TEXT NOT NULL,
batch_code TEXT NOT NULL,
public_batch_id TEXT,
referrer TEXT,
user_agent TEXT,
device_type TEXT,
share_method TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT chk_proof_event_type
CHECK (event_type IN ('proof_page_view', 'send_to_vet_click', 'share_proof_click'))
);
-- Indexes
CREATE INDEX idx_proof_page_events_type_created
ON raw_ops.proof_page_events(event_type, created_at DESC);
CREATE INDEX idx_proof_page_events_batch
ON raw_ops.proof_page_events(batch_code, created_at DESC);
Access control:
- RLS enabled, no anon or authenticated policies (service_role only)
- All writes go through fn_ingest_proof_event_v1() (click/share events) or fn_get_batch_proof_v1() (server-side page views)
- Metabase reads via service_role connection
3. Portal Homepage¶
3.1 URL¶
3.2 Pre-Launch Behaviour¶
Currently redirects (302) to https://www.protocolraw.co.uk. Remove redirect at launch to enable the full homepage with search and recent batches.
3.3 Data Source¶
fn_get_latest_released_batch_v1() — returns the most recently released batch ordered by released_at DESC. INNER JOIN on lab_results ensures only batches with complete proof data are shown.
3.4 Page Structure¶
Hero Section: "Proof, Not Promises" headline, subtitle, batch lookup card with search input.
Recent Batches Section: Displays latest released batch(es) with status, batch code, test date, and link to full results.
Bridge Section: CTAs to calculator and how-it-works pages.
3.5 Search API¶
Endpoint: GET /api/search?batch_code={id}
DB Function: fn_search_public_batch_v1(p_search_term)
Response:
// Found and released
{ "found": true, "status": "RELEASED", "public_batch_id": "PR-2CCF573F" }
// Not found OR not released (identical — no enumeration leak)
{ "found": false }
Security note: Prior to v1.1, the search endpoint returned {"found": true, "status": "NOT_RELEASED"} for unreleased batches, confirming their existence to unauthenticated users. This has been fixed. All non-released batches now return the same response as non-existent batches.
4. Individual Batch Page¶
4.1 URL¶
4.2 Data Source¶
fn_get_batch_proof_v1(p_public_batch_id, p_referrer, p_user_agent, p_device_type)
Response shapes:
| Batch Status | Function Returns | Page Rendered |
|---|---|---|
| RELEASED | Full proof data (lab results, product, formulation) + server-side page view logged | Full results page with wax seal, test cards, share buttons |
| QA_HOLD | {"status": "QA_HOLD"} |
Status page: "This batch is undergoing safety testing." |
| REJECTED | {"status": "REJECTED"} |
Status page: "This batch did not pass our safety requirements." |
| Not found | NULL | 404 page |
Integrity contract: RELEASED batches use INNER JOIN on lab_results. If a RELEASED batch has no lab results, the function logs a data integrity error to ops_events and returns NULL (404 to the user). This is deliberate: the proof portal exists to publish evidence. A released batch without evidence is a system failure, not a graceful degradation.
4.3 Server-Side Page View Logging¶
Page views are logged server-side inside fn_get_batch_proof_v1() when a RELEASED batch is successfully fetched. This is the authoritative source for QR scan and page view counts. Client-side sendBeacon was removed because it is unreliable (ad blockers, race conditions, silent failures).
Client-side tracking is retained only for click events (send_to_vet_click, share_proof_click) where server-side logging is not possible.
4.4 Page Structure¶
The page is two sections: Verdict (above the fold) and Evidence (below). The Verdict delivers the headline answer; the Evidence backs it up for anyone who scrolls.
Verdict Section¶
- Sample caption (sample page only): quiet inline text "Sample report. Every batch you receive will have its own verified results."
- Wax seal (200px): cream/neutral outer bezel with green jewel center (radial gradient #4A8B72 → #3D6B5A → #2D5144 → #234238), shine overlay, white checkmark on the jewel. Stamp-in animation with delayed checkmark draw and continuous subtle glow.
- Headline: "All clear." (Montserrat 800, 52px desktop / 40px ≤900 / 36px ≤600)
- Metadata line:
{batch_code} · Tested {reported_at, formatted en-GB} · {lab_name}(13px Inter 500, Taupe; separators in Stone). Sample page uses "Independent lab" in place oflab_name. - Result chips: three inline white chips, each with
chip-name(e.g. "Salmonella") +chip-badgegreen pill ("NOT DETECTED" or "PASS"). Salmonella and Listeria show "NOT DETECTED" when*_absent; Hygiene shows "PASS" when enterobacteriaceae value is below the limit. - Action row: "Send to Vet" + "Share" as outline buttons (1.5px Stone border, Taupe text, transparent background; hover Espresso/white).
- Action hint: "Handy for vets, family, or anyone who asks."
Evidence Section¶
- Background: Warm Linen (
#EBE8E3) with Stone top divider + 6px inset shadow. - Label: "INDEPENDENT LAB ANALYSIS" (11px, 0.15em letter-spacing, Burnt Sienna).
- Heading: "Full test details" (Montserrat 700, 24px).
- Three detail cards: white on Linen, Forest Green icon (44px, gradient
var(--forest)→var(--forest-light)), test name, greendetail-badgepill, expandable detail rows revealing Test / Result / Method / Lab. Cards expand on click. - Two bottom cards: Complete nutrition (links to formulation_pdf_url; disabled on sample) + Full lab report (download link; replaced with "Lab certificates are available on live batch reports." copy on sample).
4.5 Sample Report¶
Canonical URL: proof.protocolraw.co.uk/batch/sample
Legacy alias: proof.protocolraw.co.uk/sample (still served by the same serveSamplePage() handler)
Hardcoded data, no database queries. The page renders the same Verdict + Evidence layout as a real batch, with a quiet inline caption above the seal: "Sample report. Every batch you receive will have its own verified results."
Uses the current request time as the "Tested" field — a cosmetic recency signal disclosed by the caption.
4.6 Cloudflare Worker: proof-latest-redirect¶
The Edge Function exposes /batch/latest (and the alias /batch/PR-00LATEST) which 302-redirects to the most recent released batch via fn_get_latest_released_batch_v1(), falling back to /batch/sample when no released batches exist.
The Supabase custom-domain gateway intercepts plain-English slugs under /batch/ (latest, current, now, recent) and returns {"error":"requested path is invalid"} before requests reach the function. To work around this, the redirect is fronted by a Cloudflare Worker.
| Field | Value |
|---|---|
| Worker name | proof-latest-redirect |
| Routes | proof.protocolraw.co.uk/batch/latest, proof.protocolraw.co.uk/batch/PR-00LATEST |
| Behaviour | Fetches the direct Supabase function URL (znfjpllsiuyezqlneqzr.supabase.co/functions/v1/proof-page/batch/PR-00LATEST) and passes the 302 response through |
| Response cache | Cache-Control: no-cache (redirect target changes when new batches release) |
| Fallback | If no released batches, the function returns 302 to /batch/sample; the Worker passes that through |
| Analytics | The worker does not log; the destination batch page logs its own proof_page_view |
5. Share Functionality¶
5.1 Strategic Purpose¶
Share buttons are a growth primitive, not a marketing feature. They enable customers to transfer trust to skeptical audiences (vets, family) using verifiable evidence rather than brand claims.
5.2 Send to My Vet¶
Trigger: Button click
Action: Opens mailto with prefilled content
Tracking: send_to_vet_click event via client-side track() → /api/event → fn_ingest_proof_event_v1()
Subject:
Body:
Hi,
I wanted to share the safety test results for the raw food I'm feeding.
Batch: {batch_code}
Tests: Salmonella, Listeria, Enterobacteriaceae
Result: All clear
Full report: {batch_url}
Happy to discuss if you have questions.
Best regards
5.3 Share This Proof¶
Trigger: Button click
Action: Web Share API (mobile) or copy to clipboard (desktop)
Tracking: share_proof_click event with share_method: 'native' or 'copy'
5.4 Share Hint¶
Text: "Handy for vets, family, or anyone who asks."
6. Analytics¶
6.1 Event Sources¶
| Event | Source | Properties |
|---|---|---|
proof_page_view |
Server-side (fn_get_batch_proof_v1) | batch_code, public_batch_id, referrer, user_agent, device_type |
send_to_vet_click |
Client-side JS → /api/event → fn_ingest_proof_event_v1 | batch_code, share_method: 'email' |
share_proof_click |
Client-side JS → /api/event → fn_ingest_proof_event_v1 | batch_code, share_method: 'native' or 'copy' |
6.2 Validation¶
All events are validated by fn_ingest_proof_event_v1():
- event_type must be in allowed list (proof_page_view rejected from client — server-side only)
- device_type must be 'mobile', 'desktop', or 'tablet' (else NULL)
- share_method must be 'email', 'native', or 'copy' (else NULL)
- All text fields truncated to prevent oversized payloads
- Empty strings normalised to NULL
- Failures logged to ops_events with nested exception protection
6.3 Key Metrics¶
| Metric | Query | Insight |
|---|---|---|
| Daily page views | COUNT(*) WHERE event_type = 'proof_page_view' AND created_at > now() - interval '1 day' |
QR scan engagement |
| Share rate | share_clicks / page_views |
Trust transfer activity |
| Vet share rate | vet_clicks / page_views |
Professional validation seeking |
| Mobile vs Desktop | GROUP BY device_type |
Access context |
| Referrer breakdown | GROUP BY referrer |
Entry point analysis |
6.4 Metabase Dashboard Queries¶
Collection: "SOP-PROOF-01 — Proof Portal"
Daily Views:
SELECT
DATE(created_at) as date,
COUNT(*) as views
FROM raw_ops.proof_page_events
WHERE event_type = 'proof_page_view'
GROUP BY DATE(created_at)
ORDER BY date DESC
LIMIT 30;
Share Funnel:
SELECT
event_type,
COUNT(*) as count
FROM raw_ops.proof_page_events
WHERE created_at > now() - interval '30 days'
GROUP BY event_type
ORDER BY count DESC;
Share Method Breakdown:
SELECT
share_method,
COUNT(*) as count
FROM raw_ops.proof_page_events
WHERE event_type = 'share_proof_click'
AND created_at > now() - interval '30 days'
GROUP BY share_method;
Weekly Share Report:
SELECT
DATE_TRUNC('week', created_at) as week,
COUNT(*) FILTER (WHERE event_type = 'proof_page_view') as views,
COUNT(*) FILTER (WHERE event_type = 'send_to_vet_click') as vet_shares,
COUNT(*) FILTER (WHERE event_type = 'share_proof_click') as social_shares,
ROUND(
COUNT(*) FILTER (WHERE event_type IN ('send_to_vet_click', 'share_proof_click'))::numeric /
NULLIF(COUNT(*) FILTER (WHERE event_type = 'proof_page_view'), 0) * 100,
2
) as share_rate_pct
FROM raw_ops.proof_page_events
WHERE created_at > now() - interval '12 weeks'
GROUP BY DATE_TRUNC('week', created_at)
ORDER BY week DESC;
7. Visual Design¶
7.1 Design System Reference¶
Source: Visual Identity Guide v2.5
7.2 Color Palette¶
| Token | Hex | Usage |
|---|---|---|
--col-espresso |
#2B2523 | Primary text, footer |
--col-forest-green |
#2D5144 | Pass badges, verified status, share buttons |
--col-burnt-sienna |
#B85C3A | Primary CTAs |
--col-cream |
#F9F7F4 | Body background, header gradient |
--col-warm-linen |
#EBE8E3 | Card backgrounds, borders |
--col-taupe |
#6B6360 | Secondary text, labels |
Batch Page Container: Cream (#FCFBF9) background with paper texture overlay. This is intentionally lighter than the standard Cream to create a clean, clinical context for lab results. Declared as a proof-context override from the standard brand palette.
7.3 Typography¶
| Element | Font | Weight | Size (Desktop) | Size (Mobile) |
|---|---|---|---|---|
| Brand header | Montserrat | 700 | 18px | 18px |
| H1 | Montserrat | 800 | 48px | 28px |
| H2 | Montserrat | 700 | 28px | 22px |
| Body | Inter | 400 | 17px | 16px |
| Labels | Inter | 600 | 10-12px | 10-11px |
| Buttons | Inter | 600 | 14px | 14px |
7.4 Animations¶
All animations respect prefers-reduced-motion: reduce.
| Element | Animation | Duration | Delay |
|---|---|---|---|
| Seal | sealStamp (scale 0.85 → 1.04 → 1, opacity 0 → 1) |
700ms | 100ms |
| Seal text / hr / pips | fadeIn |
400ms | 500ms |
| Seal checkmark | checkDraw (stroke-dashoffset 40 → 0) |
400ms | 500ms |
| Seal glow ring | sealGlow (continuous pulse) |
3000ms | 800ms (loops) |
| Headline | fadeUp |
500ms | 400ms |
| Metadata line | fadeUp |
500ms | 500ms |
| Result chips | fadeUp |
500ms | 600ms |
| Action row | fadeUp |
500ms | 700ms |
| Action hint | fadeUp |
500ms | 800ms |
7.6 UI Patterns¶
- Green pill badge (
.chip-badge,.detail-badge): background#E8F5E9, text#2D5144, border-radius 100px, 12px Inter 600, uppercase, 0.03em letter-spacing. Used for "NOT DETECTED" and "PASS" labels in the verdict chips and detail cards. The green background carries the pass signal — no checkmark icon is rendered inside the badge. - Evidence section background: Warm Linen (
#EBE8E3) — a deliberate override from the proof-page Cream canvas to delineate Evidence from Verdict. - Cards on Linen: white background with
1px solid rgba(43,37,35,0.05)border and subtle shadow (0 1px 3px rgba(43,37,35,0.06), 0 4px 12px rgba(43,37,35,0.03)). - Seal: the canonical render is a cream/neutral outer bezel (
linear-gradient(145deg, #e8e5e0 0%, #d4d0ca 50%, #e2dfda 100%)) with a green jewel center (radial gradient#4A8B72 0%→#3D6B5A 30%→#2D5144 70%→#234238 100%) and a white checkmark on the jewel.
7.5 Responsive Breakpoints¶
| Breakpoint | Changes |
|---|---|
| ≤600px | Single column layout, stacked share buttons, smaller headings, stacked test cards |
| ≤900px | Reduced padding, smaller seal, vertical info card |
| >900px | Full desktop layout with 3-column test cards |
8. Security¶
8.1 Content Security Policy¶
Generated per-request with a unique nonce:
default-src 'self';
script-src 'nonce-{random}' https://cdn.protocolraw.co.uk https://my.protocolraw.co.uk;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'
unsafe-inline is removed from script-src. All inline scripts use the per-request nonce attribute. unsafe-inline is retained for style-src due to Google Fonts and inline styles.
8.2 Additional Headers¶
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
8.3 Data Access¶
- Edge Function uses service_role key (anon access revoked)
- Only RELEASED batches return full proof data
- QA_HOLD and REJECTED return status-only (no lab data, no batch details)
- Non-existent batches return NULL (404)
- No PII is exposed through the proof portal
- Search endpoint does not disclose existence of unreleased batches
8.4 Analytics Endpoint Security¶
/api/eventrate-limited via Cloudflare: 10 requests/minute per IPfn_ingest_proof_event_v1()validates event_type against allowed list- Field truncation prevents oversized payloads
- CHECK constraint on
event_typecolumn as DB-level defence in depth - No direct table access: all writes through SECURITY DEFINER functions
9. Caching¶
| Page | Cache-Control | Rationale |
|---|---|---|
| Batch pages | public, max-age=3600 |
Lab results don't change once released |
| Portal homepage | public, max-age=300 |
Latest batch updates more frequently |
| Sample page | public, max-age=3600 |
Static content |
| API endpoints | No cache | Real-time data |
10. Copy Principles¶
10.1 Voice Guidelines¶
Aligned with: Protocol Raw Brand Voice & Copy Guidelines v1.3
- Show, don't tell — Let lab results speak
- Systematic, not emotional — Facts over reassurance
- Clinical where appropriate — Vets are an audience
- No anxiety amplification — Don't mention risks at relief moments
10.2 Key Copy Decisions¶
| Element | Decision | Rationale |
|---|---|---|
| Summary verdict (h1) | "All clear." retained | Headline summary, not an individual test label. Conversational, confident, low-key. |
| Subtitle | Removed in v1.2 | Evidence is now visible above the fold via result chips, so the directional subtitle is redundant. |
| Salmonella status | "Not detected" | Matches lab terminology (ISO 6579-1 reports detection state). |
| Listeria status | "Not detected" | Matches lab terminology (ISO 11290-1 reports detection state). |
| Hygiene status | "Pass" | Threshold-based test (Enterobacteriaceae count vs limit), not binary detection. |
| Share hint | "Handy for vets, family, or anyone who asks." | Frames sharing as practical, not promotional. |
| Vet email tone | Clinical, bullet points | Vets want data, not marketing. |
| FEDIAF claim | "Meets FEDIAF guidelines for all life stages" | Only claim what we can evidence. |
11. Monitoring¶
11.1 Health Monitor¶
Function: fn_check_proof_portal_health_v2()
Schedule: Every 5 minutes via pg_cron (monitor-proof-portal-health)
Pattern: SOP-MON-01 compliant (pg_cron → pg_net → run-monitor → fn_check → monitoring_runs → ops-alerter)
Checks:
| Check | Threshold | Severity | Channel |
|---|---|---|---|
| Event volume spike | >5x 7-day hourly average AND >100 absolute | Warning | #ops-alerts |
| Batch freshness | >7 days since last release | Warning | #ops-alerts |
| Ingestion errors | >10 errors in last hour | Critical | #ops-urgent |
11.2 Metabase Dashboard¶
Collection: "SOP-PROOF-01 — Proof Portal"
See Section 6.4 for saved question SQL.
11.3 Operational Procedures¶
Upstream dependencies: Batch creation and release (SOP-PROOF-00, SOP-LAB-01). If batches stop being released, the freshness monitor will alert.
For batch lifecycle and release flow: See SOP-LAB-01 for lab result processing and batch release, and SOP-PROOF-00 for proof generation pipeline (QR codes, labels, co-packer delivery).
12. Future Enhancements¶
12.1 Planned¶
| Enhancement | Priority | Status |
|---|---|---|
| Deploy Metabase saved questions | High | Ready (SQL exists in Section 6.4) |
Partition/retention strategy for proof_page_events |
Medium | Design now, implement Phase B |
| Download certificate tracking event | Medium | Not started |
| Formulation link click tracking event | Medium | Not started |
12.2 Considered but Deferred¶
| Enhancement | Reason Deferred |
|---|---|
| Social proof counters | Could feel like marketing |
| Comments/reviews | Out of scope for verification tool |
| Batch comparison | Low value, adds complexity |
13. Related Documents¶
| Document | Relationship |
|---|---|
| Visual Identity Guide v2.4 | Design system source |
| Protocol Raw Brand Voice & Copy Guidelines v1.3 | Copy voice reference |
| SOP-PROOF-00 v1.0 | Upstream: proof generation pipeline, batch creation, QR/label system |
| SOP-LAB-01 | Upstream: lab result processing, batch release/rejection |
| SOP-MON-01 v1.0 | Monitoring pattern |
| SOP-INV-01 | Downstream: allocation after release |
14. All Objects¶
| Object | Type | Purpose |
|---|---|---|
raw_ops.proof_page_events |
Table | Portal analytics |
raw_ops.fn_get_batch_proof_v1() |
Function | Batch page data + server-side page view |
raw_ops.fn_search_public_batch_v1() |
Function | Public batch search |
raw_ops.fn_ingest_proof_event_v1() |
Function | Validated click/share ingestion |
raw_ops.fn_get_latest_released_batch_v1() |
Function | Homepage latest batch |
raw_ops.fn_check_proof_portal_health_v2() |
Function | SOP-MON-01 health monitor |
monitor-proof-portal-health |
pg_cron job | 5-minute health check schedule |
chk_proof_event_type |
CHECK constraint | DB-level event_type validation |
ux_batches_public_batch_id |
Unique index | Guarantees batch lookup contract |
ux_batches_batch_code |
Unique index | Guarantees search contract |
proof-page |
Edge Function | Thin HTTP wrapper, HTML rendering |
15. Version History¶
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.1 | 2026-03-20 | Protocol Raw Operations | Architecture remediation: DB-owned functions, security hardening, nonce-based CSP, server-side page views, SOP-MON-01 monitoring, enumeration fix, Cloudflare rate limiting. Updated all document references. |
| 1.0 | 2026-01-27 | Protocol Raw Operations | Initial comprehensive documentation |
End of SOP-PROOF-01 v1.1
Last reviewed: 2026-03-20 Next review: 2026-06-20 System status: ✅ Production Ready
Protocol Raw — Verified safe, batch by batch