Data Models
Findings flow through the platform as JSON documents. Python owns the canonical model shapes (services/src/core/models.py, pydantic), Rails persists them verbatim into jsonb columns, and the report generator consumes the stored documents.
Python models (core/models.py)
Section titled “Python models (core/models.py)”DomainFinding
Section titled “DomainFinding”The lifecycle object for a single lookalike domain. Carries the domain, the fuzzer that generated it, its enrichment payloads, its scoring result, and its AI analysis.
Enrichment sub-models
Section titled “Enrichment sub-models”| Model | Holds |
|---|---|
DnsData |
DNS records (A/AAAA/MX/NS/TXT), resolver data |
RegistrationData |
RDAP data with raw WHOIS as fallback; registrar, registrant, abuse contacts, dates |
HttpData |
HTTP response metadata, title, headers, HTML fingerprint (html_lsh via ssdeep/tlsh), redirects |
SslData |
TLS certificate info, SANs, issuer, validity |
InfraData |
ASN, hosting provider, co-hosted domains, JARM fingerprint, open ports |
Social models
Section titled “Social models”| Model | Holds |
|---|---|
SocialFinding |
A detected impersonating profile: platform, handle, display name, bio, follower counts, verification, detection method, match signals, scoring, AI analysis |
SocialMatchResult |
Embedding-based identity match outcome against the brand |
SocialScoringResult |
Severity score for a social finding |
AiAnalysis
Section titled “AiAnalysis”Holds both the stage-5 deterministic AI analysis and the stage-7 LLM audit fields:
summary,visual_similarity,content_similarity(deterministic)audit_adjustment,audit_confidence,audit_is_false_positive,audit_false_positive_reasonaudit_overlooked_details,audit_relations,audit_additional_risksaudit_recommended_action,audit_insight,audit_model,audit_provideradjusted_score,adjusted_severity,audited_at
Rails persistence
Section titled “Rails persistence”Rails stores these documents in jsonb columns so no schema migration is needed when a signal is added.
Domain findings — findings table
Section titled “Domain findings — findings table”| Column | Notes |
|---|---|
domain |
required, indexed |
fuzzer |
which generator produced it |
original_domain |
the owned domain it derives from |
dns_data, registration_data, http_data, ssl_data, infra_data |
jsonb enrichment payloads |
geoip |
jsonb |
scoring |
jsonb: score, severity, signals, threats, is_phishing, flags, debug |
ai_analysis |
jsonb — see AiAnalysis above |
notes |
jsonb array of analyst notes |
screenshot_path |
relative path under the screenshots volume |
feedback, feedback_reason, feedback_at, feedback_user_id |
analyst triage label |
Social findings — social_findings table
Section titled “Social findings — social_findings table”platform + handle (unique per scan_run), plus display_name, profile_url, avatar_path, bio, follower_count, following_count, post_count, account_created_at, verified, detection_method, match_signals, cross_links, notes, and the same scoring / ai_analysis / feedback fields.
Scans — scan_runs
Section titled “Scans — scan_runs”status (pending/queued/running/completed/failed/cancelled), message, progress, current_stage, stage_states (jsonb: per-stage {processed, total, error, status, progress, completed_at}), enrichment_pending, completed_at.
Enrichment profiles
Section titled “Enrichment profiles”domain_profiles— per owned domain:status, the five jsonb payloads,screenshot_path,screenshot_phash,notes,error,enriched_at.social_profiles— per social handle:status,display_name,bio/bios,avatar_path,logos,profile_url, counts,verified,notes,error,enriched_at.
Platform state
Section titled “Platform state”| Model | Purpose |
|---|---|
PlatformSetting |
key/value jsonb store; keys ai_provider, audit_config |
ScoringWeight |
versioned scoring weights (version, weights jsonb, sample_size, source, learned_at); scope :latest |
AuditEvent |
event log (event_type in a fixed list, metadata jsonb) |
ScanReport |
PDF artifacts; has_one_attached :report_pdf and :summary_pdf; ready? |
Users and brand profile
Section titled “Users and brand profile”User (with derived_keywords jsonb, keyword-expansion quota fields, next_run_at/scheduled_at for rescans, webhook_url, attached logo) has many OwnedDomain, BrandName, SocialHandle, Keyword, ScanRun. Each owned domain has one DomainProfile; each social handle has one SocialProfile.
The scoring document
Section titled “The scoring document”The scoring jsonb on a finding contains the full score trace:
{ "score": 74.2, "severity": "high", "is_phishing": true, "signals": [ { "key": "typosquat_distance", "category": "brand_overlap", "strength": 0.9, "weight": 0.8, "contribution": 0.72, "evidence": "..." } ], "threats": ["urlhaus", "abuseipdb"], "flags": ["hosted_on_bulletproof_asn", "fresh_registration"], "debug": { "breakdown": "...", "adjusted": 74.2 }}This document is what the dashboard debug panel renders and what the adaptive learner consumes.