Files
server-26/drb-frontend/app/privacy/page.tsx
Logan CusanoandClaude Opus 5 1b4ed0d09c Ship /terms, /privacy, and /waitlist as structure, not finished pages
SAAS_PLAN.md B5/B6, narrowed: no Stripe/pricing/tier work of any kind this
pass (a mid-build correction from the business side landed while this was
in progress - the commercial model, SAAS_PLAN.md section 6.1, is still
undecided), so app/pricing and lib/billing.ts's PLANS are untouched here.
What's left of B5/B6 without that - real legal pages and a working
waitlist - still ships.

app/terms/page.tsx and app/privacy/page.tsx are section scaffolding, not
legal text. Every section is a TODO(legal) note describing what that
section needs to cover, and the page leads with a "Draft - not yet in
force" banner. This isn't caution for its own sake: DRB records, stores,
and transcribes public-safety radio traffic, and recording/rebroadcast
legality varies by state (SAAS_PLAN.md section 6.3) - an agent-generated
draft here would be actively wrong to publish, not just unpolished. Both
were pre-added to middleware.ts's PUBLIC_PATHS and ChromeSwitcher's
MARKETING_PATHS two commits ago; MarketingFooter now links both.

app/waitlist/page.tsx is a real, working form against the already-shipped
POST /waitlist - email + optional org name/note, no plan or price
mentioned anywhere on it, matching the backend route's own scope (rate
limited by source IP, not coupled to any tier). Linked from
MarketingFooter as "Request access," not from the pricing page - pricing
CTAs stay exactly as they were.

Typecheck: clean (tsc --noEmit via the WSL-native ~/drb-frontend copy).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 20:38:49 -04:00

84 lines
4.7 KiB
TypeScript

import Link from "next/link";
/**
* SAAS_PLAN.md B5: page structure only — see app/terms/page.tsx for why the
* agent building this did not write real legal text. Privacy Policy needs
* the same jurisdiction-aware legal review as Terms, plus specifics this
* agent cannot respond for on the owner's behalf: what a real DPA/CCPA/GDPR
* posture looks like, and what third-party processors (OpenAI, Gemini,
* Google Maps, Firebase/GCP, Stripe once chosen) actually receive and why.
*/
const SECTIONS: { heading: string; note: string }[] = [
{
heading: "1. What data this collects",
note: "TODO(legal): account data (email, org membership), field node telemetry (location, status), radio call audio and AI-generated transcripts/entities/incident data, and usage/session logs (drb-c2-core's audit_log and user_sessions collections already exist and hold some of this today).",
},
{
heading: "2. Third parties this data is sent to, and why",
note: "TODO(legal): OpenAI (Whisper transcription), Google Gemini (incident extraction/summarization/embeddings), Google Maps (geocoding location strings extracted from transcripts), Google Cloud (Firestore + GCS storage, Firebase Auth), and — once a payment processor is chosen (SAAS_PLAN.md section 6.5, not yet decided) — that processor. Each of these is a real, already-integrated dependency, not a hypothetical one; this section needs to name them accurately, not generically.",
},
{
heading: "3. Recorded radio traffic specifically",
note: "TODO(legal): this product's core function is recording, transcribing, and storing monitored radio audio — including public-safety traffic that may name individuals, locations, and in-progress incidents. This needs explicit treatment distinct from generic 'we collect usage data' privacy boilerplate, and needs to be read alongside the same legal review flagged in Terms section 3.",
},
{
heading: "4. How long data is kept",
note: "TODO(legal): no retention enforcement exists in the product yet (no TTL, no sweep, no deletion job — see DEFERRED.md) — this section cannot promise a retention/deletion window the system doesn't actually implement.",
},
{
heading: "5. Customer and end-user rights",
note: "TODO(legal): access/export/deletion requests, and who they're directed to — org owner vs. platform operator.",
},
{
heading: "6. Cookies and session data",
note: "TODO(legal): drb_session is a client-set, non-httpOnly cookie used only for UI redirect logic (not an auth boundary — see CLAUDE.md); Firebase Auth sets its own session storage. No analytics/tracking cookies are set today.",
},
{
heading: "7. Security practices",
note: "TODO(legal): at a level appropriate for public disclosure — Firestore security rules, per-node credentials, encrypted transport. Should be reviewed against SAAS_PLAN.md's actual findings before publishing any specific claim.",
},
{
heading: "8. Changes to this policy",
note: "TODO(legal): how customers are notified.",
},
{
heading: "9. Contact",
note: "TODO(legal): real company legal identity and contact address — not yet decided (SAAS_PLAN.md section 6.6).",
},
];
export default function PrivacyPage() {
return (
<div className="max-w-screen-md mx-auto px-4 md:px-6 py-16 md:py-20">
<div className="bg-yellow-900/30 border border-yellow-700/50 rounded-lg px-4 py-3 mb-10">
<p className="text-yellow-200 text-sm font-mono font-semibold">
Draft — not yet in force
</p>
<p className="text-yellow-200/80 text-xs mt-1 leading-relaxed">
This page is a structural placeholder, not a real Privacy Policy. Every section below is a{" "}
<code className="text-yellow-100">TODO(legal)</code> marker, not actual legal text. Nothing on this page
describes a binding commitment about how data is handled.
</p>
</div>
<h1 className="text-display-sm text-white">Privacy Policy</h1>
<p className="text-gray-500 text-sm mt-2 font-mono">Draft — last structured {new Date().getFullYear()}</p>
<div className="mt-10 space-y-8">
{SECTIONS.map((s) => (
<section key={s.heading}>
<h2 className="text-white font-semibold">{s.heading}</h2>
<p className="text-gray-500 text-sm mt-2 leading-relaxed italic">{s.note}</p>
</section>
))}
</div>
<p className="text-gray-600 text-xs font-mono mt-16">
Questions in the meantime? <Link href="/faq" className="text-indigo-400 hover:text-indigo-300 transition-colors">Check the FAQ</Link> or{" "}
<Link href="/login" className="text-indigo-400 hover:text-indigo-300 transition-colors">sign in to reach us directly</Link>.
</p>
</div>
);
}