ObscureIQ Hostility Index
Survey Portal
A complete reference covering how every part of this system works — from survey intake on the WordPress site to advanced report delivery — written for the ObscureIQ team and developers.
System Overview
What this system does and how its three parts fit together
The Hostility Index Survey Portal is a PHP/MySQL web application at surveys.obscureiq.com. Three core responsibilities:
Key principle: Scoring happens entirely in the browser. The server stores and displays scores — it never recalculates them.
Technology Stack
Languages, frameworks, and infrastructure
| Layer | Technology | Notes |
|---|---|---|
| Server language | PHP | No framework — plain PHP files |
| Database | MySQL | Connected via mysqli |
| Frontend | Vanilla JS / HTML / CSS | No React, no Vue — pure browser JS |
| Hosting | Hostinger | Shared hosting, files in public_html/ |
| PHP mail() | Server-side SMTP via Hostinger | |
| Survey frontend | WordPress Shortcode | [hostility_index_survey] on obscureiq.com |
| Admin auth | PHP Sessions | Hardcoded password, 1-hour timeout |
| Client auth | Token + credentials | protected_pages table, 72-hour expiry |
Database
Schema, tables, and what gets stored where
db_config.phpdb_config_local.php for dev Table: survey_data — one row per submission
| Column | Type | Notes |
|---|---|---|
| id | INT AUTO_INCREMENT | Primary key |
| type | VARCHAR | basic or advanced |
| name | VARCHAR(255) | NULL for basic — set when user requests advanced analysis |
| VARCHAR(255) | NULL for basic — set after OTP verification | |
| score | INT | Overall Hostility Index (0–196) |
| risk_level | VARCHAR(50) | LOW / MODERATE / ELEVATED / HIGH / CRITICAL |
| data | LONGTEXT (JSON) | risk_factors, recommendations, responses, response_labels |
| share_id | VARCHAR(64) | Random 16-char hex on insert — used for share/upgrade links |
| created_at | DATETIME | Auto-set on insert |
| admin_pdf_path | VARCHAR | Server path to uploaded admin PDF |
| admin_protected_link | VARCHAR | URL saved by admin for internal reference |
| admin_protected_password | VARCHAR | Password saved by admin for internal reference |
Table: email_verification — temporary OTP storage
| Column | Type | Notes |
|---|---|---|
| email / name | VARCHAR(255) | User's email and name/alias |
| otp_code | VARCHAR(6) | 6-digit one-time code |
| survey_data | LONGTEXT | Full survey JSON copied here for the verify step |
| expires_at | DATETIME | 10 minutes from creation |
| verified / used | BOOLEAN | Set TRUE after verification; prevents reuse |
| verification_ip | VARCHAR | Client IP at request time |
Table: protected_pages — client access credentials
| Column | Type | Notes |
|---|---|---|
| survey_id | INT | FK → survey_data.id |
| access_token | VARCHAR | UUID in the email link URL |
| username / password | VARCHAR | Auto-generated, sent to client in email |
| expires_at | DATETIME | 72 hours from creation |
| last_login / access_count | DATETIME / INT | Tracked on each login |
File Structure
Every file in public_html/ and what it does
* = actively called in the production flow
Survey Flow — End to End
From “Start Assessment” to client receiving their report
100% client-side JS on the ObscureIQ WordPress site. On “Complete Assessment”: answers collected, scores calculated, results shown instantly, full payload POSTed to api/submit_survey.php.
submit_survey.php validates API key + origin, inserts a type=basic record, generates a share_id, emails the team, returns { survey_id, share_id }.
- Modal asks for name + email
verify_email.php(send_otp) stores a 6-digit OTP and emails it- User enters OTP → verified → basic record upgraded to advanced
- Team receives “EMAIL VERIFIED — Advanced Analysis Request”
Login to secured.php. View full Q&A answers, generate the report, upload a PDF, save internal notes.
Team clicks Send Alert → send_notification.php creates protected_pages row with token + auto credentials. Client emailed secure login. They log in via login.php → land on premium.php.
API Endpoints
All api/ endpoints — auth requirements and request/response shapes
Content-Type: application/json · X-API-Key header · Origin in allowed listNew submission: { score, risk_level, risk_factors, recommendations, responses, response_labels }
Response 201: { message, survey_id, share_id }
Upgrade to advanced: { share_id, name, email }
Send: { action:"send_otp", name, email, survey_data }
Verify: { action:"verify_otp", email, otp_code }
Called if submit_survey.php fails. Emails team only — no DB save. Body: { notification_only:true, score, risk_level }
Generates UUID token + auto credentials, creates protected_pages row, emails the client.
Body: { survey_id, custom_url, message } · Returns: login URL, username, password, expiry
multipart/form-data with survey_id + pdf_file. Stores file, saves path to admin_pdf_path.
Saves URL + optional password against a survey for internal admin tracking. Not sent to client.
Admin Dashboard
What you can do from secured.php
index.phpTab 1 — Basic Surveys
Shows type=basic. Auto-deleted after 7 days on every dashboard load.
Tab 2 — Advanced Surveys
Shows type=advanced. Permanently stored.
Export CSV button at top of this tab downloads all advanced survey data.
Tab 3 — Generate Advanced Report
Enter a Survey ID → Generate Report → formatted HTML page → print to PDF. Optional Analyst Pre-Analysis Note influences report tone (never shown verbatim to client).
Advanced Report Generation
How generate_advanced_report.php builds the analysis document
How it works
- Loads the survey record from DB by Survey ID
- Reads score, risk factors, response labels, heat tags
- Computes a deterministic seed from: client name + survey ID + score
- Each section selects from pools of pre-written phrases using that seed — same survey = same report, but two clients in the same band see different wording
- Renders full HTML; admin prints to PDF from browser
Report sections (24 total, in order)
| Section | What it contains |
|---|---|
| Report Snapshot | Client, date, score, risk band, exposure state |
| Risk Band Legend | What each band means in practice |
| Two-Sentence Truth | Two sentences on the client's core risk mechanism |
| What This Report Means For You | 6 plain-language bullets |
| Bottom Line Assessment | 3 lines: status, linkage, timing warning |
| Likely Threat Scenarios | Two named scenarios — 3 paragraphs each |
| In Plain Terms | What is happening / Why it matters / What accelerates risk |
| Likely Trajectory If Unchecked | Now / Next / Failure mode |
| Recommended Posture | Posture label + 4 actions |
| Index Overview | One paragraph |
| Analytical Summary | One paragraph |
| Primary Drivers | 4 bullets |
| Overall Read | One paragraph |
| Hostility Index Breakdown | Per-category scores + 2 bullets each |
| Heat Tag Risk Index | Selected tags + capped score |
| Activation Vectors / Fragility Zones / Triggers | How risk activates, exposed surfaces, escalation triggers |
| Threat Archetypes / Trajectory / Confidence | Structural profile, forecasts, data quality assessment |
| Interpretation + Takeaway | Final paragraphs + posture reminder |
Interpretive signal (internal only)
The synthesizeInterpretiveSignal() function reads the client’s optional free-text (Step 17) and the analyst’s pre-analysis note. It classifies them via keyword matching and selects an internal tone value (drift, tense, close, or persistent) that subtly shapes phrase selection. Neither the client text nor the analyst note is ever rendered verbatim.
Client Delivery
Protected links, PDFs, and how clients access their reports
Sending a secure link
Enters the results URL (defaults to premium.php) and writes a custom message.
api/send_notification.php creates credentials- UUID access token generated
- Auto username + password created
- Row inserted into
protected_pages(72-hour expiry) - Client emailed: login URL, credentials, admin’s message
Visits link → login.php?token=… → enters credentials → lands on premium.php
Token expiry: Link expires after 72 hours. Client needs a new link from admin after that.
Admin PDF (internal only)
Upload PDF via the Upload PDF button → stored on server → viewable at admin_pdf.php?id={id}. Not automatically sent to the client.
Email Notifications
When, to whom, and what they contain
| Trigger | To | Subject / Content |
|---|---|---|
| New survey submitted | Team | [ObscureIQ] New Survey Submission - {type} ({risk} Risk) |
| OTP verified (advanced request) | Team | [ObscureIQ] VERIFIED Advanced Analysis Request - {risk} Risk |
| Basic completion fallback | Team | [ObscureIQ] Survey Completed - Basic ({risk} Risk) |
| User requests advanced analysis | User's email | 6-digit OTP code, expires 10 minutes |
| Admin clicks Send Alert | Client's email | Secure login URL, username, password, admin's custom message |
Scoring System
How the Hostility Index score (0–196) is calculated
All scoring is browser-side JavaScript. The server stores the score as-is. To change scoring logic, update the WordPress shortcode JS file.
Structural score (max 121)
| Category | Fields | Max |
|---|---|---|
| Public Profile | professional_visibility + public_recognition + obsessive_attention | 21 |
| Financial Visibility | financial_visibility + financial_inquiries | 6 |
| Social Media Reach | social_media_reach | 6 |
| Job / Level | job_risk + sensitive_topics | 10 |
| Threat Appetite | harassment_experience + doxxed_experience + safety_feeling | 24 |
| Public Records | public_records (sum) + address_visibility + media_appearance | 11 |
| Identity Threat Amplifiers | targeted_groups (sum) + identity_harassment | 9 |
| Network & Proximity Risk | family_visibility + network_targeting | 9 |
| Volatility / Visibility Risk | attention_spike + polarizing_stances | 13 |
| Identity Disclosure | public_affiliations + controversial_topics + public_advocate | 12 |
Heat Tags (max 30–75 based on Identity Disclosure)
Overall Heat Tag cap: Identity Disclosure <4 → 30 · 4–7 → 60 · ≥8 → 75
overall_score = Math.min(structural + heat_tags, 196)Risk bands
Authentication & Sessions
How admin and client logins work
Admin login
index.php has a hardcoded password. On success sets $_SESSION['logged_in']=true. All dashboard pages check this on every load. Timeout: 1 hour of inactivity.
Client login
login.php?token=… looks up token in protected_pages, verifies username + password, then sets session vars and redirects to protected.php or premium.php.
Data Retention
What gets kept, deleted, and when
secured.php load. Anonymous score data only.Note: Basic deletion only runs when an admin visits secured.php. A cron job would eliminate this dependency.
Advanced Analysis Architecture
How the analysis engine works and what data it receives
The analysis is generated in one file: generate_advanced_report.php. Every report section is a dedicated PHP function in this file, each producing structured output based on the survey data.
How the analysis engine works
Each section is produced by a dedicated PHP function (e.g. twoSentenceTruth(), likelyThreatScenarios(), wtmBullets()) that selects from pools of carefully written phrases using a deterministic seed derived from the client’s data. Same survey always produces the same report; different clients in the same risk band see different phrasing.
Complete data set available per survey record
{ score, max } for all 11 categoriesPrivacy constraint: The client’s free-text and analyst notes are never rendered verbatim in the report output. They are used only to infer tone and framing, not echoed back.
Security Notes
Known issues and things to be aware of
db_config.php has DB username + password in plain text. Hostinger blocks direct access by default — verify .htaccess if adding new paths.Access-Control-Allow-Origin: * was set during debugging. Should be locked to the allowed origins list (obscureiq.com, surveys.obscureiq.com) when stable..env file for easier rotation.secured.php. A scheduled cron job would run it independently.
One of the world’s leading experts on data brokers and commercial surveillance, Jeff has spent years mapping the architecture of the commercial data ecosystem — understanding what’s being collected, by whom, and exactly how to neutralize it.
The product vision behind this entire survey system — from the Hostility Index scoring model to every section of the Advanced Analysis report — was shaped and guided by Jeff’s deep expertise in privacy risk, identity exposure, and threat profiling. His fingerprints are on every page of this portal.