Technical Architecture

Pragma

System design, model orchestration, API reference, and deployment guide.

The Pragma Model

A domain-specific ethical reasoning model — fine-tuned on structured analysis, improving via user feedback.

General LLMs know everything about everything.
The Pragma model knows everything about ethical reasoning.

A model trained exclusively on structured ethical analysis develops deeper domain expertise than a model trained on the entire internet. The Pragma model is our proprietary reasoning engine — and it gets smarter every training cycle.

The Retraining Flywheel

🤖
Claude (Teacher)
Labels training data
across 31 scenario templates
→
📊
Training Dataset
train.jsonl + eval.jsonl
Structured ethical analyses
→
⚗️
LoRA Fine-Tune
Phi-3-mini on Colab T4
PEFT + SFTTrainer
→
🧠
Pragma Model
HuggingFace Inference API
Primary production provider
→
👍
User Feedback
Thumbs up/down
Stored per category
→
📉
Weak Category Detection
collect_feedback.py
Approval rate < 70%
→
🎯
Targeted Retraining
More examples in weak areas
Appended to train.jsonl
↺

Model Distillation

Claude claude-opus-4-6 acts as the teacher, labelling ethical scenarios with structured JSON output. The Pragma model (Phi-3-mini LoRA adapter) learns to replicate this reasoning — at a fraction of the inference cost.

Feedback-Driven Training

Every thumbs up/down is stored by category and model version. collect_feedback.py identifies which categories have approval below 70% and auto-generates more targeted training examples for those areas.

Provider Phase-Out Plan

Claude and OpenAI are offline teachers only — used to bootstrap training data, not as the long-term production path. As the Pragma model matures, third-party LLM dependency drops to zero.

ML Pipeline Scripts

# Step 1 — Generate labelled training data (local)
ANTHROPIC_API_KEY=sk-ant-... \
python ml/generate_data.py --n 5
# → ml/data/train.jsonl (~140 examples)
# → ml/data/eval.jsonl
# Step 2 — Fine-tune on Google Colab T4 (free)
pip install transformers peft trl datasets
HF_TOKEN=hf_... python ml/train.py \
--hf_repo yourname/pragma-ethics-v1
# Step 3 — Run the feedback flywheel
DATABASE_URL=postgresql://... \
ANTHROPIC_API_KEY=sk-ant-... \
python ml/collect_feedback.py --threshold 0.70
# Step 4 — Evaluate student vs teacher quality
HF_TOKEN=hf_... python ml/evaluate.py \
--student_repo yourname/pragma-ethics-v1
# Reports: parse rate, risk Jaccard, conf MAE

Overview

Three client layers share one FastAPI engine. The Pragma model is the primary reasoning provider.

Web Frontend

iOS-style glassmorphism single-page app. Tabs: Evaluate (proxy var guard, HITL override), History, Batch, Audit Log 🔐, Compliance 🏛️ (5-step wizard with AI evidence upload + guided interview, certificate), Dashboard 📊 (score trends, heatmap, deadline countdown), Settings + Billing. Mobile bottom nav bar with iOS Safari fixed-position fix.

HTML5 Vanilla JS CSS Glassmorphism Vercel

Mobile App

React Native (Expo) app for iOS & Android. Screens: Auth (social proof + sign-in), Home (evaluate with guided questions), Results (firewall verdict banner, regulatory refs, counterfactual analysis), History, Chat (compliance-aware chatbot with scenario shortcuts and per-message firewall badges). Shares the same API as the web dashboard.

React Native Expo TypeScript Expo EAS

Backend Engine

FastAPI with Pragma model as primary provider. Claude/OpenAI as fallbacks. PostgreSQL on Railway.

FastAPI Pragma Model SQLAlchemy Railway

L1 Compliance Engine

Deterministic, synchronous rule evaluation — no LLM call, no latency. Catches most violations before reaching L2.

HOLC Geo Redlining

500+ historically redlined zip codes across 35 US cities, loaded from geo_data.py at startup. Any decision where context.zip_code maps to a redlined area is flagged as a potential Fair Housing Act violation.

ECOA Proxy Variables

35+ proxy fields detected: zip_code, last_name, ip_country, email_domain, neighborhood descriptors, national origin indicators. Compound risk detection when multiple proxies appear together.

Text Scanner — 14 Patterns

Indirect discriminatory phrasing: "that part of town", "near retirement", "family obligations", "mother tongue", "criminal background", and 9 more. Scans both decision text and context free-text fields.

Disparate Impact (EEOC 4/5ths)

Batch decision sets are analyzed for group-level pass rates. A protected group with a pass rate below 80% of the highest group's rate triggers an EEOC disparate impact flag automatically.

State Law Overlays

California FEHA, New York HRL, and Illinois HRA add state-level protected classes and stricter thresholds on top of federal (ECOA/EEOC) baseline. Applied based on geographic scope of the AI system.

Dynamic Rule Engine

Rules stored in compliance_rules table. Default action (FAIL/FLAG/ALLOW) and threshold are configurable per org via org_rule_overrides. Changes take effect immediately — no deployment required. Full audit trail of who changed what.

Law Research Agent

Daily cron sweeps Federal Register API + EEOC newsroom. Claude filters for AI hiring relevance, drafts a rule config (regulation name, citation, violation patterns). Saved as pending, admin notified by email, auto-promoted to live rules after 24 h unless rejected. Zero human effort to stay current.

Law Research Agent

Self-updating compliance corpus — new regulations discovered daily, rules deployed automatically.

🔍
Source Sweep
Federal Register API
EEOC newsroom · 4 search terms
→
🤖
Claude Filters
Relevant to AI hiring?
Draft rule config JSON
→
⏳
Pending (24 h)
Admin email sent
Reject window open
→
✅
Live in Engine
Inserted into compliance_rules
Active on next request

Sources

  • Federal Register API — 4 parallel search queries
  • EEOC newsroom — AI-relevant press releases
  • Deduplication by URL hash — never re-analyzes a seen document
  • Configurable lookback window (LAW_AGENT_DAYS_BACK)

Rule Format

Claude drafts a JSON config — not arbitrary code. Fields: rule_key, regulation, statute_citation, patterns[], fields[], default_severity (FAIL/FLAG), categories. Inserted into compliance_rules table via upsert_compliance_rule_from_agent().

Admin Control

  • GET /admin/law-agent/findings — all findings
  • POST /admin/law-agent/run — trigger manually
  • POST /admin/law-agent/findings/{id}/reject — kill before activation
  • POST /cron/law-agent — Railway cron endpoint (X-Cron-Secret)
  • 24 h activation delay — safety window

Files

backend/law_agent.py — agent: fetch → Claude filter → draft rule → save pending → email backend/cron_runner.py — standalone script: python -m backend.cron_runner (Railway cron) backend/database.py — law_agent_findings table + 7 DB functions backend/main.py — /admin/law-agent/* endpoints + POST /cron/law-agent Env vars required: ANTHROPIC_API_KEY — Claude analysis RESEND_API_KEY — admin email notification ADMIN_EMAIL — where to send alerts (default: cosmosservicesai@gmail.com) CRON_SECRET — protects POST /cron/law-agent from unauthenticated calls LAW_AGENT_ACTIVATION_HOURS — hours before auto-activation (default: 24) LAW_AGENT_DAYS_BACK — lookback window for sweeps (default: 7)

System Architecture

Request flows through L1 deterministic rules, then L2 LLM analysis. Analytics captured at each step.

CLIENT LAYER
Web Browser frontend/index.html
iOS / Android App mobile/ (Expo)
HTTPS / REST API
↓
Authentication
Google OAuth 2.0 GIS Token Verify
Guest Session Anonymous Token
↓
Backend — Railway
FastAPI backend/main.py
L1 Compliance Engine geo_data.py · text scanner · state overlays
LLM Orchestrator llm_orchestrator.py · Claude primary
EU AI Act Engine compliance_engine.py · 15 articles
Evidence Analyzer evidence_analyzer.py + interview_engine.py
PDF / Certificate Generator report_generator.py · compliance_certificate.py
Adverse Action Generator ECOA §1002.9 · FCRA §615(a) notices
Analytics Layer PostHog + user_profiles + feature_events
Notifications notifications.py · Resend · daily cron
↓
Billing — Stripe
Stripe Checkout Growth $299/mo · /billing/create-checkout-session
Customer Portal Subscription mgmt · /billing/portal
Webhook Handler Signature-verified · /billing/webhook
↓
LLM Layer — L2 Analysis
Claude (Anthropic) 🥇 Primary — L2 LLM analysis
→ fallback →
Mock Response Offline / unconfigured
L1 deterministic rules run before LLM — most violations caught before any LLM call
↓
Storage
PostgreSQL request_logs · audit_log · ai_systems · certificates · subscriptions · users · notification_log · compliance_snapshots · analysis_feedback · waitlist · organizations · org_members · api_keys · compliance_rules · org_rule_overrides · user_profiles · feature_events
In-Memory Sessions Bearer tokens · 24hr TTL
↓
Analytics
PostHog pragmaTrack() · behavioral events
Own DB Analytics user_profiles · feature_events · DAU/WAU/MAU
Admin Dashboard GET /admin/analytics · in-app panel

API Endpoints

All endpoints served from the FastAPI backend on Railway.

Method Path Access Description
POST /auth/google Public Verify Google ID token, create session
POST /auth/guest Public Create anonymous guest session
POST /logout Bearer Invalidate session token
GET /me Bearer Return current user name & picture
GET /my-stats Bearer Return anonymous usage stats for session
POST /evaluate-decision Bearer Run compliance evaluation — returns firewall_action, risk_flags, regulatory_refs, confidence_score, audit_log_id, proxy_variables_detected
POST /chat Bearer Compliance-aware chat — firewall evaluates message before response is generated; blocked messages return verdict only
POST /evaluate-batch Bearer CSV upload (up to 100 rows) — returns CSV with firewall verdicts and regulatory refs appended
POST /counterfactual Bearer Bias audit — compares original vs. modified context, diffs flags_added / flags_removed / confidence_delta
POST /feedback Bearer Submit thumbs up/down on an analysis — feeds the retraining flywheel
POST /generate-report Bearer Generate downloadable PDF audit report
GET /questions Bearer Category-specific guided context questions for hiring, finance, healthcare, and more
POST /orgs Bearer Create an organization workspace with auto-generated invite code
POST /orgs/join Bearer Join an organization via invite code
POST /api-keys Bearer Generate a pragma_* API key for programmatic SDK access
GET /api-keys Bearer List all API keys for current user (id, name, prefix, created, last_used)
DELETE /api-keys/{key_id} Bearer Revoke an API key by id
GET /compliance-rules Bearer Get effective compliance rules for a category (merged org overrides over global defaults)
PUT /compliance-rules/{rule_id} Bearer Update a compliance rule (action, threshold, enabled) — changes take effect immediately, no redeploy
POST /analyze-text-bias Bearer Run text scanner on a string — detects indirect discriminatory phrasing (14 pattern categories)
POST /adverse-action-notice Bearer Generate ECOA §1002.9 + FCRA §615(a) compliant adverse action notice from an audit_log_id
POST /disparate-impact Bearer Run EEOC 4/5ths disparate impact analysis across demographic groups from a batch decision set
POST /track Bearer Track a feature event to own DB (complements PostHog) — updates user_profiles and feature_events
GET /admin/analytics Admin Return DAU/WAU/MAU, feature adoption rates, top events, plan distribution, new users (7d) — admin only
GET /cosmos Public Serve Cosmos AI LLC company homepage (frontend/cosmos.html)
GET /audit/log Bearer Last 50 audit entries for current user — verdict, risk flags, proxy vars detected, HITL override status, input hash
POST /audit/override Bearer Record human investigator override of a firewall verdict — requires reason; EU AI Act Art. 14 compliance
POST /ai-systems Bearer Register an AI system for EU AI Act data lineage tracking (system_name, company, risk_tier, training_data_sources…)
GET /ai-systems Bearer List all registered AI systems for the current user
GET /ai-systems/{id}/compliance Bearer EU AI Act 15-article compliance checklist (Arts. 4–6, 9–15, 17, 25, 27, 30, 33) — per-article pass/partial/fail with evidence-based scoring, overall score, verdict
POST /ai-systems/{id}/certificate Bearer Generate PDF compliance readiness certificate — certificate ID, valid 1 year, SELF-ASSESSMENT ONLY watermark, DECLARATION ONLY badges
GET /billing/subscription Bearer Current plan (free / growth / enterprise), evaluations used this month, period end date
POST /billing/create-checkout-session Bearer Start Stripe Checkout for Growth plan ($299/mo) — returns checkout_url
POST /billing/portal Bearer Open Stripe Customer Portal to manage / cancel subscription — returns portal_url
POST /billing/webhook Stripe Stripe webhook receiver — signature-verified; handles checkout.session.completed, subscription created/updated/deleted
POST /proxy-variable-report Bearer Run standalone ECOA proxy variable scan on a context dict; returns proxy_variables_detected, compound_risks, severity summary
POST /evidence/extract Bearer Upload PDF/TXT/MD/CSV + article_key; Claude extracts compliance evidence and returns notes, date, verdict (pass/partial/insufficient), confidence
POST /evidence/interview Bearer Submit Q&A answers for an article; Claude scores them and returns verdict, feedback, and list of gaps
GET /evidence/questions/{article_key} Bearer Return the 4–5 structured interview questions for any of the 9 interviewable articles (Art. 4, 9, 10, 11, 17, 25, 27, 30, 33)
GET /ai-systems/{id}/history Bearer Return ordered compliance snapshots for a system (for trend charts in the Dashboard)
GET /dashboard/summary Bearer Return all registered systems with latest compliance scores + snapshot history for the Dashboard tab
GET /notifications/unsubscribe Public One-click unsubscribe via token query param
POST /notifications/preferences Bearer Update email notification opt-in/out preferences
GET /health-check Public Model availability — model.pragma, model.claude, model.openai

Python SDK

Separate installable package that wraps OpenAI-compatible clients with one line of code.

Integration

from openai import OpenAI
from pragma import Pragma, ComplianceError

client = Pragma(
    OpenAI(),
    policy_id="hr-compliance-v1",
    pragma_api_key="pragma_...",
    mode="block",          # block | flag | audit
    block_threshold=0.8,   # tune sensitivity
)

try:
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role":"user",
          "content":"Reject her — she is 58."}]
    )
except ComplianceError as e:
    print(e.result.firewall_action)  # "block"
    print(e.result.risk_flags)       # ["bias","discrimination"]
    print(e.result.violations[0].regulation)

Architecture

  • Pragma() / AsyncPragma() — factory functions returning wrapped client
  • Intercepts chat.completions.create() — same interface as underlying client
  • Calls POST /evaluate-decision on the Pragma backend before forwarding to OpenAI
  • Mode block — raises ComplianceError with full ComplianceResult
  • Mode flag — passes through, attaches .pragma_result to response
  • Mode audit — passes through silently, violations logged server-side
  • Supports sync + async, OpenAI + AzureOpenAI
  • Configurable block_threshold per client instance

Authentication Flow

Two paths: Google SSO or Guest. Both return a bearer token used for all subsequent requests.

Google SSO Flow

1

Load GIS SDK

Google Identity Services JS loaded on page init.

2

User Clicks Sign In

Google popup appears. User selects their account.

3

ID Token Returned

Google returns a signed JWT credential to the callback.

4

POST /auth/google

Token sent to backend. google-auth SDK verifies with Google's public keys.

5

Session Created

Backend returns opaque session token stored in localStorage.

Guest Flow

1

Click Continue as Guest

No credentials required. Immediate access.

2

POST /auth/guest

Backend generates a random anonymous ID and session token.

3

Token Stored

Same bearer token stored in localStorage. Expires in 24 hours.

4

Full Access

Guest users get full evaluate + PDF + feedback features. No name/picture shown.

Data & Privacy

No personal data stored. Feedback drives model improvement, not user profiling.

What We Store

  • Anonymous session ID (SHA-256 hash, not reversible)
  • Request timestamp
  • Context field names only (not values)
  • Decision word count (not the text)
  • Provider used (pragma / claude / openai / mock)
  • Confidence score, risk flag count, decision category
  • Feedback ratings (👍 / 👎) per category — for model retraining
  • Audit log: sha256 input hash, firewall verdict, proxy vars detected, HITL override reason
  • AI system profiles (company name, risk tier, training data sources) — EU AI Act Art. 10
  • Compliance certificates (certificate ID, articles status, issue date)
  • User email (Google-authenticated users only) — for compliance notifications
  • Notification log — deduplication of sent welcome/gap/countdown emails
  • Daily compliance score snapshots per system — powers Dashboard trend charts

What We Never Store

  • User email or name
  • Google account details
  • The actual decision text
  • Context field values
  • IP addresses
  • Any personally identifiable information

PostgreSQL Schema

-- Request metadata (no PII, no decision text) CREATE TABLE request_logs ( id SERIAL PRIMARY KEY, anon_id TEXT, -- SHA-256 of session sub, not reversible timestamp TEXT, -- ISO 8601 UTC context_keys TEXT, -- JSON array of field names ["gender","role"] decision_words INTEGER, -- word count only provider TEXT, -- pragma | claude | openai | mock confidence REAL, risk_count INTEGER, risk_categories TEXT, -- JSON array ["bias","fairness"] category TEXT -- hiring | finance | healthcare | ... ); -- Feedback for model retraining flywheel CREATE TABLE analysis_feedback ( id SERIAL PRIMARY KEY, anon_id TEXT, timestamp TEXT, rating INTEGER, -- 1 = thumbs up, -1 = thumbs down category TEXT, provider TEXT, model_version TEXT, confidence REAL, risk_categories TEXT -- JSON array ); -- Immutable audit trail (fintech compliance + EU AI Act Art. 12) CREATE TABLE audit_log ( id SERIAL PRIMARY KEY, anon_id TEXT, timestamp TEXT, input_hash TEXT, -- sha256(decision+context) — no raw PII decision_words INTEGER, category TEXT, firewall_action TEXT, -- block | override_required | allow confidence REAL, risk_flags TEXT, -- JSON array proxy_vars TEXT, -- JSON array of detected ECOA proxy fields regulatory_refs TEXT, -- JSON array of triggered regulation names provider TEXT, model_version TEXT, hitl_override INTEGER, -- 1 if a human investigator overrode the verdict hitl_reason TEXT, -- investigator's written reason hitl_anon_id TEXT -- who overrode it (anon) ); -- EU AI Act data lineage — registered AI systems (15-article compliance) CREATE TABLE ai_systems ( id SERIAL PRIMARY KEY, anon_id TEXT, system_name TEXT, company_name TEXT, risk_tier TEXT, -- minimal | limited | high | unacceptable use_case TEXT, model_version TEXT, training_data_sources TEXT, -- JSON array (Art. 10 data governance) intended_purpose TEXT, geographic_scope TEXT, -- Declarative fields + evidence for Arts. 4, 6, 15, 17, 25, 27, 30, 33 -- Evidence scoring: declaration alone → "partial"; declaration + notes + date → "pass" art4_literacy_training BOOLEAN, art4_literacy_training_evidence_notes TEXT, art4_literacy_training_evidence_date TEXT, art6_annex_category TEXT, -- Annex III high-risk category art15_accuracy_metric TEXT, -- e.g. "AUROC 0.87, Precision 0.83" art15_robustness_tested BOOLEAN, art17_qms_documented BOOLEAN, art17_qms_documented_evidence_notes TEXT, art17_qms_documented_evidence_date TEXT, art25_instructions_provided BOOLEAN, art25_instructions_provided_evidence_notes TEXT, art25_instructions_provided_evidence_date TEXT, art25_monitoring_active BOOLEAN, art25_monitoring_active_evidence_notes TEXT, art25_monitoring_active_evidence_date TEXT, art27_fria_conducted BOOLEAN, art27_fria_conducted_evidence_notes TEXT, art27_fria_conducted_evidence_date TEXT, art30_eu_db_registered BOOLEAN, art30_registration_number TEXT, -- EU AI database registration number art30_eu_db_registered_evidence_notes TEXT, art30_eu_db_registered_evidence_date TEXT, art33_conformity_type TEXT, -- self-assessment | third-party art33_conformity_type_evidence_notes TEXT, art33_conformity_type_evidence_date TEXT, created_at TEXT, updated_at TEXT ); -- EU AI Act compliance certificates CREATE TABLE compliance_certificates ( id SERIAL PRIMARY KEY, certificate_id TEXT UNIQUE, -- e.g. PRAGMA-A3F9C2 ai_system_id INTEGER, anon_id TEXT, issued_at TEXT, valid_until TEXT, -- 1 year from issue overall_score REAL, articles_status TEXT, -- JSON {art_4: "pass", ..., art_33: "partial"} total_evaluations INTEGER, hitl_overrides INTEGER, proxy_vars_caught INTEGER -- PDF includes SELF-ASSESSMENT ONLY watermark and DECLARATION ONLY badges ); -- Stripe billing subscriptions CREATE TABLE subscriptions ( id SERIAL PRIMARY KEY, anon_id TEXT UNIQUE NOT NULL, plan TEXT NOT NULL DEFAULT 'free', -- free | growth | enterprise status TEXT NOT NULL DEFAULT 'active', stripe_customer_id TEXT, stripe_subscription_id TEXT, current_period_end TEXT, -- ISO 8601 UTC created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); -- User profiles (email capture + notification preferences) CREATE TABLE users ( id SERIAL PRIMARY KEY, google_sub TEXT UNIQUE NOT NULL, email TEXT, name TEXT, unsubscribe_token TEXT UNIQUE, notify_welcome BOOLEAN DEFAULT TRUE, notify_gap BOOLEAN DEFAULT TRUE, notify_countdown BOOLEAN DEFAULT TRUE, created_at TEXT, updated_at TEXT ); -- Notification deduplication log CREATE TABLE notification_log ( id SERIAL PRIMARY KEY, google_sub TEXT NOT NULL, notification_type TEXT NOT NULL, -- welcome | gap_reminder | countdown system_id INTEGER, -- NULL for user-level notifications sent_at TEXT NOT NULL ); -- Daily compliance score snapshots per system (for Dashboard trend charts) CREATE TABLE compliance_snapshots ( id SERIAL PRIMARY KEY, google_sub TEXT NOT NULL, system_id INTEGER NOT NULL, score REAL NOT NULL, verdict TEXT NOT NULL, passes INTEGER NOT NULL, partials INTEGER NOT NULL, fails INTEGER NOT NULL, articles_json TEXT NOT NULL, -- {art_key: status} snapshot taken_at TEXT NOT NULL -- ISO UTC timestamp, one per day (deduped) ); -- Dynamic compliance rule engine (DB-backed, live config, per-org overrides) CREATE TABLE compliance_rules ( id SERIAL PRIMARY KEY, rule_id TEXT UNIQUE NOT NULL, -- e.g. "holc_redlining", "ecoa_proxy_zip" category TEXT, -- lending | hiring | healthcare | all description TEXT, default_action TEXT NOT NULL, -- FAIL | FLAG | ALLOW threshold REAL, -- configurable severity threshold enabled BOOLEAN DEFAULT TRUE, created_at TEXT, updated_at TEXT ); CREATE TABLE org_rule_overrides ( id SERIAL PRIMARY KEY, org_id INTEGER NOT NULL, rule_id TEXT NOT NULL, action TEXT, -- overrides default_action for this org threshold REAL, enabled BOOLEAN, updated_by TEXT, -- anon_id of admin who made the change updated_at TEXT ); -- Customer analytics — feature adoption and user behavior CREATE TABLE user_profiles ( id SERIAL PRIMARY KEY, anon_id TEXT UNIQUE NOT NULL, plan_tier TEXT DEFAULT 'free', login_method TEXT, -- google | guest total_evaluations INTEGER DEFAULT 0, session_count INTEGER DEFAULT 0, has_api_key INTEGER DEFAULT 0, features_used TEXT, -- JSON array of feature names last_active TEXT, created_at TEXT ); CREATE TABLE feature_events ( id SERIAL PRIMARY KEY, anon_id TEXT NOT NULL, event_name TEXT NOT NULL, -- decision_evaluated, report_downloaded, … properties TEXT, -- JSON dict of event properties created_at TEXT NOT NULL ); -- Law Research Agent findings (self-updating rule corpus) CREATE TABLE law_agent_findings ( id SERIAL PRIMARY KEY, source TEXT NOT NULL, -- federal_register | eeoc source_url TEXT NOT NULL, url_hash TEXT NOT NULL UNIQUE, -- sha256[:16] for dedup title TEXT NOT NULL, published_date TEXT NOT NULL, raw_summary TEXT NOT NULL, relevant INTEGER DEFAULT 0, -- 1 = Claude marked relevant regulation_name TEXT, -- Claude-extracted regulation name statute_citation TEXT, -- e.g. 42 U.S.C. § 2000e-2 key_requirement TEXT, -- one-sentence summary jurisdiction TEXT, -- federal | california | new_york | … rule_key TEXT, -- snake_case rule key rule_config_json TEXT, -- full rule config as JSON status TEXT DEFAULT 'pending', -- pending | active | rejected | not_relevant activate_at TEXT, -- ISO — auto-activates here activated_at TEXT, rejected_at TEXT, reject_reason TEXT, discovered_at TEXT NOT NULL );

Two-Layer Firewall

L1 deterministic rules run synchronously in microseconds. L2 LLM analysis runs only when L1 passes — or to enrich context.

⚡
L1 — Deterministic
HOLC geo · text scanner · ECOA proxy
disparate impact · state overlays
→ if pass →
🤖
L2 — Claude LLM
Regulatory reasoning · risk flags
evidence generation · confidence score
→
📋
Verdict + Audit Record
BLOCK / FLAG / ALLOW
W3C PROV JSON-LD export

L1 Compliance Engine

Deterministic rule evaluation: HOLC historically redlined zip codes (500+ across 35 US cities), ECOA proxy variables (35+ fields), indirect phrasing (14 categories), compound risk detection, state law overlays (CA/NY/IL). DB-backed rules with per-org overrides — no redeploy needed.

L2 — Claude (Anthropic)

claude-sonnet-4-6 primary. Streams regulatory reasoning across EU AI Act, ECOA, EEOC, FCRA, CFPB. Also powers evidence analyzer (document upload validation) and guided interview scoring.

Outputs

  • Firewall verdict: BLOCK / FLAG / ALLOW
  • Regulatory refs + risk flags (JSON)
  • ECOA §1002.9 adverse action notice
  • FCRA §615(a) consumer disclosure
  • EEOC 4/5ths disparate impact report
  • W3C PROV JSON-LD audit export
  • PDF compliance certificate

Deployment Guide

Four services, each on the right platform for its workload.

1
Railway — Backend

Deploy FastAPI

Connect GitHub repo. Railway auto-detects Python via requirements.txt.

railway.app → New Project
→ Deploy from GitHub
→ Add PostgreSQL plugin
→ Set env vars → Deploy

Required env vars: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_CLIENT_ID, ALLOWED_ORIGINS, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_GROWTH_PRICE_ID, RESEND_API_KEY, ADMIN_EMAIL, SENTRY_DSN, CRON_SECRET, GEMINI_API_KEY

2
Vercel — Web Frontend

Deploy Web App

Static HTML. Set root directory to frontend/ in Vercel dashboard.

vercel.com → New Project
→ Import pragma
→ Root: frontend/
→ Deploy

Update PROD_URL in frontend/index.html to your Railway URL.

3
HuggingFace — Pragma Model

Train & Serve Model

Fine-tune on Colab (free T4 GPU), push adapter to HuggingFace Hub, activate via env var.

python ml/generate_data.py --n 5
python ml/train.py \
--hf_repo user/pragma-ethics-v1
→ Set CUSTOM_MODEL_REPO in Railway
4
Expo EAS — Mobile

Build Mobile App

Build and submit to App Store / Play Store via Expo EAS.

cd mobile
npm install -g eas-cli
eas login
eas build --platform ios
eas submit --platform ios

Update PROD_URL in mobile/src/config.ts to your Railway URL.

Full Tech Stack

Every library and service across all layers.

Backend

Python 3.11+ FastAPI Uvicorn Pydantic v2 SQLAlchemy 2.x PostgreSQL psycopg2-binary anthropic SDK openai SDK stripe huggingface-hub google-auth reportlab python-dotenv httpx resend pypdf

ML Pipeline

PyTorch Transformers PEFT (LoRA) TRL (SFTTrainer) BitsAndBytes (4-bit) Datasets Accelerate Phi-3-mini-4k-instruct HuggingFace Hub Google Colab (T4) Ollama

Frontend & Mobile

HTML5 / CSS3 Vanilla JS Google Identity Services React Native Expo SDK TypeScript expo-linear-gradient expo-blur React Navigation AsyncStorage

Infra & Testing

Railway Vercel Expo EAS HuggingFace Inference API pytest pytest-cov httpx GitHub Actions Nixpacks Resend

Security Hardening

Controls applied across the stack as of Q2 2026.

XSS Prevention

esc() helper using document.createTextNode() applied to every dynamic HTML injection site in the frontend — system name, company, article evidence, risk flags, counterfactual keys, wizard review rows. No raw innerHTML from server data.

CORS Hardening

Explicit origin allowlist via ALLOWED_ORIGINS env var replaces the previous allow_origins=["*"]. Wildcard origins allow any site to make authenticated API calls from a logged-in user's browser — the allowlist prevents this.

Input Limits

Decision text capped at 4,000 characters. Context payload capped at 8,000 characters. Both enforced at the FastAPI layer with HTTP 400 before the LLM is called — prevents prompt injection via oversized inputs.

Batch Rate Limiting

The /evaluate-batch endpoint now checks the monthly plan limit before processing any rows. Previously, free users could bypass the 100 eval/month limit by uploading a 100-row CSV in a single batch call.

SQL Injection Guard

Column names in the ALTER TABLE migration path are validated against re.compile(r'^[a-z_][a-z0-9_]*$') before being interpolated into SQL. Unknown column names are logged and skipped rather than executed.

Stripe Webhook Auth

Webhook payloads are verified with stripe.WebhookSignature.verify_header() using the signing secret before any database writes occur. Unsigned or tampered events return HTTP 400 immediately.