이 레포가 무엇을 하는 물건인가.
수사물에서 형사가 용의자·장소·전화번호 사진을 벽에 붙이고 실로 잇는 장면을 떠올려 보세요. Flowsint는 바로 그 보드를 디지털로 옮긴 것입니다. 다만 실을 사람이 직접 잇는 대신, "이 도메인과 연결된 IP를 찾아줘" 같은 자동 도구(엔리처)가 새 단서를 캐내 선을 그어줍니다.
Flowsint는 OSINT (Open Source Intelligence, 공개 출처 정보) 조사를 위한 그래프 기반 수사 플랫폼입니다. OSINT란 누구나 접근할 수 있는 공개된 정보(WHOIS 등록 정보, DNS 기록, 공개 SNS 계정, 유출된 데이터베이스 목록 등)를 모아 의미 있는 그림을 그리는 활동을 말합니다. 해킹이 아니라, 이미 공개된 조각들을 잘 엮는 것이 핵심입니다.
사용 방식은 단순합니다. 화면에 도메인·이메일·IP 같은 엔티티(entity, 조사 대상이 되는 객체) 노드를 하나 놓고, 그 노드에서 엔리처를 실행하면 관련된 새 노드들이 그래프에 붙습니다. 이 과정을 반복하면 처음엔 점 하나였던 화면이 수백 개의 노드와 관계선으로 이루어진 관계 지도로 자라납니다.
트렌딩 이유 · 경쟁 대비 장점.
OSINT 그래프 도구의 대명사는 오랫동안 상용 소프트웨어인 Maltego였습니다. 강력하지만 비싸고, 데이터가 외부 서버를 거치는 구조라 민감한 조사에는 부담이 있었습니다. Flowsint는 완전 오픈소스 + 자체 호스팅(self-hosted)으로 이 빈자리를 노립니다 — 내 컴퓨터에서 Docker로 띄우면 끝이고, 데이터는 한 발짝도 밖으로 나가지 않습니다.
두 번째 강점은 확장성입니다. 엔리처가 도메인·IP·ASN·이메일·전화번호·암호화폐 지갑·SNS 닉네임 등 30종 이상 내장돼 있고, 모듈식 구조라 새 엔리처를 코드로 쉽게 추가할 수 있습니다. 세 번째는 성능 — 프론트엔드가 WebGL 렌더링(pixi.js)을 써서 노드가 수천 개로 불어나도 화면이 버벅이지 않습니다.
| 비교 항목 | 기존 방식 (Maltego 등 상용) | Flowsint의 접근 |
|---|---|---|
| 비용/접근 | 유료 라이선스 · 클라우드 의존 | 오픈소스(Apache-2.0) · 무료 · 자체 호스팅 |
| 데이터 프라이버시 | 외부 서버 경유 가능 | 전부 내 머신에 저장 — 외부 유출 없음 |
| 확장 | 플러그인 SDK(폐쇄적일 수 있음) | 모듈식 파이썬 엔리처 — 코드로 자유 추가 |
| 대규모 그래프 | 노드 많아지면 느려짐 | WebGL(pixi.js) 렌더로 수천 노드도 매끄럽게 |
기자나 사기 조사팀이 다루는 단서는 그 자체가 극도로 민감합니다. 조사 내용이 제3자 클라우드를 거치면, 누가 무엇을 조사 중인지가 새어나갈 위험이 생깁니다.
Docker로 로컬에 통째로 띄우는 구조라, PostgreSQL·Neo4j·조사 데이터가 모두 내 디스크에만 남습니다. README도 "OSINT 조사엔 높은 수준의 프라이버시가 필요하다"고 명시하며 이 점을 전면에 내세웁니다.
프론트엔드·백엔드·데이터·인프라를 한눈에.
Flowsint는 TypeScript 프론트엔드 + Python 백엔드의 조합입니다. 언어 비중도 거의 반반(TS 약 153만 줄, Python 약 128만 줄)이라 어느 한쪽이 곁다리가 아니라 둘 다 본체입니다. 가장 독특한 점은 데이터베이스를 둘 쓴다는 것 — 관계형 DB(PostgreSQL)와 그래프 DB(Neo4j)를 역할에 따라 나눠 씁니다.
| 레이어 | 기술 / 버전 | 역할 |
|---|---|---|
| 프론트엔드 | React 19 + Vite 5 | 화면 렌더링·빌드. React Compiler로 자동 최적화. |
| 그래프 UI | @xyflow/react · react-force-graph-2d · pixi.js | 노드/엣지 편집 + 힘기반 자동 배치 + WebGL 고속 렌더. |
| UI 부품 | Radix UI · Tailwind 4 · framer-motion | 접근성 좋은 컴포넌트 + 스타일 + 애니메이션. |
| 상태/데이터 | TanStack Query · Zustand · Zod | 서버 데이터 캐싱 · 전역 상태 · 스키마 검증. |
| 보조 도구 | Monaco · Tiptap · xterm.js · AI SDK | 코드 에디터 · 노트 · 터미널 · AI 조사 보조 채팅. |
| 백엔드 API | FastAPI (Python) | REST API·인증·이벤트 스트리밍. uvicorn으로 구동. |
| 비동기 작업 | Celery + Redis | 오래 걸리는 스캔을 백그라운드에서 실행. |
| 타입/모델 | Pydantic | 엔티티(도메인·IP·이메일…) 데이터 모델 정의. |
| 데이터 (관계형) | PostgreSQL 15 | 계정·인증·메타데이터. Alembic으로 마이그레이션. |
| 데이터 (그래프) | Neo4j 5 + APOC | 엔티티 간 관계. Cypher 쿼리로 탐색. |
| 인프라 | Docker Compose · Make | 전 서비스 컨테이너화 · 명령 단축. |
데이터가 어디서 어디로 흐르는가.
Flowsint는 다섯 개의 독립 모듈로 쪼갠 모듈식 모노레포(modular monorepo)입니다. 하나의 저장소 안에 여러 패키지를 두되, 서로의 의존 방향을 한 줄기로 고정한 것이 특징입니다. 위층은 아래층을 알지만 아래층은 위층을 모릅니다 — 덕분에 엔리처(아래층)를 고쳐도 화면(위층)이 깨지지 않습니다.
구조가 한눈에 안 들어와도 괜찮습니다. 흐름 한 줄기를 끝까지 따라가 보면 전체가 잡힙니다. "사용자가 도메인 노드에서 '서브도메인 찾기' 엔리처를 실행했다"는 가장 흔한 동작 하나를 추적해 봅시다.
① 화면(app): 그래프에서 도메인 노드를 우클릭 → "Subdomain Discovery" 선택. React Flow가 클릭을 받아 API로 요청을 보냅니다.
② API(api): FastAPI가 요청을 받아 인증을 확인하고, "이 작업을 시작해달라"고 core에 넘깁니다.
③ 오케스트레이터(core): 작업을 Celery 태스크로 만들어 Redis 큐에 넣습니다. API는 여기서 바로 응답 — 화면은 멈추지 않습니다.
④ 일꾼(core/Celery worker): 큐에서 작업을 꺼내 해당 엔리처를 실행합니다.
⑤ 엔리처(enrichers): 실제 서브도메인 열거 로직이 돌며 결과(새 엔티티 목록)를 만듭니다. 이때 외부 API 키가 필요하면 Vault에서 복호화해 꺼냅니다.
⑥ 그래프 저장(Neo4j): 찾은 서브도메인들을 노드로, "이 도메인의 하위"라는 관계를 엣지로 Neo4j에 기록합니다.
⑦ 화면 갱신(app): 결과 이벤트가 스트리밍으로 프론트에 도착하고, 그래프에 새 노드가 스르륵 붙습니다. 한 점이 여러 점으로 번집니다.
MASTER_VAULT_KEY_V1)로 각 키를 암호화해 보관합니다. 키 이름에 V1이 붙은 건 나중에 마스터 키를 교체(rotation)할 수 있게 버전을 매겨둔 것입니다. 은행 금고를 큰 열쇠 하나로 잠그고, 그 안에 작은 금고들을 또 넣어둔 셈입니다.API는 접수 창구, Core는 관리실, Enrichers는 현장 요원, Neo4j는 사건 보드. 창구가 의뢰를 받아 관리실에 넘기면, 관리실이 현장 요원을 보내 단서를 캐오고, 그 단서를 사건 보드에 핀으로 꽂아 실로 잇습니다. 사용자는 보드(화면)만 보면 됩니다.
어떤 폴더가 무슨 일을 하나.
루트에는 다섯 개의 flowsint-* 모듈과, 환경별 Docker Compose 파일 세 종, 그리고 모든 명령을 묶어주는 Makefile이 있습니다. 모듈 이름만 봐도 역할이 드러나는 설명적 작명이 이 레포의 관례입니다.
| 모듈 / 파일 | 역할 |
|---|---|
flowsint-app/ | 화면 전부. React Flow 그래프 캔버스, 노드 편집, AI 채팅, 노트 에디터. |
flowsint-api/ | REST API·인증·스키마. 외부에서 들어오는 모든 요청의 관문. |
flowsint-core/ | 핵심 ★ — 작업 오케스트레이션, Celery 설정, Vault, Postgres/Neo4j 연결. |
flowsint-enrichers/ | "단서 캐는 로직"이 모인 곳. 새 조사 능력을 더할 땐 여기에 추가. |
flowsint-types/ | 도메인·IP·이메일·지갑 등 엔티티의 데이터 모양. 모든 모듈의 공통 어휘집. |
routeTree.gen.ts | TanStack Router가 파일 구조를 보고 자동 생성하는 라우트 트리(직접 수정 X). |
개발 워크플로도 이 구조를 그대로 따릅니다 — 새 타입은 flowsint-types에, 새 엔리처는 flowsint-enrichers에, 새 API는 flowsint-api에. 어디에 무엇을 넣을지가 폴더 이름만으로 정해지니, 초보자가 길을 잃기 어렵습니다.
이 레포에서 배울 만한 것 + 어디를 보면 되는지.
"왜 DB를 둘 쓰지?"는 그 자체가 좋은 설계 공부거리입니다. 계정·설정처럼 표로 떨어지는 데이터는 PostgreSQL에, 엔티티 간 관계 그물은 Neo4j에 — 데이터 모양에 맞는 도구를 고른 사례입니다. flowsint-core의 DB 연결 코드와 docker-compose의 neo4j 설정을 함께 보면 감이 옵니다.
웹앱이 느려지는 흔한 원인은 "느린 작업을 요청 안에서 처리"하는 것입니다. Flowsint는 스캔을 Celery 태스크로 분리해 화면을 멈추지 않게 합니다. flowsint_core.core.celery 워커 설정과 Makefile의 celery 타깃이 출발점입니다.
"입력 엔티티 → 처리 → 출력 엔티티"라는 한 가지 약속을 정해두면, 같은 모양의 모듈을 끝없이 찍어낼 수 있습니다. 30종 넘는 엔리처가 모두 이 패턴을 따릅니다. flowsint-enrichers의 베이스 클래스를 읽으면 "확장 가능한 구조"의 전형을 배웁니다.
노드 수천 개를 부드럽게 그리는 건 의외로 어렵습니다. Flowsint는 React Flow로 상호작용을, pixi.js(WebGL)로 렌더 성능을, d3-force로 자동 배치를 맡깁니다. 프론트 성능 최적화의 살아있는 교본입니다.
엔리처가 어떤 모양인지 감을 잡기 위한 개념 코드입니다(실제 코드가 아니라 패턴을 단순화한 예시):
# 모든 엔리처가 공유하는 약속: 엔티티를 받아 새 엔티티들을 돌려준다
class SubdomainEnricher(BaseEnricher):
input_type = Domain # 도메인을 입력으로
output_type = Domain # 서브도메인(역시 도메인)을 출력으로
def run(self, entity: Domain) -> list[Domain]:
subs = discover_subdomains(entity.name) # 실제 스캔
return [Domain(name=s) for s in subs] # 그래프에 붙을 새 노드들
모든 엔리처가 같은 모양의 입출구를 가지면, 오케스트레이터는 엔리처의 속을 몰라도 "넣고 → 받기"만 하면 됩니다. 콘센트 규격이 같으면 어떤 가전이든 꽂히는 것과 같습니다. 새 능력을 더하는 일이 "콘센트 규격에 맞는 기기 하나 만들기"로 줄어듭니다.
돌리려면 무엇이 필요한가.
좋은 소식은 전부 Docker로 묶여 있다는 점입니다. 언어 런타임을 일일이 깔 필요 없이, Docker와 Make만 있으면 make prod 한 줄로 다섯 개 서비스가 한꺼번에 뜹니다.
| 항목 | 요구사항 |
|---|---|
| 필수 도구 | Docker + Make (이 둘이면 실행 끝) |
| 로컬 개발(선택) | uv(Python 의존성) · Node.js + yarn(프론트엔드) |
| 구동 서비스 | PostgreSQL 15 · Neo4j 5 · Redis · API · 프론트엔드 (모두 컨테이너) |
| 주요 포트 | 5173(화면) · 5001(API) · 7474·7687(Neo4j) · 5433(Postgres) · 6379(Redis) |
| OS | macOS · Linux · Windows (Docker가 도는 곳이면 어디든) |
| 계정 | 기본 계정 없음 — 첫 실행 후 /register에서 직접 생성 |
# 설치 3줄 — README가 약속하는 가장 빠른 길
git clone https://github.com/reconurge/flowsint.git
cd flowsint
make prod # → http://localhost:5173/register 에서 계정 생성
compose 파일의 Postgres 기본 계정은 flowsint/flowsint이고, Neo4j·인증 시크릿(AUTH_SECRET)·금고 키(MASTER_VAULT_KEY_V1)는 .env로 주입됩니다. 내 PC 실험용이면 괜찮지만, 외부에 노출되는 서버라면 이 값들을 강한 임의값으로 반드시 교체해야 합니다.
난이도별로 손에 익히는 단계.
Flowsint는 강력한 정보 수집 도구입니다. 저장소의 ETHICS.md는 무단 감시·괴롭힘·신상털기(doxxing)를 금지합니다. 실습은 내가 소유한 도메인이나 공개 테스트용 대상에만 하세요. 남의 정보를 동의 없이 캐는 것은 불법일 수 있습니다.
make prod로 실행 → /register에서 계정 생성 → 내 소유 도메인 노드를 하나 놓고 "DNS Resolution" 엔리처를 돌려본다. 도메인이 IP 노드로 이어지는 첫 관계선을 확인.
도메인 → IP → ASN으로 엔리처를 연달아 실행하며 그래프가 어떻게 번지는지 본다. Neo4j 브라우저(localhost:7474)에 접속해 같은 데이터가 노드/관계로 어떻게 저장됐는지 Cypher로 직접 조회.
섹션 4의 "7단계 해피 패스"를 실제 코드에서 따라가 본다. flowsint-api의 라우트 → flowsint-core의 Celery 태스크 → flowsint-enrichers의 실행 로직 순으로 파일을 열어 연결고리를 눈으로 잇기.
flowsint-enrichers의 베이스 클래스를 상속해 간단한 엔리처를 하나 만든다(예: 이메일 → 도메인 추출). 타입은 flowsint-types에 정의하고, 테스트는 uv run pytest로. 플러그인 패턴을 몸으로 익히는 단계.
한 주씩 따라가는 계획.
| 주차 | 주제 | 학습 자료 |
|---|---|---|
| 1주차 | OSINT·그래프 사고방식 + 로컬 실행 | README · ETHICS.md · 실습 1 |
| 2주차 | 그래프 DB 기초 (Neo4j · Cypher) | Neo4j 공식 튜토리얼 · 실습 2 |
| 3주차 | FastAPI + Celery 비동기 백엔드 | FastAPI 문서 · flowsint-core 정독 |
| 4주차 | 엔리처 패턴 · Pydantic 모델링 | flowsint-enrichers·flowsint-types · 실습 4 |
| 5주차 | React Flow + 대규모 그래프 시각화 | React Flow 문서 · pixi.js · flowsint-app |
| 6주차 | Docker Compose 멀티서비스 운영 | 3종 compose 파일 비교 · 리버스 프록시 기초 |
본문에 나온 용어 빠른 참조.
| 용어 | 의미 |
|---|---|
| OSINT | Open Source Intelligence. 공개된 정보를 모아 분석하는 조사 기법. |
| Enricher | 엔티티 하나를 받아 관련 엔티티를 캐내 그래프에 추가하는 자동 모듈. |
| Entity | 조사 대상이 되는 객체(도메인·IP·이메일·전화·지갑 등). 그래프의 노드. |
| 그래프 DB | 데이터를 노드(점)와 관계(선)로 저장하는 DB. 여기선 Neo4j. |
| Neo4j / APOC | 대표적 그래프 DB와 그 확장 라이브러리(APOC). Cypher 언어로 질의. |
| Cypher | Neo4j의 질의 언어. "이 노드에서 선을 따라가라"를 패턴으로 표현. |
| Celery / Redis | 비동기 작업 큐. 오래 걸리는 스캔을 백그라운드 일꾼이 처리. |
| FastAPI | 파이썬 고속 웹 API 프레임워크. 백엔드 관문. |
| React Flow | 노드·엣지 캔버스를 만드는 React 라이브러리. 수사 보드의 토대. |
| Vault | 외부 API 키를 마스터 키로 암호화해 보관하는 시크릿 금고. |
| WHOIS | 도메인 등록자·등록일 등을 조회하는 공개 프로토콜. |
| ASN | Autonomous System Number. 인터넷에서 IP 묶음을 운영하는 조직의 번호. |
| Maigret | 닉네임 하나로 수백 개 사이트의 계정 존재 여부를 찾는 오픈소스 도구. |
| self-hosted | 외부 클라우드가 아닌 내 서버/PC에서 직접 돌리는 방식. |