Skip to content

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.

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.

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
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

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_reason
  • audit_overlooked_details, audit_relations, audit_additional_risks
  • audit_recommended_action, audit_insight, audit_model, audit_provider
  • adjusted_score, adjusted_severity, audited_at

Rails stores these documents in jsonb columns so no schema migration is needed when a signal is added.

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

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.

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.

  • 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.
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?

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 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.