이 프로젝트의 주요 변경 사항을 기록합니다.
형식은 Keep a Changelog를 따르며,
버전은 유의적 버전(SemVer) MAJOR.MINOR.PATCH를 사용합니다.
- MAJOR: 비호환(breaking) 변경 · MINOR: 하위호환 기능 추가 · PATCH: 하위호환 버그 수정.
type액션: input/textarea/contenteditable에 텍스트 입력(변형).submit:true면 Enter 키 이벤트 추가 디스패치(채팅형 UI 전송 트리거). 텍스트는 값으로만 주입 —script/eval노출 없음.read_clipboard액션(실험적):navigator.clipboard.readText()결과 회신.?requestClipboard=1opt-in + 클립보드 권한 사전승인 + 호출 시점 탭 포커스 필요(둘 다 브라우저가 강제, 자동화 우회 불가).fetch_image액션(실험적): 로그인 세션에 묶인 바이너리(이미지 한정 아님)를 로더가 쿠키 포함 fetch →<img src>GET 비콘으로 청크 분할 업로드(GET /api/v1/blob-chunk.json,BlobStore.add_chunk재조립) → 운영자가GET /agent/blob/{id}(admin 인증)로 회수.last: bool옵션으로 매칭 요소 중 마지막(문서 순서)을 대상 지정 가능.- CSP 우회형 인라인 북마크릿(실험적,
D-csp-script-src): 대상 사이트script-src가 외부<script>로딩을 막을 때, 로더 전체(loader.v2.iife.js)를 북마크릿 본문에 직접 실행해 로더 기동만은 우회. 연결 실패 시 4초 뒤alert()워치독(로더 미기동) + 화면 배너#__arachne_connfail(로더는 떴으나 폴링 채널까지 막힘, 재연결 시 자동 정리).POST /agent/operators응답에bookmarklet_inline필드 추가. - 관리 WebUI: 북마크릿 탭에 "활용 가이드" 카드(일반형/CSP 우회형 사용법) + CSP 우회형 북마크릿
버튼/컬럼. 교정 탭에
fetch_image결과용 "바이너리 업로드 결과" 패널(blob_id·content-type· 크기·다운로드 링크·이미지면 인라인 미리보기). docs/specs/23_extension_bridge.md— 브라우저 확장 기반 통신 채널 계획 문서(미구현, CSP 잔여 문제의 완전한 해법 설계).
BlobStore.add_chunk— 같은upload_id에total이 요청마다 다르게 오면(오류/변조) 재조립 시KeyError로 크래시하던 버그(idx를 최초 등록total기준으로 범위 검증하도록 수정).
scripts/build_client.sh/scripts/verify.py—loader.v2.js(ESM) 외에loader.v2.iife.js(IIFE, 인라인 북마크릿용)도 빌드·신선도 검증./agent/loader.js·/agent/loader.v2.js응답에Cache-Control: no-store추가(재빌드 후 재클릭 해도 구버전이 캐시돼 계속 로드되던 문제 해소).- 교정 UI의 "이미지 업로드" 라벨을 "바이너리 업로드"로 정정(이미지 한정 아님 —
img요소의src만 있으면 어떤 바이너리든 가능).
1.3.0 - 2026-07-14
교정 스텝 빌더 UX 표준화 — "클릭 이동 입력이 너무 불편함"이라는 UX 검토 피드백에서 도출한
로드맵(Phase 0~3)을 전부 구현. /ui 교정 탭의 액션 폼·피킹 흐름을 재설계.
ACTION_SCHEMA(단일 소스): 액션별 필드 정의(라벨·타입·필수 여부)와toPayload/fromPayload왕복 변환을 한 객체에 통합. 이전엔stepFormSpec()(폼 렌더링)·buildStep()(payload 생성)·stepToForm()(기존 값 복원) 세 함수가 액션별로 각자 분기해 arg/arg2/arg3/ arg4라는 포지셔널 필드의 의미가 액션마다 달랐다(같은 칸이 extract에선 "필드", scroll에선 "픽셀", wait에선 "밀리초").- 타입별 입력 위젯: enum→세그먼트 컨트롤(scroll의 "끝까지/셀렉터/픽셀", wait의 "요소/ 시간"처럼 텍스트로만 설명되던 "또는" 분기를 클릭 선택으로 대체), tags→칩 입력(추출 필드), urllist→줄바꿈 textarea(frame_tour의 다중 URL), selector→인라인 "🎯 픽" 버튼 내장.
- 인라인 편집: 단계 행을 클릭하면 그 자리에서 펼쳐져 편집(기존엔 상단 "추가"용 폼을 "수정" 모드로 재사용해 시선이 위아래로 이동해야 했음).
- 드래그 재정렬 + 복제: 단계 행 드래그로 순서 변경(↑/↓ 버튼도 유지), "⎘" 버튼으로 단계 복제.
- 빠른 시작 프리셋: "스크롤 후 추출", "더보기 클릭 후 추출", "iframe 상세 추출" 3종 — 자주 쓰는 조합을 빈 셀렉터 스켈레톤으로 즉시 삽입.
- 픽 후 자동 포커스 복귀: "창 열기"로 연 대상 창에서 요소를 클릭하면
window.opener.focus()로 관리자 창에 자동 복귀(가능한 경우) + 캡처된 셀렉터가 무장된 필드에 즉시 채워짐 + 토스트 확인. 대상 창은 이름 있는 창으로 열려 화면 오른쪽에 나란히 배치되고 재사용된다("🔎 대상 창 보기" 버튼으로 언제든 재포커스). - 라이브 하이라이트 안내: 추출 계열 단계를 테스트/재생하면 대상 페이지의 매칭 요소가
이미 초록으로 표시되던 기존 동작(
markMatches)에, "결과가 대상 페이지에 표시됨" 안내 배너 + "확인하기"(대상 창 포커스) 버튼을 추가해 놓치지 않게 함. - 교정 UI에
frame_tour액션 추가(기존엔 backend만 지원, UI 드롭다운에 없었음).
- "미입력" 경고가
scroll by:800px처럼 셀렉터가 필요 없는 정적 완결 액션에도 잘못 표시되던 버그(정적 필수 필드만 검사하도록 수정).
tests/client/ts/ui_steps.test.ts(GT-uistep)를ACTION_SCHEMA추출·검증으로 재작성 (구buildStep/stepFormSpec함수 제거에 따른 갱신, admin.py 실소스 추출 방식은 유지).loader.ts의onPick()에window.opener.focus()안전 호출 추가(opener 없으면 무해한 no-op).
1.2.1 - 2026-07-13
세그폴트 근본 수정 — 백그라운드 워커가 DB 작업 중일 때 취소되면 드물게 프로세스가 크래시하던(exit 139) 문제를 재현·확인 후 수정.
- 워커 취소-DB작업 경합 세그폴트(D-worker-safe-cancel):
background_queue_worker/background_retention_worker가run_in_executor로 sqlite 작업을 진행 중일 때 lifespan shutdown이Task.cancel()을 호출하면, 이미 실행 중인 스레드는 멈추지 않은 채(파이썬concurrent.futures는 실행 중인 작업을 취소하지 못함)get_db_session_ctx의finally: conn.close()가 같은 sqlite3 커넥션을 다른 스레드가 여전히 쓰는 도중 실행돼 (check_same_thread=False) C레벨 레이스로 세그폴트했다.asyncio.shield()로 감싸도asyncio.Runner.close()의_cancel_all_tasks()가 루프의 모든 태스크를 무차별 취소해 막지 못함을 실측으로 확인. 반복 PUT 직후 즉시 서버 종료 시나리오로 안정 재현(재현 스크립트 기준 exit 139) 후, lifespan shutdown이 워커의asyncio.Event(DB 작업 중 clear, 큐 대기로 복귀 시 set)를 먼저 기다린 뒤에만 취소하도록 수정 — 800회 연속 재현 스크립트 무크래시로 검증. - 이에 따라
tests/integration/test_metrics.py의 방어적time.sleep(0.2)워크어라운드 제거 (근본 원인이 수정돼 더 이상 필요 없음).
1.2.0 - 2026-07-13
운영 성숙도 로드맵 — rate-limit·메트릭 export·데이터 보존정책·PostgreSQL(옵션)·MCP 서버 실등록(옵션). 모두 하위호환 기능 추가(기본값은 기존 동작과 동일하거나 더 안전한 쪽).
- Rate-limit 미들웨어(
adapters/rate_limit.py): 전 엔드포인트 고정 윈도(60s) 상한(기본 600/분,RATE_LIMIT_PER_MINUTE). 키 우선순위op쿼리파라미터 > client IP(cloudflared 등 리버스 프록시 뒤에서 IP가 전부 동일해지는 문제 대비). 초과 시 429+Retry-After,rate_limited카운터(/status). GET /metrics(Prometheus 텍스트 노출 형식,api/v1/metrics.py): 카운터 6종(published· received·stored·errors·queue_dropped·rate_limited) +arachne_build_info게이지. 새 의존성 없이 직접 구성.- 데이터 보존정책(
RETENTION_DAYS, 기본0=무제한):N>0설정 시received_at기준 N일 경과 수집 데이터를 기동 즉시+매일 자동 정리. 수동 트리거POST /admin/retention/purge. - PostgreSQL 백엔드(옵션,
arachne[postgres]extra):adapters/store/postgres_repo.py—DB_URL=postgresql://...면 자동 분기(get_repo()). SQLite와 동일 공개 메서드 시그니처를 유니언 타입(api/deps.py:CollectionRepo)으로 mypy가 상시 검증. asyncpg 네이티브 async 커넥션 풀링.GT-store-pg(tests/integration/test_postgres_repo.py)가 실 PostgreSQL로 SQLite와 동일 계약을 재검증(설치·접속 불가 시 자동 skip). - MCP 서버 실등록(옵션,
arachne[mcp]extra):adapters/mcp/server.py— mcp SDKFastMCP로 S0/S1 도구(list_recipes·run_recipe·get_result·connect·action·disconnect)를 같은 프로세스·같은 포트의/mcp에 streamable-http로 노출.ENABLE_MCP(기본 OFF) + mcp SDK 설치 여부를 요청 시점마다 동적으로 재확인(GT-mcp-server가 실 mcp 클라이언트로 프로토콜 왕복 검증). - MCP
connecthost 자동 강제(connect(op, repo=repo)): host 인자를 생략해도 op의 최근client_meta.url에서 hostname을 자동 유도해 allowlist를 바인딩 — 호출자가 host를 깜빡해도 무방비로 뚫리지 않음.
scripts/verify.py정밀도 개선:check_endpoint_auth가 이제 GET도 스캔(admin 전용 GET에서require_admin누락 적발),pytest.raises정확 매칭(이름에 우연히 "raises"가 들어간 무관 컨텍스트매니저 오판정 제거), 신설check_no_asgitransport_stored_fakegreen이 lifespan 미기동 상태의status=="stored"가짜green 패턴을 정적으로 적발(D1 함정,specs/07).
- 개방 읽기 채널(
/api/v1/*.json·sync.js)의 보안모델을 명문화:require_admin을 걸지 않는 것은 의도된 설계(자기 제출 확인·클라 동기화 정상 흐름 보존)이며, op_id(96bit)·task_id (122bit) 엔트로피 + 신규 rate-limit로 defense-in-depth(specs/08 §7).
1.1.0 - 2026-07-12
iframe 기반 페이지 제어 추출 — 목록→상세 이동형 사이트를 최상위 내비게이션 없이 순회 수집.
frame_extract액션: 같은 출처 상세 URL을 숨김 iframe으로 로드해 그 안에서 추출하고 iframe만 제거(terminal). 최상위 문서가 이동하지 않아 로더/교정 세션이 끊기지 않는다(click실제 내비게이션이 로더를 언로드해 세션이 끊기던 문제 해소). cross-origin은 클라이언트 즉시 거부 + 서버 same-origin 방어(op의 최근 client_meta.url origin과 비교, 클라 우회 대비). (docs/specs/22_frame_extract.md)frame_tour액션: 같은 출처 상세 URL 여러 개를 전면(전체화면) iframe 하나로 차례로 이동하며 각각 추출(frame_extract의 가시적 다중페이지 순회판). 사용자는 iframe이 화면 전체를 덮은 채 각 상세가 넘어가는 것을 본다. 최상위 문서 불변 → 세션 비단절. 실측: 실 m.news.nate.com 최신뉴스 상위 10개를 북마크릿 1회로 전면 순회하며 본문 획득(10/10, 순회 후 connected=true).- WebUI 교정 탭에
🪟 iframe 추출액션 옵션(url·셀렉터·필드·대기ms 입력).
extractItems(클라 추출 유틸)에root파라미터 추가 — iframe의contentDocument를 추출 대상으로 지정(동일 추출 로직 재사용, 신규 로직 아님).
- frame_extract/frame_tour의 same-origin 강제를 클라이언트+서버 이중 검증. iframe append 시 about:blank 초기 로드로 발화하는 onload를 무시(실제 src 문서만 추출).
1.0.0 - 2026-06-21
첫 안정 공개 릴리스 — 공개 API 안정화 선언(SemVer 시작). 브라우저 세션 기반 동적 제어형 데이터 수집 시스템: 서버가 클라이언트(브라우저)를 원격 통제하는 명령-수행-보고(Command-Execute-Report) 패턴. Zero-Footprint · Dynamic Control · Decoupled Expansion.
- v2 커맨드 인터프리터: 서버가 클라를 타입 커맨드로 통일 제어(북마크릿 불변·서버만 갱신). 단일 소스 Pydantic →
commands.d.ts순수 파이썬 codegen(ES2017, Node 무의존). - 라이브 교정 콘솔: 타입 액션 시퀀스(click·drag·scroll·hover·wait·swipe·extract) → 추출 레시피. 행위 검증(DOM 변화)으로 액션 성공 확인.
scripteval 금지(화이트리스트 경계). - MCP 에이전트 제어: 라이브 파이프를 MCP 도구로 노출. host allowlist(기본 거부)·동시성 상한·rate-limit·op TTL을 코드 상수+게이트로 강제. 봇 회피·대량 스크래핑은 비목표.
- 식별자 3종(op_id·client_id·task_id): 도메인 넘는 통합·세션·사이클 매개. 핑거프린팅 미사용(GA식 익명 랜덤).
- 무손실 적재: write-ahead(동기 커밋 후 202) +
(task_id, stage)멱등 + 큐 워커received→stored전이. 기동 시 자동 복구(재시작 정체분 무인개입 stored 전이). - 공개 URL 터널 토글:
ENABLE_TUNNEL=1명시 opt-in으로 cloudflared 임시 quick tunnel(기본 OFF=로컬 전용).PUBLIC_URL(정적)·DISABLE_TUNNEL(kill-switch). 임시용이며 정상 서비스는 고정 HTTPS·인바운드 필요. - WebUI(
/ui): 현황·북마크릿·교정·사이트·데이터·로그 탭(코드·curl 불필요). - 저장소: 기본 SQLite(WAL·
busy_timeout) + 옵션 PostgreSQL(동일 인터페이스). 옵저버빌리티 카운터(published·received·stored·errors·queue_dropped).
- 관리 인증 secure-by-default: 기본 ON(Jupyter식 자동 토큰·콘솔 출력), fail-open 폐기. timing-safe 비교, 토큰 비유출(
/admin/logs미노출). 면제는/ui셸만. - 클라 실행 경계:
script커맨드(서버 응답 eval) 기본 비활성(?allowScript=1opt-in),beacon송출 대상을 서버 오리진으로 제한,loader.js?op=반사형 인젝션 차단. - MCP 가드 누적:
run_recipe발행 시점 step 재검증·시퀀스 상한·정규화 발행·동시성 좀비 슬롯 청소. - HMAC/
sig死코드 제거, CORSallow_credentials=False. 실타겟 수집은@pytest.mark.live수동 승인(robots/ToS는 운영자 책임).
- 완료 게이트
scripts/verify.py(pytest·ruff·mypy·bun·보안 AST). 라이선스 MIT. - 1차 목표는 내부·소규모 단일 노드. 단일 프로세스(인메모리 세션/큐) 한계는 의도된 경계이며 수평 확장은 인터페이스 교체로(
docs/specs/19_deployment_scaling.md).