Registro público
Informe de salud del softwareesquema 0.31.0 · métricas 2.5.0 · 2026-08-05 08:59 UTC

osintph / falconeye

Free self-hosted OSINT investigator's toolkit. Twelve tools in one: crypto tracing, phishing fingerprinting, domain intel, Telegram OSINT, IP reputation, email header forensics with LLM scam detection, Google dork generator, script deobfuscator. AGPL-3.0.

Python · JavaScript · HTMLAGPL-3.0★ 10 estrellas⑂ 3 forksdesde jun 2026Ver en GitHub ↗
TipoServicio de redcómo se determina

osintph/falconeye tiene un índice de salud de 39 sobre 100, lo que lo sitúa en la banda Débil. Su puntuación más alta es Vitality (70/100) y la más baja, Security (16/100). Se actualizó por última vez hace 10 días. Una sola persona concentra la mayor parte del trabajo reciente.

39
global / 100
Débil

Índice de salud del software

Las métricas se agrupan en categorías ponderadas sobre una escala estandarizada de 1 a 100. El resultado global parte de su media ponderada, calibrada contra la distribución del registro público para que las bandas tengan significado percentil; cuando la evidencia pública activa la Política de Jurisdicciones de Alto Riesgo, la calificación se ajusta y recibe un límite «En riesgo» de 34.

39
Excepcional93-100El nivel más alto del registro (≈ el 5% superior); cumple prácticamente todos los criterios evaluados
Excelente80-92Sólido en todos los frentes; carencias menores
Bueno65-79Saludable; carencias limitadas y manejables
Moderado50-64Aceptable con carencias notables; se recomienda revisión
Débil35-49Debilidades sustanciales en varias áreas
En riesgo20-34Debilidades significativas; su adopción exige cautela
Crítico1-19Problemas graves (proyecto abandonado, un solo mantenedor, sin higiene)
VitalidadComunidad yAdopciónSostenibilidady GobernanzaCalidad deIngenieríaSeguridadPreparaciónpara IA

Perfil de puntuación

Cada eje es una categoría. La forma importa más que la media: un proyecto sano llena toda la figura, mientras que un perfil de picos y cráteres indica que la fortaleza en una dimensión enmascara el riesgo en otra.

El resultado global ponderado 43 se calibra a 39 en la escala publicada del índice (calibración del registro 2026-08-02).

Titularidad

osintphCuenta personal
15 seguidores14 repositorios públicosdesde ago 2020OSINTPH

Este repositorio pertenece a una cuenta personal. Un proyecto con un único propietario conlleva más riesgo de continuidad que uno respaldado por una organización.

Métricas por categoría

Vitalidad

¿Está vivo el proyecto: se escribe código y se publican versiones?

70Bueno · 21% del índice global
Cómo se puntúa
28.8/36Recencia de push — último push hace 10 días
3.5/36Cadencia de commits — 5/52 semanas con commits
18/18Volumen de commits — 164 commits en el último año
0/10OpenSSF Scorecard: Maintained — project was created within the last 90 days. Please review its contents carefully
Datos de entrada utilizados
commits_last_year164
human_commit_share1
days_since_last_push10
active_weeks_last_year5
Cómo se puntúa
27/27Publica versiones — 33 versiones publicadas
36/36Recencia de las versiones — última versión hace 11 días
27/27Cadencia de publicación — una versión cada ~0,1 días
0/10OpenSSF Scorecard: Signed-Releases — sin datos
Datos de entrada utilizados
releases_count33
latest_release_tagv3.24.1
releases_from_tagsno
days_since_latest_release11
mean_days_between_releases0,1
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: Signed-Releases. Los pesos restantes se han renormalizado.

Comunidad y Adopción

¿Tiene el proyecto usuarios, descargas, atención y unas condiciones acogedoras para quienes contribuyen?

49Débil · 17% del índice global
Cómo se puntúa
15.5/60Estrellas — 10 estrellas
2.5/25Forks — 3 forks
0/15Observadores — 0 observadores
Datos de entrada utilizados
forks3
stars10
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Cómo se puntúa
22.5/22.5README
22.5/22.5Licencia — licencia reconocida (AGPL-3.0)
18/18Guía CONTRIBUTING
13.5/13.5Código de conducta
0/7.2Plantilla de issues
0/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_license
readme_badges0
has_contributing
has_issue_templateno
has_code_of_conduct
readme_badge_services
has_pull_request_templateno

Sostenibilidad y Gobernanza

¿Sobrevivirá el proyecto a sus personas: factor bus, capacidad de respuesta, quién lo respalda y mantenimiento del paquete?

20En riesgo · 23% del índice global
Cómo se puntúa
9/54Factor bus — la mitad de los commits recae en 1 contribuyente(s)
0/22.5Distribución de commits — el principal contribuyente firma el 100% de los commits
1.4/13.5Amplitud de contribuyentes — 1 contribuyentes
3/10OpenSSF Scorecard: Contributors — project has 1 contributing companies or organizations -- score normalized to 3
Datos de entrada utilizados
bus_factor1
contributors_sampled1
top_contributor_share1
Cómo se puntúa
0/42Resolución de issues — sin issues o sin datos
0/30Aceptación de PR — sin PR decididos o sin datos
0/13Newcomer PR acceptance — ningún PR de un contribuyente primerizo decidido en 30 d
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Datos de entrada utilizados
merged_prs0
open_issues0
closed_issues0
prs_merged_7d0
prs_decided_7d0
prs_merged_30d0
prs_decided_30d0
issue_closed_ratio
closed_unmerged_prs0
first_time_authors_30d0
first_time_prs_merged_30d0
first_time_prs_decided_30d0
Excluidos de la puntuación (sin datos o no aplicable): Resolución de issues, Aceptación de PR, newcomer_pr_acceptance. Los pesos restantes se han renormalizado.
Cómo se puntúa
10/30Respaldo de la propiedad — cuenta personal (usuario)
0/20Dominio verificado — no aplicable a cuentas de usuario
8.7/25Alcance del propietario — 15 seguidores de osintph
20.4/25Trayectoria — 14 repos públicos, cuenta de ~5 años
Datos de entrada utilizados
followers15
owner_typeUser
is_verified
owner_loginosintph
public_repos14
account_age_days2169
Excluidos de la puntuación (sin datos o no aplicable): Dominio verificado. Los pesos restantes se han renormalizado.

Calidad de Ingeniería

¿Existen unas prácticas mínimas de ingeniería y documentación?

58Moderado · 19% del índice global
Cómo se puntúa
0/24Flujos de trabajo de CI
24/24Pruebas presentes
0/16Configuración de linter
0/9.6Hooks de pre-commit
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — sin datos
Datos de entrada utilizados
has_cino
has_tests
has_editorconfigno
has_linter_configno
has_precommit_configno
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: CI-Tests. Los pesos restantes se han renormalizado.

Documentación

100Excepcional
Cómo se puntúa
30/30README
25/25Directorio de documentación
15/15Sitio de documentación / página del proyecto — https://falconeye.osintph.info
10/10Descripción del repositorio
10/10Topics — 20 topics
10/10Wiki
Datos de entrada utilizados
topicsosint, osint-tool, osint-toolkit, email-header-analyzer, bec-detection, phishing-analysis, google-dorks, powershell-deobfuscator, crypto-investigation, telegram-osint, threat-intelligence, ip-reputation, incident-response, claude-api, anthropic, fastapi, cybersecurity-tools, blue-team, security-tools, self-hosted
has_wiki
homepagehttps://falconeye.osintph.info
has_readme
has_docs_dir
has_description

Seguridad

¿Son sólidas las prácticas visibles de seguridad y de cadena de suministro, sin exposición jurisdiccional de alto riesgo sin resolver?

16Crítico · 16% del índice global
Cómo se puntúa
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
0/2.5CI-Tests — sin datos
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
0.8/2.5Contributors — project has 1 contributing companies or organizations -- score normalized to 3
0/10Dangerous-Workflow — sin datos
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5Licencia — license file detected
0/7.5Maintained — project was created within the last 90 days. Please review its contents carefully
0/5Packaging — sin datos
0/5Pinned-Dependencies — sin datos
0/5SAST — no SAST tool detected
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — sin datos
0/7.5Token-Permissions — sin datos
0/7.5Vulnerabilities — 18 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated12
scorecard_versionv5.5.0
checks_inconclusive6
scorecard_aggregate1,6
Excluidos de la puntuación (sin datos o no aplicable): ci_tests, dangerous_workflow, packaging, pinned_dependencies, signed_releases, token_permissions. Los pesos restantes se han renormalizado.

Preparación para IA

¿Hasta qué punto está el repositorio preparado para desarrollarse y mantenerse con agentes de codificación de IA? Tiene un peso deliberadamente pequeño (4%): las herramientas para agentes son una señal real de mantenimiento, pero un repositorio sin ninguna puede alcanzar igualmente 100/100.

41Débil · 4% del índice global
Cómo se puntúa
0/45Instrucciones para agentes — sin CLAUDE.md / AGENTS.md / reglas de editor
0/15Documentación legible por máquinas (llms.txt)
40/40Historial de commits legible — 100 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share1
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
0/18Arranque con un solo comando
22/22Pruebas automatizadas
0/11Configuración de lint / formato
0/11Verificación estática de tipos
0/10Entorno reproducible
10/10Práctica demostrada con agentes — 97 de los últimos 100 commits con autoría o crédito de agente
0/8Mantenimiento automatizado — no se observan actualizaciones automáticas de dependencias
0/10OpenSSF Scorecard: Pinned-Dependencies — sin datos
Datos de entrada utilizados
has_nixno
has_tests
lockfiles
has_dockerfileno
typed_languageno
bootstrap_files
has_devcontainerno
has_linter_configno
typecheck_configs
agent_commit_share0,97
toolchain_manifests
dependency_bot_commit_share0
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: Pinned-Dependencies. Los pesos restantes se han renormalizado.
Cómo se puntúa
0/45Código verificable por tipos — Python sin configuración de verificación de tipos
54.6/55Tamaños de archivo manejables — 1/149 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languagePython
largest_source_bytes352.772
source_files_sampled149
oversized_source_files1
Cómo se puntúa
0/40Esquema de API (OpenAPI/GraphQL/proto)
0/20Servidor MCP
40/40Ejemplos ejecutables — samples
Datos de entrada utilizados
example_dirssamples
has_mcp_signalno
api_schema_files

Datos clave

10estrellas de GitHub
1contribuidores
164commits en los últimos 12 meses
10días desde el último push
33versiones publicadas
1factor bus
0issues abiertas
PyPIecosistemas de paquetes

Advertencias de recopilación de datos

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Más detalle

Historial de estrellas y forks 0 ★ / 3 ⇿
0Estrellas
3Forks
14Versiones

Cuándo se añadió cada estrella y fork, recopilado de GitHub y agrupado por día. El crecimiento acumulado se sitúa justo encima de las adiciones diarias que lo componen, de modo que ambos se leen en conjunto: la acumulación orgánica sostenida no se parece en nada a un pico abrupto y efímero. Cuando esa diferencia es medible, se informa como autenticidad del crecimiento.

12233312026-072026-072026-07
Mayor 0Menor 7Parche 7
OpenSSF Scorecard 1.6 / 10
1.6agregado

Evaluación de seguridad independiente y agnóstica en cuanto a herramientas, procedente del proyecto de código abierto OpenSSF Scorecard. Cada comprobación premia una práctica de seguridad, no la herramienta de un proveedor concreto. Las comprobaciones que Scorecard no pudo determinar se marcan como n/d y se excluyen de la puntuación de seguridad (nunca se cuentan como cero).Scorecard v5.5.0 · 2026-08-05 08:58 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
n/dCI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
3Contributorsproject has 1 contributing companies or organizations -- score normalized to 3
n/dDangerous-Workflowno workflows found
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
0Maintainedproject was created within the last 90 days. Please review its contents carefully
n/dPackagingpackaging workflow not detected
n/dPinned-Dependenciesno dependencies found
0SASTno SAST tool detected
0Security-Policysecurity policy file not detected
n/dSigned-Releasesno releases found
n/dToken-PermissionsNo tokens found
0Vulnerabilities18 existing vulnerabilities detected
Todas las dependencias no recopilado

No fue posible recopilar el conjunto de dependencias resuelto para este informe: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Informe JSON sin procesar legible por máquina
{
  "data": {
    "icon": {
      "bytes": 1033,
      "width": 32,
      "height": 32,
      "rejected": [],
      "collected": true,
      "media_type": "image/png",
      "source_url": "https://falconeye.osintph.info/static/favicon-32x32.png",
      "source_type": "homepage",
      "content_hash": "2e27ea710cde98f7925205fe695dae49a3a2b32fe3ca4d6a7cd4befb215ca7d8",
      "candidates_considered": 1
    },
    "repo": {
      "topics": [
        "osint",
        "osint-tool",
        "osint-toolkit",
        "email-header-analyzer",
        "bec-detection",
        "phishing-analysis",
        "google-dorks",
        "powershell-deobfuscator",
        "crypto-investigation",
        "telegram-osint",
        "threat-intelligence",
        "ip-reputation",
        "incident-response",
        "claude-api",
        "anthropic",
        "fastapi",
        "cybersecurity-tools",
        "blue-team",
        "security-tools",
        "self-hosted"
      ],
      "is_fork": false,
      "size_kb": 1811,
      "has_wiki": true,
      "homepage": "https://falconeye.osintph.info",
      "languages": {
        "CSS": 13422,
        "HTML": 166515,
        "Shell": 5080,
        "Python": 842056,
        "JavaScript": 352772
      },
      "pushed_at": "2026-07-25T21:05:15Z",
      "created_at": "2026-06-22T13:12:54Z",
      "owner_type": "User",
      "updated_at": "2026-07-25T21:05:16Z",
      "description": "Free self-hosted OSINT investigator's toolkit. Twelve tools in one: crypto tracing, phishing fingerprinting, domain intel, Telegram OSINT, IP reputation, email header forensics with LLM scam detection, Google dork generator, script deobfuscator. AGPL-3.0.",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "AGPL-3.0",
      "default_branch": "main",
      "license_spdx_raw": "AGPL-3.0",
      "primary_language": "Python",
      "significant_languages": [
        "Python",
        "JavaScript",
        "HTML"
      ]
    },
    "owner": {
      "blog": "https://www.osintph.net",
      "name": "osintph",
      "type": "User",
      "login": "osintph",
      "company": "OSINTPH",
      "location": null,
      "followers": 15,
      "avatar_url": "https://avatars.githubusercontent.com/u/70299699?v=4",
      "created_at": "2020-08-27T00:08:09Z",
      "is_verified": null,
      "public_repos": 14,
      "account_age_days": 2169
    },
    "license": {
      "state": "standard",
      "spdx_id": "AGPL-3.0",
      "raw_spdx": "AGPL-3.0",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v3.24.1",
          "kind": "patch",
          "published_at": "2026-07-25T06:52:42Z"
        },
        {
          "tag": "v3.24.0",
          "kind": "minor",
          "published_at": "2026-07-25T06:27:45Z"
        },
        {
          "tag": "v3.23.0",
          "kind": "minor",
          "published_at": "2026-07-25T05:23:21Z"
        },
        {
          "tag": "v3.22.0",
          "kind": "minor",
          "published_at": "2026-07-25T03:17:23Z"
        },
        {
          "tag": "v3.21.0",
          "kind": "minor",
          "published_at": "2026-07-25T02:43:59Z"
        },
        {
          "tag": "v3.20.1",
          "kind": "patch",
          "published_at": "2026-07-25T01:24:26Z"
        },
        {
          "tag": "v3.20.0",
          "kind": "minor",
          "published_at": "2026-07-25T00:30:52Z"
        },
        {
          "tag": "v3.19.0",
          "kind": "minor",
          "published_at": "2026-07-24T11:34:59Z"
        },
        {
          "tag": "v3.18.0",
          "kind": "minor",
          "published_at": "2026-07-24T09:00:50Z"
        },
        {
          "tag": "v3.17.0",
          "kind": "minor",
          "published_at": "2026-07-24T07:45:18Z"
        },
        {
          "tag": "v3.16.0",
          "kind": "minor",
          "published_at": "2026-07-24T06:39:07Z"
        },
        {
          "tag": "v3.15.3",
          "kind": "patch",
          "published_at": "2026-07-22T09:34:09Z"
        },
        {
          "tag": "v3.15.2",
          "kind": "patch",
          "published_at": "2026-07-22T09:19:20Z"
        },
        {
          "tag": "v3.15.1",
          "kind": "patch",
          "published_at": "2026-07-22T08:54:40Z"
        },
        {
          "tag": "v3.15.0",
          "kind": "minor",
          "published_at": "2026-07-22T08:28:34Z"
        },
        {
          "tag": "v3.14.0",
          "kind": "minor",
          "published_at": "2026-07-22T06:39:02Z"
        },
        {
          "tag": "v3.13.0",
          "kind": "minor",
          "published_at": "2026-07-21T01:53:20Z"
        },
        {
          "tag": "v3.12.2",
          "kind": "patch",
          "published_at": "2026-07-21T00:35:32Z"
        },
        {
          "tag": "v3.12.1",
          "kind": "patch",
          "published_at": "2026-07-20T09:21:52Z"
        },
        {
          "tag": "v3.12.0",
          "kind": "minor",
          "published_at": "2026-07-20T09:13:35Z"
        },
        {
          "tag": "v3.11.0",
          "kind": "minor",
          "published_at": "2026-07-20T07:48:42Z"
        },
        {
          "tag": "v3.10.0",
          "kind": "minor",
          "published_at": "2026-07-20T06:13:33Z"
        },
        {
          "tag": "v3.9.1",
          "kind": "patch",
          "published_at": "2026-07-20T05:15:30Z"
        },
        {
          "tag": "v3.9.0",
          "kind": "minor",
          "published_at": "2026-07-20T04:24:29Z"
        },
        {
          "tag": "v3.8.3",
          "kind": "patch",
          "published_at": "2026-07-20T01:34:15Z"
        },
        {
          "tag": "v3.8.1",
          "kind": "patch",
          "published_at": "2026-07-19T23:06:56Z"
        },
        {
          "tag": "v3.8.0",
          "kind": "minor",
          "published_at": "2026-07-19T12:59:51Z"
        },
        {
          "tag": "v3.7.1",
          "kind": "patch",
          "published_at": "2026-07-19T11:16:06Z"
        },
        {
          "tag": "v3.6.0",
          "kind": "minor",
          "published_at": "2026-07-17T09:33:08Z"
        },
        {
          "tag": "v3.5.2",
          "kind": "patch",
          "published_at": "2026-07-17T08:32:33Z"
        },
        {
          "tag": "v3.5.1",
          "kind": "patch",
          "published_at": "2026-07-05T08:47:26Z"
        },
        {
          "tag": "v3.5.0",
          "kind": "minor",
          "published_at": "2026-07-05T03:13:40Z"
        },
        {
          "tag": "v3.1.0",
          "kind": "minor",
          "published_at": "2026-06-26T02:25:30Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "0719cea66764e4fbd45469712b33082e50a91e5d",
          "body": "…dit harness\n\nItem 5 (copy, on screen + PDF):\n- Avatar section rewritten with synthetic-face sourcing guidance (never reuse a\n  real photo; where to generate; match the face to the Cover).\n- Starting-point disclaimer added to the tab intro and under the PDF page-1\n  header (the dossier is reviewable\n[…]\nider locales.\nDoc sweep: README Legend cap corrected to default 25 (SOCKPUPPET_LLM_PER_DAY).\n\nDeterministic audit 0/249. Full suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.27.0: Sock Puppet - avatar guidance, starting-point disclaimer, au…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T21:05:06Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "43982cea5d242a8367d2f713d76299f92a04fc13",
          "body": "The Tier B employer template used the persona's surname as the company base\n(\"Safo\" -> \"Safo Media Ltd\"), reading as a self-owned firm; it hit every Tier B\npersona. Use an independent base (a redrawn locale surname that is not the\npersona's, else a neutral brandable word), plus a guard that redraws \n[…]\n a whole word.\n\n0 whole-word surname-in-employer collisions across 300 personas / 50 locales.\nDeterministic audit 0/249. Full suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.26.3: Sock Puppet - employer name never the persona's own surname",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T10:36:49Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "43fe37c16362cff3c641ef3009f572b653cec4bf",
          "body": "The residual sweep re-romanized the whole _roman field when any non-Latin\nremained, which also stripped legitimate accented Latin (Sao Paulo). Add\n_strip_residual_nonlatin: transliterate only characters >= U+0250 in place, keep\nalready-Latin characters including accents. Applied to name/street/city/region/\nemployer/job_title roman twins in _fold_legend.\n\nDeterministic audit 0/249. Full suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.26.2: Sock Puppet - in-place residual non-Latin sweep",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T10:02:37Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ccefd2136a3427c194fdc68038851b3963f7064c",
          "body": "Audit pass 4 (FI, CZ PDFs).\n\nP1 handles: LLM extra_handles were folded with no validation, producing foreign\nwords (\"lubos_vezeni\" = Czech for prison), a third language\n(\"amministrador_north\"), and wrong numbers (\"hruby_admin1992\" for a 2000 birth).\nNow each LLM handle is kept only if every word is \n[…]\nonas rarely land at 0 years unless a\nyoung age is explicitly requested.\n\nDeterministic audit 0/249 across all 10 checks. Full suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.26.1: Sock Puppet - validate LLM handles, widen employer, age floor",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T09:50:47Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "551e7a5ce7fbf93412e3371a1141f23e907a57bf",
          "body": "Audit pass 3 (three hand-checked PDFs: SG, BR, AU).\n\nP1 timezone from region: multi-zone countries returned\npytz.country_timezones[cc][0] regardless of region (AU always Lord_Howe, BR\nNoronha, RU Kaliningrad, CA St_Johns). Curated (city, region, timezone) table\nfor multi-zone countries + city-states\n[…]\nle pool 10 -> 27. (SG street and BR IBAN were misdiagnoses,\nsee reply.)\n\nDeterministic audit 0/249 across all 10 checks. Full suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.26.0: Sock Puppet - region-correct timezone + real Legend-off city",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T08:14:46Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "524eacde2cbdf46fda3a46b3f44cf55eb17d447c",
          "body": "years_experience was age - start_age while career_start_year was\nbirth_year + start_age; the age/birth-year offset let them disagree by a year\n(\"0 years, since 2025\" in 2026). Derive tenure from (current year - start year)\nso \"since YEAR\" and \"N years\" always match. Deterministic audit still 0/249\nacross all checks, now including a career-consistency check.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.25.2: Sock Puppet - tenure agrees with career start year",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T07:51:10Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c5bebfde3d62a2a4c65c17d3394ecc7e8768d734",
          "body": "A rare Legend slip could place the supplied city in a neighbouring country when\na region name was globally ambiguous (e.g. a city-state CDC district like\nSingapore's \"South East\"). Constrain the city to be inside the given region AND\nthe given country. Deterministic Cover unchanged.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.25.1: Sock Puppet Legend - pin the city to the persona country",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T07:44:58Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "678c0f81f15231b306d481d4529ecd1af9c1ce33",
          "body": "Audit across all 249 ISO countries (LLM off) found non-country-specific\nstructural breaks: department unrelated to job (100%), blank postcode (78%),\ncity/region drawn independently (21%), ISO long name shown (6%), native\nfield-noun job titles (5%), tenure that could contradict age.\n\nFix: build the C\n[…]\nCover pinned into the Legend prompt.\n\nRe-audit (LLM off): 0 failures across all deterministic checks, all 249\ncountries. Full suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.25.0: Sock Puppet coherence - self-consistent Cover, full pin",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T07:36:05Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "93aa55eb5d693b62b30dd4e41d937d4198c86792",
          "body": "The 249 server-rendered country options were concatenated on a single line, so\n`curl | grep -c '<option'` counted 1 line, not ~249 (the options were all\npresent and selectable; only the line-based count was misleading). Emit one\noption per line. Behavior unchanged from 3.24.2.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.24.3: Sock Puppet - one country <option> per line",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T07:05:37Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "958875884a02532d50a6cf824c2f7a033f40b665",
          "body": "The Country picker showed only \"Random country\": the full ISO list was\npopulated by a client-side fetch (initSockpuppet -> /api/sockpuppet/countries)\nthat never reached the served HTML, so no country could be selected and\ncurl of the page showed 1 option, not ~249.\n\n- serve_index now injects the ful\n[…]\npopulation; the tier note reads the option's\n  data-tier. Selecting a country generates that country; Random unchanged.\n\nFull suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.24.2: Sock Puppet - render the country list server-side",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T07:03:47Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fdbf52e32fce332845d847ab134c0a2ccccf5443",
          "body": "Correction pass. Non-Latin personas were broken: the PDF blanked native fields\n(jsPDF Latin-1), the username stem fell back to researchpersonaNNN, and the\nLegend invented a different city/employer than the Cover.\n\n- Romanization layer: every script-variable field carries a Latin twin\n  (*_roman). Tw\n[…]\nn final guard); no other tab touched.\n  Native-in-PDF (Noto fonts) left as a follow-up.\n\nNew dep: Unidecode. Docs swept. Full suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.24.1: Sock Puppet Generator - fix non-Latin locales",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T06:52:09Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b327a566d3672509bbcc5408c3c3623b2680a2df",
          "body": "New tab in the Identity cluster. Builds a coherent fictional persona for\nauthorized OSINT use: a Cover (deterministic surface fields) and an optional\nLegend (Haiku back-story that cannot contradict the Cover). Fictional only,\nnever impersonation of a real person.\n\n- Full ISO 3166 country coverage (p\n[…]\nck Puppet, LLM-tab list now four), JSON-LD feature list, version.\nNew deps: Faker, pycountry, phonenumbers, pytz, Babel. Full suite green (462).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.24.0: Sock Puppet Generator tab (fictional OSINT research personas)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T06:27:16Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "275c443df5062d5aff9bbbbb5a0f1bb480adaaaa",
          "body": "The Sandbox tab was quadruply redundant for URL reputation (IP Reputation,\nThreat Pulse, ThreatFox already cover abuse.ch). Its one unique capability,\nMalwareBazaar/URLhaus file-hash reputation, moves inline:\n\n- New checkHashReputation() renders URLhaus payload + MalwareBazaar results\n  inline, reus\n[…]\nist updated.\n\nAlso records the shared-DB self-init test sharp edge in the review doc.\nFull suite green (462). Four-place version bump to 3.23.0.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.23.0: remove Sandbox tab; hash reputation inline (review follow-up)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T05:22:50Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fee2da4a867c9c23859e44fc965cafc5ce5959b7",
          "body": "Internal refactor, no user-facing behavior change.\n\n- app/utils/rate_limit.py + app/utils/cache.py: one shared per-IP daily\n  limiter and one single-blob TTL cache. Migrated the 4 copy-pasted limiters\n  (dork/qr/url/script) and 5 caches (dork/script/email/ip_intel/threat_pulse)\n  onto them. Each tab\n[…]\np_sources (SourceResult contract).\n\ntests/unit/test_shared_stores.py (9 cases). Full suite green: 462 passed.\nFour-place version bump to 3.22.0.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.22.0: dedup — shared cache/rate-limit stores + abuse.ch client (P4)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T03:17:03Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8b09f91da4f079ed9884697436bb59756dd946f1",
          "body": "They were untracked AND un-ignored, so a stray `git add -A` would sweep the\nwhole virtualenv into a commit. Ignore them explicitly.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "gitignore: ignore local .venv/ and .venv312/ virtualenvs",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T02:51:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5aee7bc4eec4c2e210e22aa428f20376247fe966",
          "body": "Security\n- M-5: delete app/utils/ssrf.py (weak SSRF blocklist); crypto + image_search\n  now validate via the canonical safe_fetch.resolve_and_check.\n- requirements: add anthropic==0.119.0 + extract-msg==0.56.0 (clean install\n  could not boot the LLM tabs or parse .msg), pin pydantic==2.13.4, raise\n \n[…]\nge news strip kept; /api/news\n  endpoint kept (the strip uses it).\n\nFull suite green (453 passed, 2 skipped). Four-place version bump to 3.21.0.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.21.0: hardening batch + News tab removal (2026-07 review P2/P3)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T02:43:35Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "46c9c7e9b26b8a8764fadee3e23217514c7769ef",
          "body": "The prod box never deployed via git pull. Document the real mechanism\n(author on Mac, push from Mac, rsync to the box) in docs/deploy-runbook.md,\nincluding the uid-501 directory root cause and the one-time\n`chown -R ubuntu:ubuntu /opt/falconeye/app_src` that normalized it so\ngit reset --hard now works. Record the correction and the reconcile in the\narchitecture review, and check off P1 (shipped v3.20.1).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: canonical deploy runbook + record VPS ownership normalization",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T01:53:26Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8a9aba125874827ee3b81e16a60b3c4465b0d85c",
          "body": "Both are referenced in tracked markup (index.html: favicon 3x, og-image 2x)\nbut existed only on the production VPS, never in git — so a clone had broken\nreferences, which matters given AGPL and the documented self-hosting path.\nRecovered from the box (checksums verified) and committed. Surfaced while\nreconciling the VPS working tree to origin/main.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "assets: commit favicon-32x32.png and og-image.png",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T01:40:54Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "26007ee5822432ba71807c272eafa24fb832dfef",
          "body": "Attacker-controlled URLhaus values (malware URL strings, reference/download\nlinks) were interpolated into innerHTML unescaped. Six sinks now pass through\nthe existing escapeHtml/escapeAttr helpers (as PH Threat Pulse already does):\nrenderIpUrlhaus (reference href + Recent URLs text) and the GreyNois\n[…]\ny closed (that\npass covered Telegram/RDAP/RSS, not URLhaus).\n\nFour-place version bump to 3.20.1 (app metadata, /health, JSON-LD, ?v cache-bust).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.20.1: escape URLhaus DOM-XSS sinks (IP Reputation + Sandbox)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T01:23:58Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "35c8c9592e799877a3d298fc0cd842bb6f824960",
          "body": "Fresh-reader review at v3.20.0: which tabs earn their place, whether the\none-module-per-indicator architecture holds at 17 tabs, silent failure\nmodes, and the cross-tab case-context capability that would tie the tabs\ntogether. Records a ranked remediation checklist and which prior-review\nfindings th\n[…]\n that the\nFastAPI/Starlette upgrade is shipped (not open) and that the XSS pass was\nincomplete (URLhaus render paths in Sandbox AND the IP tab).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: architecture & purpose review 2026-07",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T01:05:37Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "e8465028a4cf8a3dfae204d485f32f98458b260e",
          "body": "Briefed against BGPview; a live check before writing code found it shut\ndown permanently on 2025-11-26 (api.bgpview.io no longer resolves), and its\nsuggested replacement (bgp.tools) has no per-ASN REST API or peers/upstreams\ndata at all. Built entirely on RIPEstat instead, already integrated\nelsewhere in this tab. See CHANGELOG for the resulting scope deltas\n(no cross-source disagreement flagging, no country-diversity metric).\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.20.0: IP Reputation ASN Intelligence — operator footprint pivot",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-25T00:29:40Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "72fe8654e08d4c1fa0d5da15cb786be1e1ec556d",
          "body": "Every panel heading now states its real date window (derived from\nMIN(discovered), never hardcoded), fixing a case where the PH/SEA bar\nchart was genuinely all-time while the month-over-month table next to\nit was silently capped to 6 months. Adds a tab-level range selector\n(30d/90d/12mo/ytd/all, def\n[…]\nred vs. all countries); the\nunderlying hybrid fetch logic is unchanged.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01X7PnvtqMzEKnEtijh5WfA8",
          "is_bot": false,
          "headline": "v3.19.0: Ransomware Watch time framing and country selector",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-24T11:34:27Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "64b39fbff178cc04725608c2c330081dc5b88495",
          "body": "…trol\n\nPure UI restructuring, no data layer/route/schema changes. Splits the\n8-panel single-scroll tab into Overview, Regional, Feed, Groups, and\nWatchlist sub-views routed via a compound URL hash (#ransomware/{view}).\nAdds row caps with show-more expanders to keep each sub-view to roughly\ntwo scree\n[…]\n the world map's height to a constant instead of\nscaling with viewport.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01X7PnvtqMzEKnEtijh5WfA8",
          "is_bot": false,
          "headline": "v3.18.0: Ransomware Watch layout — 5 sub-views behind a segmented con…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-24T09:00:17Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4b79ba1771496c4f9550c872b6c3f91d8fdf46c4",
          "body": "Two guarded exceptions to v3.16.0's collector-only rule - full detail in\nCHANGELOG.md [3.17.0].\n\nCompany search (GET /api/ransomware/search?q=): always live against PRO's\n/victims/search, never served from local cache as primary (local coverage\nis a small fraction of upstream - a cache would produce\n[…]\n both\npaths, sub-3-char rejection, and a forced 401 (via a temporary invalid-key\ninstance) confirming the degradation labels on both endpoints.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.17.0: Ransomware Watch company search and country lookup",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-24T07:44:50Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4b27670545add0e9f060ef36bcbf2c123cbd8ec4",
          "body": "Discovered during release verification (Part 3's \"confirm the first\ntimer-driven run actually fires, don't assume it\" step): with only\nOnBootSec=2min and OnUnitActiveSec=30min, systemctl list-timers showed no\ncomputed NEXT time at all on the actual VPS, which has been up 15+ days.\nOnBootSec's anchor\n[…]\n trusting the timer's own\nschedule. This commit brings the tracked reference copy in the runbook back\nin sync with the corrected deployed unit.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Ransomware Watch runbook: fix systemd timer, add OnActiveSec",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-24T06:46:56Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "acf471d926f3ac7611feb0bd4a0dab24caea11c9",
          "body": "New empty table, created now rather than in the release that first reads\nit, so that release doesn't need a schema migration against a victims\ntable that will by then hold many more rows than the 1,189 already live in\nproduction. On every collector run, one row is stamped per standing-scope\ncountry \n[…]\nelease.\n\nMigration verified against the actual production database (1,189 existing\nrows) before this was written, with a DB backup taken first.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Ransomware Watch: add country_coverage forward-compat table",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-24T06:37:59Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "f5c3a9c83dc45be5c4db2cb4ae42f3540ae5a03a",
          "body": "Global ransomware victim tracking with a Philippines/Southeast Asia focus\n(PH, SG, MY, ID, TH, VN, HK, TW), built on ransomware.live PRO\n(RANSOMWARE_LIVE_API_KEY) and RansomLook (keyless). Full detail in\nCHANGELOG.md [3.16.0].\n\nArchitecture: the tab never calls either upstream from the browser. A ne\n[…]\n tier/min-length handling, permalink host\nvalidation, and that the API key never appears in a log line or response\nbody under any failure path.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.16.0: Ransomware Watch tab",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-24T06:35:47Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "631335c9754525f1bf580bf532e45f74eaac10f0",
          "body": "…ytics beacon\n\nTwo real-browser console errors, both CSP:\n\n- connect-src 'self' blocked the Breach Check password lookup's deliberate\n  direct browser -> https://api.pwnedpasswords.com fetch (k-anonymity design,\n  v3.14.0: only the 5-char SHA-1 prefix leaves the client, never routed\n  through our ba\n[…]\nowser) was verified but not whether the browser would actually be\nallowed to send the request at all, which is exactly how this shipped broken.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.15.3: CSP fixes for Breach Check password lookup + Cloudflare Anal…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-22T09:33:53Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3e9029270c7261a7de71faa21d1801b7556b397a",
          "body": "…tyle.css\n\nFixes the known issue flagged in the v3.15.1 CHANGELOG: /static/* had no\ncache-busting (stable filenames, 1h Cache-Control), so a release depended on\nremembering a manual Cloudflare purge, and even a clean purge couldn't touch\na visitor's own independent browser cache of the old app.js.\n\n\n[…]\nt) that reproduced in\na normal tab and vanished in Incognito — a stale pre-3.15.0 app.js paired\nagainst fresh HTML, not a browser-specific bug.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.15.2: Self-invalidating static assets — cache-busting for app.js/s…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-22T09:18:17Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "7bcb9d7309397802dcf2aeba71465c253974531c",
          "body": "Two bugs in v3.15.0's drawer, both only surfaced on a real iPhone, not in\nsimulated-viewport testing:\n\n- Drawer trigger appeared to zoom the page instead of opening the sheet.\n  #drawer-search and #palette-input were 14px, and iOS Safari auto-zooms the\n  viewport when a focused input's font-size is \n[…]\nudflare purge after a deploy can serve stale app.js paired with\nfresh index.html — plausibly a contributing factor in how this first\npresented.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.15.1: Fix mobile navigation (iOS Safari) found by real-device testing",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-22T08:54:07Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "35d81413151af0db6ed7a06ace5e59b140f10ebf",
          "body": "…nd palette\n\nReplaces the horizontal tab strip (out of room at 18 tabs — cut off on a 13\"\nlaptop, ~3.5 of 18 visible on mobile) with a responsive sidebar (>=1024px)\nand bottom-sheet drawer (<1024px), viewport-driven via CSS media queries and\nthe (hover:hover)/(pointer:fine) features only — no naviga\n[…]\nc.\n\nNav and layout only — no tab panel, endpoint, or behavior changes. Every\nexisting hash deep-link and cross-tab pivot verified individually.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.15.0: Navigation rework — responsive sidebar, mobile drawer, comma…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-22T08:27:55Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b57dc9ac9d35743fc270103c01031a5ec453d248",
          "body": "New tab: email breach + paste lookup (enriched with full breach metadata),\nclient-side password check via Pwned Passwords K-anonymity (never proxied\nthrough our server), domain breach history, recent-breaches awareness, and a\nsearchable browse-all-breaches reference. Sensitive breaches redacted by\nd\n[…]\n CC BY 4.0 attribution on all five\nrequired surfaces (subtitle, per-result footer, breach logos, CSV export,\nprivacy note). Tab count 17 -> 18.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.14.0: Breach Check — Have I Been Pwned integration",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-22T06:38:19Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "7b5dffa3568307050dc6e8da8df4243bdff25ed1",
          "body": "…Proto)\n\nReplaces the old Telegram Channel Inspector, which only handled\nt.me/s/{channel} and 404'd on any user, bot, or channel/group without\npreview enabled. New app/telegram/ package resolves any username, @handle,\nchannel, bot, or t.me link across three independently-degrading tiers:\n\n- Tier 1 (\n[…]\n and\nsurfaced as a transient tier-3 error rather than a crash; monitoring via\nthe daily operator report before deciding whether it needs a fix.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.13.0: Telegram Intelligence — tiered rework (scrape / Bot API / MT…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-21T01:52:51Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b75336a0ad86d3246d57ff66fa383171f81e8815",
          "body": "Full scan (~965 sites, ~50s) was surfacing a raw parse error when nginx's\n30s proxy_read_timeout cut it off mid-request and returned an HTML 504\ninstead of the app's JSON response. The timeout fix (30s/60s -> 90s) was\nalready applied live and is noted in this release's CHANGELOG for record.\n\nThis co\n[…]\nd error.\n- Frontend guard: a JSON-parse failure (SyntaxError) on the scan response\n  now shows a clean message instead of the raw parser error.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.12.2: JSON-error hardening for Username Full scan (three layers)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-21T00:35:01Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "886d354e51e918e30a021f6c6d7377b001025e33",
          "body": "nginx proxy_read_timeout was 30s while a Full username scan takes ~50s,\nso nginx returned its own 504 HTML page mid-scan instead of the app's\nJSON response. Raised proxy_read_timeout/proxy_send_timeout and the\ngunicorn worker --timeout to 90s, comfortably under the scan's ~50-53s\nhard wall-clock deadline (app.username.routes._DEADLINE) and Cloudflare's\n~100s edge ceiling.",
          "is_bot": false,
          "headline": "Raise proxy/worker timeouts for Full username scans (30s/60s -> 90s/90s)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T22:47:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fccafb0a244028564796b627dcd9138798c80ccb",
          "body": "The app location inherited the default access_log, which records the Cloudflare\nedge IP (`$remote_addr`) rather than the real visitor. Point it at a dedicated\nlog using the goaccess_cf format (`$http_cf_connecting_ip`), so access logs and\nGoAccess see real client IPs. The Cloudflare-IP allowlist keeps using\n`$remote_addr`, so it is unaffected.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "nginx: log real visitor IP (CF-Connecting-IP) on the app location",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T10:10:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "126cbdb32e28c498cb1b0f9b95c07635e478d807",
          "body": "…-2 analyze nesting)\n\nSecurity release for the two exploitable MEDIUM denial-of-service findings from the\n2026-07-20 assessment (M-1, M-2), on top of the v3.12.0 framework upgrade. Scoped to\nthese two only; no dependency change.\n\nSecurity:\n- M-1: reinstate rate limiting on /api/abuse/send. The endpo\n[…]\nrate_limited), wrong-password backoff engages, nested-multipart returns 400 (worker\nsurvives), analyze rate limit fires. Full suite 296 passed.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.12.1: fix two unauthenticated DoS findings (M-1 send rate limit, M…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T09:21:08Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "85ffbd194862c158ad9d9e13dc6c164bca22629c",
          "body": "…e DoS advisories)\n\nCloses H-2 part 2, the last open HIGH from the 2026-07-20 assessment. Framework\nbump only, scoped so a regression is traceable to the framework change and nothing\nelse.\n\nSecurity:\n- Upgrade FastAPI 0.111.0 -> 0.139.2, Starlette 0.37.2 -> 1.3.1 (pinned; the exact\n  advisory-cleari\n[…]\nndled with controlled statuses, StaticFiles serves, and\nthe abuse-send auth structured-200 contract is intact. No router or logic code\nchanged.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.12.0: FastAPI/Starlette upgrade — close H-2 part 2 (clear Starlett…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T09:13:03Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "41db0d1ce60ee9a234afdaa90078643e707fb3b5",
          "body": "…bump\n\nSecurity release for the two HIGH findings from the 2026-07-20 assessment.\n\nSecurity:\n- H-1: fix the DNS-rebinding TOCTOU in the SSRF guard by pinning every\n  connection to the validated IP. safe_fetch now resolves the host once\n  (resolve_and_check), validates every returned address, and con\n[…]\napi-upgrade-plan.md for a following release. Interim mitigations\n  (nginx body limits, request timeouts, Cloudflare upload limits) noted there.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.11.0: SSRF DNS-rebinding fix (IP-pinned fetch) + python-multipart …",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T07:47:58Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "39a90d64a54785e7ef4f6c4f1069423fdb19709e",
          "body": "Added:\n- Light/dark theme. Tailwind's color scales are remapped to\n  rgb(var(--x) / <alpha-value>) CSS variables so a single data-theme on\n  <html> retints the whole UI (opacity modifiers still work). Dark values\n  equal Tailwind's stock palette, so the default look is unchanged; light\n  is a delibe\n[…]\nie disclosed. Last updated 20 Jul 2026\n  with a 14-day \"updated\" badge.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01PBdYY7DDmDT9PtQbJjdvQo",
          "is_bot": false,
          "headline": "v3.10.0: light/dark theme, privacy policy refresh, per-tab privacy notes",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T06:13:10Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3d35e84b367013b1a9e162080857f924d9ce882f",
          "body": "Fixed:\n- IP card header no longer leads with the least-supported country when\n  sources disagree. Disputed geo now reads \"Location disputed (IR/LT/US/RS)\"\n  and drops the single-city lead; undisputed geo is unchanged.\n\nAdded:\n- Client-side PDF export for the IP reputation report (\"Download Report\")\n\n[…]\n falconeye-abuse-report-{target}-{date}.pdf (dots sanitized to dashes).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01PBdYY7DDmDT9PtQbJjdvQo",
          "is_bot": false,
          "headline": "v3.9.1: client-side PDF export + disputed-geo header fix",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T05:15:09Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "59a638ebff7b788b29205b77bb4029848c85717e",
          "body": "Cross-reference five threat-intel sources on the IP Reputation tab —\nAbuseIPDB, VirusTotal, AlienVault OTX, Censys, ThreatFox — each in its\nown sub-card with an explicit ok/no-key/quota/error/not-found state so\none failing source never blanks the card or 500s the endpoint. All five\nfetched concurren\n[…]\n guard\n- ThreatFox reuses ABUSECH_AUTH_KEY; no new runtime dependencies\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01PBdYY7DDmDT9PtQbJjdvQo",
          "is_bot": false,
          "headline": "v3.9.0: Multi-source IP reputation with consensus verdict",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T04:24:03Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4514b5606a4202250f089f96571687f853a0cd12",
          "body": "…d (incl. v3.8.2)\n\nThis lands v3.8.2 (never separately deployed) and v3.8.3 together; the live jump\nis 3.8.1 -> 3.8.3. CHANGELOG documents both.\n\nv3.8.3 — abuse reports:\n- Reports now carry the real email content: Subject, Message-ID, Date,\n  Return-Path, Authentication-Results, the verbatim Receive\n[…]\n.\n- Operator CLI: python -m app.abuse.tools reset-rate-limit --ip <ip> [...].\n\nVersion bumps: README, CHANGELOG, JSON-LD, main.py app + health.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.8.3: real email evidence in abuse reports; send rate limits remove…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-20T01:33:26Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bb4bf7a474db66fe6fcc498ac3054e4f36412696",
          "body": "The /api/abuse/send endpoint accepted HTTP Basic Auth AND the frontend\nrendered an in-page admin form for the same credential. The endpoint's\n401 + WWW-Authenticate response could trip the browser's native Basic\nAuth dialog, which raced the in-page form and rejected correct passwords.\n\nRemoved Basic\n[…]\n503 endpoint tests were updated to\nthe new 200-with-structured-body behavior.\n\nVersion bumps: README, CHANGELOG, JSON-LD, main.py app + health.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.8.1: fix duplicate auth surface on abuse send",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-19T23:06:19Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b1fcec56534586a8cdb382b5b09fa867258b1495",
          "body": "Dual-engine username lookup across ~965 platforms using vendored data from\nWhatsMyName (698 sites) and Sherlock (478 sites), merged and deduplicated by\nhost. Cross-validated hits (found by both engines) carry a high-confidence\nbadge, single-source hits carry medium. Quick Scan (priority 2-3, ~280 si\n[…]\nsername_data.py; no runtime dependency on either\nupstream project (both MIT).\n\nVersion bumps: README, CHANGELOG, JSON-LD, main.py app + health.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.8.0: Username Enumeration tab",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-19T12:59:13Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "38e94a1ae87cd4727102ab2be3fa754fb04732ad",
          "body": "Adds RDAP-based abuse contact lookup and category-templated report\ncomposition to the IP Reputation and Email Header tabs. Compose and copy\nwork without configuration; the card leads with a banner directing users\nto copy the report and send it from their own email so the provider can\nreply directly.\n[…]\nNew-feature UI\ncards are wrapped so a failure can never regress the host tab.\n\nVersion bumps: README, CHANGELOG, JSON-LD, main.py app + health.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.7.1: abuse report composition on IP and Email Header tabs",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-19T11:13:59Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b759c637deaf110b065fd8f01fec36c6094fe2e9",
          "body": "Two new tabs targeting the same investigative flow: figure out where a\nsuspicious link actually goes before touching it.\n\nURL Expander (/api/url/expand) walks redirect chains hop-by-hop, re-running\nthe existing safe_fetch SSRF guard (resolve_and_check) at every hop and before\neach per-hop TLS grab -\n[…]\nn.\n\nNew deps: pyzbar + qrcode[pil]; libzbar0 system package required. Version\nbumps: README, CHANGELOG, JSON-LD, FastAPI app metadata, /health.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.6.0: URL Expander and QR Code Analyzer tabs",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-17T09:30:49Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "274eee26c00d6fabfbbf840a14ba9b1da9c29a0b",
          "body": "…+ health)\n\nThe v3.5.2 tag and release shipped with stale version constants in the\nFastAPI app metadata, the /health endpoint, the README 'Current version'\ndisplay line, and the JSON-LD softwareVersion in app/static/index.html.\nNo behavior changes. README L53 (historical 'as of v3.5.0' phrasing)\nintentionally left as-is.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: bump version references to 3.5.2 (README, JSON-LD, FastAPI app …",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-17T08:45:16Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "effc0265d894b6753360ffd35aafa291902e6e93",
          "body": "The nginx CSP added in v3.5.0 blocked the D3 CDN (cdnjs.cloudflare.com),\ncausing the Crypto Investigation Workbench transaction graph to fail with\n\"d3 is not defined\" after any wallet lookup. Added https://cdnjs.cloudflare.com\nto script-src in nginx/falconeye.conf and mirrored it on the live VPS config.\nstyle-src was intentionally not widened. Updated the README CSP paragraph and\nprepended a v3.5.2 CHANGELOG entry.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "v3.5.2: allow cdnjs.cloudflare.com in CSP script-src for D3",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-17T08:29:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c4240a26a54f1d8480668a3c0ec6f615190addb1",
          "body": "app/main.py: version 3.5.0 → 3.5.1 (two places: FastAPI constructor and /health).\nCHANGELOG.md: [3.5.1] section added covering the four phishing scanner\n  detection features shipped today (PH banking indicators, Cloudflare challenge\n  detection, urlscan.io enrichment, domain age check with RDAP/whois lookup).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: bump version to 3.5.1 for scanner detection improvements",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T08:46:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6ed63f7c2a5eed6bb3a7c516d88cffc18851245e",
          "body": "app/utils/domain_age.py — new module:\n  - check_domain_age(domain, db?) queries RDAP first (rdap.org), falls back\n    to whois subprocess. Returns {found, created_at, age_days, source, error}.\n  - RDAP parses events[] for \"registration\" or \"registrar registration\" action.\n  - whois collects ALL cand\n[…]\ns old).\n\nTests: 35 new (20 test_domain_age + 15 test_domain_age_indicators);\n  full suite 212 passed → 212 passed, 3 skipped. No regressions.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add domain age check with newly-registered phish indicators",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T08:34:12Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "006d2b72f784bba83adca2267939d84e021a8f43",
          "body": "Adds app/utils/urlscan.py:\n  - check_urlscan(url) queries urlscan.io search API for the most recent scan\n    of the target domain. Returns {found, verdict, malicious, submitted_at,\n    screenshot_url, live_url, tags}.\n  - Uses safe_fetch so it inherits SSRF hardening (no SSRF via lookup of\n    attac\n[…]\ngle.com/safe-browsing/v4/lookup-api\n  Same pattern: separate enrichment pass returned as gsb_result, does not\n  overwrite indicators_matched.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: enrich scanner results with urlscan.io lookup",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T07:54:24Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c461bd15c6525773e2cd8027d3c7d5f7b34a3817",
          "body": "Adds app/scanner/cloudflare_detect.py. When fetched HTML matches any of:\n  - \"Attention Required! | Cloudflare\" in title\n  - \"Just a moment\" / \"Checking your browser\" in title\n  - \"Sorry, you have been blocked\" in body\n  - \"cf-browser-verification\" / \"cf-error-details\" in body\n  - \"Enable JavaScript\n[…]\ny do.\n\nscanner.py chains this after PH indicator matching. 10 unit tests; all pass.\nPython 3.9 compat: Optional[dict] instead of dict | None.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: detect Cloudflare challenge pages as an indicator signal",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T07:41:00Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "dda9071fc5f03a1d9cb1047d6f5ec8c96eb054c3",
          "body": "Adds app/scanner/ph_bank_indicators.py with 35 indicators across four groups:\n  - URL path patterns: /cancel/, /verify/, /update/, /confirm/, /suspended/,\n    /reactivate/, /otp/ — common in PH banking phish flows\n  - Domain impersonation: gobpi*, bpiverify*, bpi-online*, bdo-online*,\n    gcash-veri\n[…]\n an empty HTML body — URL alone is enough to detect\nthe kit family.\n\nTODO in module docstring: Google Safe Browsing enrichment (GSB_API_KEY).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: expand phishing scanner with PH banking impersonation indicators",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T07:02:09Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fe04d0418eb1181864e459f95dd85d9723bbd26c",
          "body": null,
          "is_bot": false,
          "headline": "fix: allowlist cdn.tailwindcss.com in script-src CSP (M-5 followup)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T02:59:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "81c5437cfd8c958a7698c9fddff7a476217683a0",
          "body": "app/main.py: FastAPI version= and /health response updated to \"3.5.0\".\nfalconeye.service: Description updated to \"FalconEye v3.5.0\".\n\nJustification: minor version bump (3.4 → 3.5) rather than patch because\nthis release closes two High and nine Medium/Low security findings from the\nFable 5 review, in\n[…]\n_fetch, llm_response),\nand makes a breaking fix to the .env key name (DB_PATH → FALCONEYE_DB).\nNo existing public API contracts were changed.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: bump version to 3.5.0 for security remediation batch",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T02:44:54Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "25eb1f8b4d7ef354a3bdb43bf72fbe4eea0cd3c3",
          "body": "Changes:\n  - chmod 600 on falconeye.db and chmod 700 on data/ after DB init (security)\n  - git pull --ff-only on existing repo instead of failing on re-run\n  - redis-server added to apt-get install (required by Prospect and Image tabs)\n  - anthropic + extract-msg installed from pip after requirement\n[…]\non \"Next steps\" block replaces the previous silent completion\n  - Version string in echo updated to 3.5.0\n\nbash -n confirms no syntax errors.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "deploy: verify and update provision.sh for current state",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T02:44:37Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "caa28e067080afa76b408f6cc039cdc21fb6dbb2",
          "body": "Key changes:\n  - DB_PATH renamed to FALCONEYE_DB (the actual env var read by config.py\n    and prospect/investigations.py; the old DB_PATH alias was never read by code —\n    existing deployments using DB_PATH had a silent misconfiguration since v3.0).\n    Upgrade note added inline.\n  - FALCONEYE_DAT\n[…]\ns.\n  - Comments clarify which features degrade vs crash when a key is absent.\n  - anthropic SDK install note added next to ANTHROPIC_API_KEY.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: sync .env.example with all variables read by current code",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T02:43:50Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d6fb2665757723382b6c9c6c2d27138bf82ebdcf",
          "body": "Creates CHANGELOG.md. Sections:\n  [3.5.0] security remediation — closes H-1, H-2, M-1–M-5, L-1–L-5 from the\n  Fable 5 security review; documents every added file, changed file, and\n  removed pattern.\n  [3.4.0] Image Search tab baseline.\n  [3.3.x / 3.2.0 / 3.1.0 / 3.0.0] brief historical summaries.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add CHANGELOG with security remediation batch",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T02:43:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6fa2a95c5eeba8b6ba918db0760f0c8ca4c20ccd",
          "body": "…e, and version\n\nUpdates from v3.4.0:\n- Version bump to 3.5.0\n- Security posture section documenting safe_fetch, CF-Connecting-IP keying,\n  LLM output validation, XSS escaping, security headers, error isolation\n- Install instructions corrected: paths are scripts/provision.sh, falconeye.service\n  (ro\n[…]\n and correct scripts/ / nginx/ paths\n- Python version corrected to 3.10+ (3.11 recommended)\n- Test baseline documented: 122 passed, 3 skipped\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: rewrite README to reflect current architecture, security postur…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T02:42:16Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4079555de08a715353d23323688fb14ae5fe613e",
          "body": "…l body (L-5)\n\nAdds REGEX_MAX_BODY_BYTES = 100_000 to config.py. In _analyze_body, text_only\nis truncated to 100KB after HTML stripping and whitespace normalization, before\nSCAM_PATTERNS runs. This reduces the worst-case surface from 500KB to 100KB\n(5x reduction) for ~50 regex patterns per request.\n\n[…]\nalyze_body now returns body_truncated:bool; the API response exposes this\nas body_regex_truncated so analysts know when the scan was partial.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: cap regex pass at 100KB to prevent compute amplification on emai…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T00:48:38Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "81af5021f7738282f9ae1cd229d27c9c46ef1cef",
          "body": "…s (L-4)\n\ncrypto.py: three Exception handlers now call log.exception (full traceback\nserver-side) and return \"Upstream service unavailable.\" to the client\ninstead of f\"Upstream fetch failed: {str(e)}\" / f\"TronGrid fetch failed: {str(e)}\".\n\ntelegram_inspector.py: Exception handler keeps the log.warni\n[…]\nail_file ValueError at email_header.py:1173 is left unchanged —\nit surfaces our own internal format-validation messages, not upstream str(e).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: replace upstream error string echoes with generic client message…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T00:47:11Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8f122883f9cfbcc2d0274308ac131e34ce02c2dd",
          "body": "grep -rn \"verify=False\" app/ returns zero matches. safe_fetch.py (the\nonly path for user-supplied URLs) sets verify=True explicitly on the\nhttpx.AsyncClient. All other outbound clients omit verify= entirely,\ninheriting httpx's default of True.\n\nNo code changes required; this commit documents the verified clean state.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: confirm TLS verification enforced across all outbound HTTP (L-2)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T00:44:33Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9e3db4cc6744303c5656e624337f094e2a04bfeb",
          "body": "…Pv6 ranges (L-1)\n\nAll six L-1 ranges are confirmed covered by is_private_ip in safe_fetch.py:\n  0.0.0.0/8    — stdlib is_private (also explicit _THIS_NETWORK)\n  100.64.0.0/10 — explicit _CGNAT (not in stdlib is_private on Python 3.9)\n  ::ffff:a.b.c.d — ipv4_mapped unwrap before stdlib checks\n  fe80\n[…]\nnspecified\n\nAdds an inline comment to safe_fetch.py documenting each range and why\nthe explicit blocks are needed alongside the stdlib flags.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: verify SSRF blocklist covers CGNAT, NAT64, unspecified, mapped-I…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T00:44:15Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "adbea69be4e908512a3f458dc67ccb2b2efb8fd9",
          "body": "Adds Content-Security-Policy, X-Content-Type-Options, X-Frame-Options,\nReferrer-Policy, and Strict-Transport-Security to the HTTPS server block.\n\nscript-src retains 'unsafe-inline' because the frontend uses inline onclick\nhandlers; a comment documents the refactor needed to remove it. frame-ancestors\nreplaces X-Frame-Options for modern browsers (both are set for compatibility).\nHSTS is set to 1-year with includeSubDomains.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: add CSP and security response headers at nginx (M-5)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T00:42:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ab46a8acd1949681cb4816d31290385ec30b88d1",
          "body": "…odel responses (M-4)\n\nAdds app/utils/llm_response.py with parse_llm_json, clamp_int, safe_str,\nand validate_findings_list helpers. Replaces raw dict.get() calls in\nemail_header (scam_score, findings, verdict fields), script_decoder\n(severity, intent enum clamping), and dork_generator (dorks list\nst\n[…]\n_source_note\nfields to responses indicating these are model opinions not verified\nverdicts. 23 unit tests in tests/unit/test_llm_response.py.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: validate and clamp LLM JSON output to prevent 500 on malformed m…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-05T00:41:48Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "17bbc6f630b6905579163451b636462e01678295",
          "body": "Removes the validate_url + raw httpx.AsyncClient(follow_redirects=True,\nverify=False) block from the phishing scanner.  Replaces it with a call\nto safe_fetch which re-validates every redirect hop against the SSRF\nblocklist and enforces TLS verification.  SafeFetchError is mapped to\nHTTP 400 with a g\n[…]\neric message (no internal detail leaked to the client\nper L-4 guidance).  Generic exceptions also suppress str(e) from the\nfetch_error field.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: migrate scanner fetch to safe_fetch (H-1)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-04T23:55:36Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bf4c1237d8c7cb9d31589fd363b315a09361f64b",
          "body": "…ocklist (H-1)\n\nIntroduces app/utils/safe_fetch.py — an SSRF-safe HTTP fetcher for\nuser-supplied URLs.  Defends against the two bypass classes identified\nin H-1: redirect-chain bypass (validates every hop independently with\nfollow_redirects=False) and DNS rebinding (re-resolves and re-validates\nat t\n[…]\nt tests in tests/unit/test_safe_fetch.py covering all blocked ranges,\nscheme rejection, redirect-to-private-IP, and redirect-cap enforcement.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: add safe_fetch with per-hop revalidation and mapped-IPv6-safe bl…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-04T23:55:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3bd21a6fbc19abd8e18ce3e3d395e7c6d135fbf5",
          "body": "…ders (H-2, M-2, M-3)\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "sec: escape attacker-controlled fields in Telegram, RDAP, and RSS ren…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-04T10:35:50Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9aac1cb39ff99e46e68ef1f28138a6ba8f15f5b1",
          "body": "… as 500\n\n_record_llm_call, _record_call (dork), _record_call (script) had no exception\nhandling. A SQLite error (locked by another worker, wrong path, missing table)\nwould propagate as a 500, silently skip the row write, and leave no log evidence.\n\nWrap each in try/except and log.error so failures \n[…]\neturn). Test with\na previously-unseen email body to bypass the cache.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VCfT6QydBhpn2DPvXM1Yn9",
          "is_bot": false,
          "headline": "fix: log errors in rate-limit INSERT functions instead of propagating…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-04T10:21:15Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c6f7a8f29aa4777bf4e2600b1391d22d9c66420d",
          "body": "…real client (L-3)\n\nNon-upload branch of /api/image/search now requires https:// and passes image_url\nthrough validate_url() (the same SSRF guard the scanner uses) before forwarding\nto SearchAPI. Also switches the endpoint's Limiter to get_client_ip_key (real\nclient IP via CF-Connecting-IP) so the 20/min rate limit tracks actual users.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VCfT6QydBhpn2DPvXM1Yn9",
          "is_bot": false,
          "headline": "sec: validate image_search URL against SSRF and rate-limit branch on …",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-04T10:07:34Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ede5f275d7c795b3366acdce5c59b3096351e090",
          "body": "…eal users (M-1)\n\nAdd app/utils/client_ip.py with get_client_ip() / get_client_ip_key() that read\nCF-Connecting-IP first (trustworthy because nginx restricts origin traffic to\nCloudflare IP ranges) and fall back to request.client.host in local dev.\n\nReplace Limiter(key_func=get_remote_address) with \n[…]\nsing tests: CF header present, fallback\nto client.host, XFF ignored).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VCfT6QydBhpn2DPvXM1Yn9",
          "is_bot": false,
          "headline": "sec: key rate limits on CF-Connecting-IP so LLM cost controls track r…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-04T10:07:28Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8a8f5a9d1482becf130e7e25a548f3ed27e71e8d",
          "body": "Add Image tab to module table (14 tabs), update count in description.\nDocument IMAGE_SEARCH_ENABLED, IMAGE_SEARCH_YANDEX_ENABLED, IMAGE_UPLOAD_SECRET,\nSITE_ORIGIN env vars in .env.example.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "docs: update README and .env.example for v3.4.0 Image tab",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-03T06:01:39Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "649ff57abae95e0f134ee69b1513547e721deac1",
          "body": "New module app/image_search/ provides:\n- Google Lens and Yandex Reverse Image Search in parallel via asyncio.gather\n- File upload with HMAC-SHA256 signed URLs (5-min token, 15-min file cleanup)\n- EXIF extraction via Pillow (camera, datetime, GPS with OSM map link)\n- Cross-source domain corroboration\n[…]\nion, partial failure handling, and route-level kill switch and cache.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "feat: v3.4.0 Image tab with reverse image search (Google Lens + Yandex)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-03T05:59:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b947e1f20eab23f538f1eb26cc4258b881a92432",
          "body": "\"Stripe, Inc.\" as the Meta search query returns pages like \"Stripes & Checks Inc\"\ninstead of Stripe's actual page. _meta_search_name() strips common legal suffixes\n(\", Inc.\", \" plc\", \" Ltd.\", \" GmbH\", etc.) to produce a clean search name.\n\"Stripe, Inc.\" -> \"Stripe\", \"BP p.l.c.\" -> \"BP\" (already short), \"HP\" -> \"HP\".\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "fix: strip legal suffixes from Meta page search query",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-03T04:41:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d444072f3d3074008365d106169b2bc326c13074",
          "body": "…isplay only\n\npartial_ratio(\"Stripe-A-Zone, Inc.\", \"Stripe, Inc.\") = 74. Short aliases like\n\"Stripe\" score 100 via partial_ratio against \"Stripe-A-Zone, Inc.\" because the\nalias appears as a substring. Fix: score only against canonical_name and\ndisplay_name (not aliases), raise threshold from 70 to 7\n[…]\nn used\nfor the whole-word boundary filter where they are appropriate.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "fix: raise meta page fuzzy threshold to 75, score against canonical+d…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-03T04:38:48Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ca09de871a4c35c48ce45e9c3e6cd69d67aefc0b",
          "body": "…op ads table, hp.com identity fix\n\nFix 1 (Meta page matching): _select_meta_page() filters pages by whole-word\nmatch of any identity name, rapidfuzz partial_ratio >= 70, category boost +20\nfor semantic match (e.g. Oil industry -> Energy Company), then ranks by\n(score, followers). BPI is now correct\n[…]\n_partial_ratio stub so module attribute exists when rapidfuzz absent.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "feat: v3.3.2 - meta page identity filtering, multi-advertiser tabs, t…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-03T04:07:24Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ebb82d7022b7aaa90bef66cd6a28db30d10309b6",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "fix: bump health endpoint version to 3.3.1",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-03T02:53:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3987f7d6e338626beff840adcad388a5cb43bf3c",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "chore: bump version to 3.3.1",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-03T02:52:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "95a865206cb15386e17f3bfbbaae1a6fee1f6842",
          "body": "…t validation\n\nReplaces derive_company_name() with a structured resolver that returns\ndisplay_name, canonical_name, aliases, confidence, and source.\n\nResolution paths (in priority order):\n  1. kg.description: extract name before \" is \" (stripe.com, orf.at, bp.com)\n  2. ai_overview paragraphs: parent\n[…]\ndds 4 new about_domain fixtures: stripe.com, orf.at, bp.com, ibm.com.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "feat: v3.3.1 - company identity resolver, query disambiguation, resul…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-03T02:52:01Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c6c773164196dee6b2eeecd0b1abc24b198b3c13",
          "body": "…timeline cards\n\nRenames Prospect tab label to Company (UI only; internal names unchanged).\nExpands from 2 to 7 engine calls across 2 async waves:\n- Wave 1: about_domain, ads_transparency, meta_page_search, google_news, google_jobs\n- Wave 2: ads_transparency_historical (chains from advertiser_id), m\n[…]\n side by side.\nBumps version to 3.3.0 in health endpoint and JSON-LD.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XHGiXa1AiEHfaxaGAEU3E9",
          "is_bot": false,
          "headline": "feat: Company tab v3.3.0 - 6 engines, investigation log, hiring/news/…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-02T11:47:58Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ed0b9c2f4844341107689b90464db6860e7f5ce8",
          "body": "…relying on setdefault\n\nOn VPS the real key is already set in the process environment; setdefault()\nhas no effect. Use patch.dict(os.environ, ...) inside the test instead so\nthe assertion uses the expected test value regardless of environment.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: fix auth header test to patch SEARCHAPI_KEY in env rather than …",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-02T11:20:42Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "69add4b7fb4b1d8d6290df0a996e502fc64968eb",
          "body": "…hAPI.io\n\nAdds a new Prospect tab backed by two SearchAPI.io engines:\ngoogle_about_this_domain (identity/knowledge graph) and\ngoogle_ads_transparency_center (ad count, creative thumbnails, date range).\n\n- app/prospect/: SearchAPIClient (httpx, 429 backoff, 5xx retry x3),\n  engines.py, service.py (as\n[…]\nnts.txt\n- SEARCHAPI_KEY and PROSPECT_ENABLED documented in .env.example\n- README, privacy policy table, and module counts updated for 13 tabs\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: Prospect tab v3.2.0 — commercial intelligence dossier via Searc…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-07-02T11:18:05Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2db1bf1c06ba7a418f3ae8a975e4dc13600397da",
          "body": "FastAPI exposes /openapi.json, /docs, and /redoc by default. These\ndisclose the complete API surface to unauthenticated callers. Now\ngated behind FALCONEYE_PUBLIC_DOCS=true, defaults off in prod.\n\nSurfaced by an external scan with Suri (github.com/osintph/suri).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "harden: disable public OpenAPI spec, Swagger UI, ReDoc in production",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-29T11:05:15Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "397c8f54aab428d60c57fc373b9ad6782af9d45a",
          "body": "…y policy, contact, SEO)",
          "is_bot": false,
          "headline": "docs: refresh README and contributing docs for v3.1 (LLM tabs, privac…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-26T02:02:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c33e0e2340d95e2818a7b29f2de96e933485b44a",
          "body": "Full privacy policy covering: data collected (IP for rate limiting, nginx\naccess logs), what is NOT collected (accounts, cookies, analytics, file\npersistence), 24h content-hash cache lifecycle, per-tab third-party data\nflows (Blockstream, rdap/crt.sh, Telegram, Shodan, abuse.ch, Anthropic),\nCloudfla\n[…]\nC key, backdrop click, X button, and Close button all dismiss.\nBackground scroll locks while modal is open. footer link visible on every tab.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add privacy policy modal accessible from footer on all tabs",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-26T01:52:10Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "68c79065f80887c6e9ed2239eb037b87727df3bf",
          "body": "…sitemap.xml\n\nScript Decoder (tab 11): LLM-powered deobfuscation via claude-haiku-4-5\n(hardcoded). Accepts PowerShell, JavaScript, VBA, Base64, hex, and mixed\nencodings. Returns encoding layers, deobfuscated code (with Copy button),\nintermediate stages, IOCs with pivots to Domain/IP/Scanner/Sandbox \n[…]\n3 URLs. favicon references kept matching\nexisting filenames. VALID_TABS updated to include decoder and contact.\npivotToDomain() helper added.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: Script Decoder tab, Contact tab, SEO meta/JSON-LD, robots.txt, …",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-26T01:22:11Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6b5d62cdfb093054b8e48a39f22c10270957a13c",
          "body": "Adds a drag-and-drop upload zone that accepts .eml, .msg, and .txt files\n(max 5MB). Files are parsed in-memory via _parse_email_file() and discarded\nimmediately; .msg uses tempfile.NamedTemporaryFile(delete=True) with explicit\nmsg.close() in a finally block. The /api/email-header/upload endpoint ret\n[…]\nelp section\nauto-collapses 1.5s after clicking Analyze.\n\nPrivacy notice card explains in-memory-only processing and 24h content-hash\ncaching.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(email): .eml/.msg file upload + 10-client header retrieval guide",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-26T00:57:23Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d00897b8d5bbbcf3e23c89634ed6a0efc2ba29b1",
          "body": "New 10th tab between Email Header and News. Natural-language goal + optional\ntarget -> 5-10 LLM-generated Google dorks with risk badges, explanations,\ndefensive-use notes, Copy button, and direct Google search links. 10 quick\npresets (exposed files, subdomains, admin panels, credential leaks, cloud\n\n[…]\ncovery). Hardcoded claude-haiku-4-5, separate SQLite rate limit table,\nSHA256 goal+target cache, kill switch via LLM_DORKGEN_ENABLED env var.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: Google Dork Generator tab (Claude Haiku 4.5, 10/IP/day rate limit)",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-26T00:51:28Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3ca82115a7a585035dfb0457a71451abd946c529",
          "body": "…M summary as headline card\n\nAdds a left-border summary card above the risk assessment block that shows\nthe LLM-written scam type and plain-English summary when Claude Haiku ran.\nRemoves the duplicate LLM Classification mini-card that was embedded inside\nthe score card. The summary card is hidden when no LLM analysis was performed.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ui(email): rename BEC Assessment to Email Risk Assessment, surface LL…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-26T00:34:50Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6993e4d83135d1c238152d4956890a0c569d7b79",
          "body": "Adds a second-pass LLM analyzer running Claude Haiku 4.5 (hardcoded, not\nconfigurable) on the email body. Guards: feature flag (LLM_ANALYSIS_ENABLED),\nAPI key check, 10-call/IP/day SQLite rate limit, 50-char body minimum, 8K\ntoken cap. Any failure silently falls back to regex-only. LLM scam_score\nta\n[…]\nre to rescue misses without lowering\nexisting hits. Results surface in a new BEC card classification block with\ntoken usage telemetry footer.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: Claude Haiku 4.5 LLM scam analysis for email body",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-26T00:29:09Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ea11239e30efe03b54b56ae5064ab5c949c9e1cf",
          "body": "… crypto pivot\n\nAdds optional body textarea to the Email Header Analyzer. Backend runs\n12 detection categories (urgency, threat, authority impersonation, financial\nlure, credential phishing, crypto scam, romance/pig butchering, invoice wire\nfraud, URL deception, crypto addresses, suspicious attachme\n[…]\nFindings merge\ninto BEC score. Frontend: collapsible body section, body analysis card with\ncategory badges and pivots to Crypto/Scanner tabs.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: email body scam analysis — 8 pattern categories, URL deception,…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T23:55:52Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "43f60bdb0af7670fbddfa7b5b8c10abf58463d40",
          "body": "…EC scoring, and pivots\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add Email Header Analyzer tab with SPF/DKIM/DMARC validation, B…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T23:34:18Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "03f45e5043b477023c136d287dbbc3ed497fbbec",
          "body": "…hots scaffolding\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: AGPL-3.0 LICENSE, rewritten README, CONTRIBUTING guide, screens…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T11:57:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "25e5ab4df051199b42fd9fcb9962962dde1c77c1",
          "body": "…, tabs are shareable URLs\n\n- showTab() centralises all tab-switch logic: hides all .tab-content,\n  un-hides the target, updates nav active state, scrolls to top, and\n  triggers loadNews() when needed\n- Tab button clicks set window.location.hash; the hashchange event handler\n  calls showTab() so bac\n[…]\n hashes\n- Home tab nav button and tab-home div now start in the default inactive/hidden\n  state; showTab() applies correct state on first run\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: hash-based tab routing — back/forward buttons work between tabs…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T10:33:04Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "9f5a6eff439cea04a92fd631308e36d3266b1f71",
          "body": "…mple-card click handler\n\n- Tagline text-xs → text-sm (12px → 14px)\n- Footer data source <span> pills replaced with <a> links opening provider\n  homepages in a new tab\n- style.css .data-source gains text-decoration: none and color: inherit so\n  <a> elements render identically to the previous <span> \n[…]\ncard querySelectorAll().forEach() replaced with a document.body\n  delegated click listener so attachment timing can never cause missed clicks\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: larger tagline, link out to data source providers, delegated exa…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T10:07:08Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "180f926ee1c690254c7d8863c7cfaba6ea1ffc28",
          "body": "…ging on landing widgets\n\n- New Home tab as default landing: About FalconEye story block, PH Threat\n  Pulse widget, example investigations grid, latest news strip\n- Crypto tab cleaned to focused workbench only (hero cards and landing\n  extras removed)\n- Removed setupLandingCollapse MutationObserver (no longer needed)\n- loadThreatPulse and loadLandingNews now log HTTP errors to console before\n  rendering user-facing error state\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: add dedicated Home tab, clean Crypto tab, improve error log…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T09:58:13Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7ab82bfae94f644aacb4e2c3b9f24a7d44741615",
          "body": "…us/Threat/Host/IP/ASN/Country)\n\nThe country feed uses a different column schema than the URL/payload APIs.\nStrip extra quotes from each field value since the feed wraps values in double-quotes.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: correct URLhaus PH feed CSV column names (Dateadded/URL/URL_stat…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T09:43:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3df351d071e4fe98690b6cb5b839ebf531849293",
          "body": "…id, latest news strip\n\n- app/routers/threat_pulse.py: new /api/threat-pulse endpoint; fetches URLhaus PH country CSV feed (zip-aware), aggregates 24h/7d/live counts, brand impersonation breakdown (GCash, Maya, BPI, BDO, etc.), latest 5 URLs; 60-min SQLite cache with stale-fallback on upstream failu\n[…]\nclick handlers, loadLandingNews(); both fire on page load\n- app/static/style.css: example-card, line-clamp-3, threat-pulse tabular-nums rules\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: landing page enhancements — PH Threat Pulse widget, examples gr…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T09:42:24Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "07d16e8304b13d311b81e95bba139e987e5b2429",
          "body": "…ample buttons, data sources footer\n\n- app/static/favicon.svg: geometric falcon-eye SVG, scales from 16px to 256px\n- app/static/index.html: logo mark + \"Investigator's Toolkit\" tagline, inline SVG tab icons for all 7 tabs, capability hero cards on Crypto tab (collapse on first lookup), Try sample bu\n[…]\nerver hero collapse on crypto-result visibility\n- nginx/falconeye.conf: /favicon.ico and /favicon.svg location blocks with 7d immutable cache\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: visual upgrade — logo mark, favicon, tab icons, landing hero, s…",
          "author_name": "osintph",
          "author_login": "osintph",
          "committed_at": "2026-06-25T09:31:14Z",
          "body_truncated": true,
          "is_coding_agent": true
        }
      ],
      "releases_count": 33,
      "commits_last_year": 164,
      "latest_release_at": "2026-07-25T06:52:42Z",
      "latest_release_tag": "v3.24.1",
      "releases_from_tags": false,
      "days_since_last_push": 10,
      "active_weeks_last_year": 5,
      "days_since_latest_release": 11,
      "mean_days_between_releases": 0.1
    },
    "artifacts": {
      "collected": true,
      "structure": [],
      "declarations": []
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "readme_badges": {
        "hosts": [],
        "total": 0,
        "header": 0,
        "collected": true,
        "has_inspect_badge": false
      },
      "has_description": true,
      "has_contributing": true,
      "health_percentage": 71,
      "has_issue_template": false,
      "has_code_of_conduct": true,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": []
    },
    "popularity": {
      "forks": 3,
      "stars": 10,
      "watchers": 0,
      "fork_history": {
        "days": [
          {
            "date": "2026-07-20",
            "count": 1
          },
          {
            "date": "2026-07-22",
            "count": 1
          },
          {
            "date": "2026-07-24",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 3,
        "total_forks": 3
      },
      "star_history": null,
      "open_issues_and_prs": 0
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [
        "samples"
      ],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [],
      "largest_source_bytes": 352772,
      "source_files_sampled": 149,
      "oversized_source_files": 1,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "requirements.txt"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "pypi"
      ],
      "dependencies": [],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 0,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "osintph",
          "commits": 164,
          "avatar_url": "https://avatars.githubusercontent.com/u/70299699?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": false,
      "has_tests": true,
      "ci_workflows": [],
      "has_docs_dir": true,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": null,
            "reason": "no pull request found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/30 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 3,
            "reason": "project has 1 contributing companies or organizations -- score normalized to 3",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": null,
            "reason": "no workflows found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 0,
            "reason": "project was created within the last 90 days. Please review its contents carefully",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": null,
            "reason": "no dependencies found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "no SAST tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": null,
            "reason": "No tokens found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 0,
            "reason": "18 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "0719cea66764e4fbd45469712b33082e50a91e5d",
        "ran_at": "2026-08-05T08:58:56Z",
        "aggregate_score": 1.6,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "recent_prs": {
        "merged_7d": 0,
        "decided_7d": 0,
        "merged_30d": 0,
        "authors_30d": 0,
        "decided_30d": 0,
        "sample_size": 0,
        "window_days": 30,
        "sample_exhausted": false,
        "authors_probed_30d": 0,
        "newcomer_merged_30d": 0,
        "bot_prs_excluded_30d": 0,
        "newcomer_authors_30d": 0,
        "newcomer_decided_30d": 0
      },
      "ci_last_run_at": "2026-07-25T21:05:11Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": null,
      "ci_last_conclusion": null,
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/osintph/falconeye",
    "host": "github.com",
    "name": "falconeye",
    "owner": "osintph"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "weak",
      "name": "Overall health",
      "note": "The weighted overall 43 is calibrated to 39 on the published index scale (record calibration 2026-08-02).",
      "notes": [
        {
          "code": "overall_calibration",
          "params": {
            "raw": 43,
            "calibrated": 39,
            "calibration": "2026-08-02"
          }
        }
      ],
      "value": 39,
      "inputs": {
        "security": 16,
        "vitality": 70,
        "community": 49,
        "governance": 20,
        "calibration": "2026-08-02",
        "engineering": 58,
        "ai_readiness": 41,
        "weighted_overall_raw": 43
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 70,
        "weight": 0.21,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "commits_last_year": 164,
              "human_commit_share": 1,
              "days_since_last_push": 10,
              "active_weeks_last_year": 5
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 10 days ago",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 10
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "5/52 weeks with commits",
                "points": 3.5,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 5
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "164 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 164
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "exceptional",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "releases_count": 33,
              "latest_release_tag": "v3.24.1",
              "releases_from_tags": false,
              "days_since_latest_release": 11,
              "mean_days_between_releases": 0.1
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "33 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 33
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 11 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 11
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~0.1 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 0.1
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "exceptional",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "weak",
        "name": "Community & Adoption",
        "value": 49,
        "weight": 0.17,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 18,
            "inputs": {
              "forks": 3,
              "stars": 10,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "10 stars",
                "points": 15.5,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 10
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "3 forks",
                "points": 2.5,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "excellent",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "readme_badges": 0,
              "has_contributing": true,
              "has_issue_template": false,
              "has_code_of_conduct": true,
              "readme_badge_services": [],
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (AGPL-3.0)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "AGPL-3.0"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 18,
                "status": "met",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 13.5,
                "status": "met",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 20,
        "weight": 0.23,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 13,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 1 contributing companies or organizations -- score normalized to 3",
                "points": 3,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "critical",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Issue resolution, PR acceptance, Newcomer PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution",
                    "pr_acceptance",
                    "newcomer_pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 1,
            "inputs": {
              "merged_prs": 0,
              "open_issues": 0,
              "closed_issues": 0,
              "prs_merged_7d": 0,
              "prs_decided_7d": 0,
              "prs_merged_30d": 0,
              "prs_decided_30d": 0,
              "issue_closed_ratio": null,
              "closed_unmerged_prs": 0,
              "first_time_authors_30d": 0,
              "first_time_prs_merged_30d": 0,
              "first_time_prs_decided_30d": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "no issues or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_issues_or_data",
                    "params": {}
                  }
                ],
                "max_points": 42
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "no decided pull requests or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_decided_prs_or_data",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "newcomer_pr_acceptance",
                "name": "Newcomer PR acceptance",
                "detail": "no first-time contributor's PR decided in 30d",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_newcomer_prs",
                    "params": {
                      "days": 30
                    }
                  }
                ],
                "max_points": 13
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "weak",
            "name": "Ownership & stewardship",
            "note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "verified_domain"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 49,
            "inputs": {
              "followers": 15,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "osintph",
              "public_repos": 14,
              "account_age_days": 2169
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "personal (user) account",
                "points": 10,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_personal",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": "not applicable to user accounts",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_applicable_to_user_accounts",
                    "params": {}
                  }
                ],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "15 followers of osintph",
                "points": 8.7,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 15,
                      "login": "osintph"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "14 public repos, account ~5 yr old",
                "points": 20.4,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 14
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 5
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "moderate",
        "name": "Engineering Quality",
        "value": 58,
        "weight": 0.19,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "at_risk",
            "name": "Engineering practices",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 30,
            "inputs": {
              "has_ci": false,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": false,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "exceptional",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "topics": [
                "osint",
                "osint-tool",
                "osint-toolkit",
                "email-header-analyzer",
                "bec-detection",
                "phishing-analysis",
                "google-dorks",
                "powershell-deobfuscator",
                "crypto-investigation",
                "telegram-osint",
                "threat-intelligence",
                "ip-reputation",
                "incident-response",
                "claude-api",
                "anthropic",
                "fastapi",
                "cybersecurity-tools",
                "blue-team",
                "security-tools",
                "self-hosted"
              ],
              "has_wiki": true,
              "homepage": "https://falconeye.osintph.info",
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://falconeye.osintph.info",
                "points": 15,
                "status": "met",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "20 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 20
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "critical",
        "name": "Security",
        "value": 16,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "critical",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests, Dangerous-Workflow, Packaging, Pinned-Dependencies, Signed-Releases, Token-Permissions. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests",
                    "dangerous_workflow",
                    "packaging",
                    "pinned_dependencies",
                    "signed_releases",
                    "token_permissions"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 16,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 12,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 6,
              "scorecard_aggregate": 1.6
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 1 contributing companies or organizations -- score normalized to 3",
                "points": 0.8,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no workflows found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "no dependencies found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "no SAST tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "No tokens found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "18 existing vulnerabilities detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "weak",
        "name": "AI Readiness",
        "value": 41,
        "weight": 0.04,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "weak",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 1,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "100 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 100,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "weak",
            "name": "Verify loop (build / test / typecheck)",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Pinned-Dependencies. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_pinned_dependencies"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 36,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [],
              "agent_commit_share": 0.97,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "97 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 97,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "no dependencies found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "moderate",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "primary_language": "Python",
              "largest_source_bytes": 352772,
              "source_files_sampled": 149,
              "oversized_source_files": 1
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Python without a type-check config",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_typecheck_config_language",
                    "params": {
                      "language": "Python"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "1/149 source files over 60KB",
                "points": 54.6,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 149,
                      "oversized": 1
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "weak",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "example_dirs": [
                "samples"
              ],
              "has_mcp_signal": false,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": "samples",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "samples"
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight: agent tooling is a real maintenance signal, but its absence must never gate the top of the scale (calibration saturates at raw 91, so 100/100 remains reachable with AI Readiness at zero)."
      }
    ],
    "classification": {
      "top": [
        "application"
      ],
      "labels": [
        "network-service"
      ],
      "scores": {
        "network-service": 4
      },
      "primary": "network-service",
      "evidence": [
        {
          "tier": "description",
          "label": "network-service",
          "source": "description:network-service",
          "weight": 2
        },
        {
          "tier": "tags",
          "label": "network-service",
          "source": "tag:self-hosted",
          "weight": 2
        }
      ],
      "artifacts": [],
      "confidence": "low",
      "host_extension": false,
      "runs_as_process": true,
      "consumed_by_code": false
    },
    "metrics_version": "2.5.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-08-05T08:59:05.329225Z",
  "schema_version": "0.31.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/o/osintph/falconeye.svg",
  "full_name": "osintph/falconeye",
  "license_state": "standard",
  "license_spdx": "AGPL-3.0"
}

Las puntuaciones son señales, no garantías. Reflejan prácticas públicamente visibles en GitHub; no son una auditoría de código ni una garantía de seguridad.

Los datos ausentes se excluyen y los pesos se renormalizan; nunca se puntúan como cero. La metodología es versionada y abierta: métricas v2.5.0, esquema v0.31.0 — metodología completa · wiki de métricas.

Cómo se sitúa un resultado dentro del registro general: estadísticas agregadas.