Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
atproto — 대규모 자가 호스팅 소셜 네트워킹을 위한 성능 최적화된 AppView, Rust 기반 firehose 인덱서, Redis 캐싱 및 커뮤니티 기능을 갖춘 AT Protocol 참조 구현의 포크 | Kitploit
도구/GitHubGitHub/blacksky-algorithms/atproto
Cloud Infrastructure SecurityConfiguration AuditingSecret DetectionIdentity & Access Management (IAM)AuthenticationMisconfigurationAPI SecurityDatabase SecurityLog Analysis
GitHubblacksky-algorithms/atproto

atproto

대규모 자가 호스팅 소셜 네트워킹을 위한 성능 최적화된 AppView, Rust 기반 firehose 인덱서, Redis 캐싱 및 커뮤니티 기능을 갖춘 AT Protocol 참조 구현의 포크

9436일 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
저장소 보기

Blacksky AppView

이것은 Bluesky Social PBC의 AT 프로토콜 참조 구현을 포크한 Blacksky의 포크입니다. 이 포크는 api.blacksky.community에서 AppView를 구동합니다.

투명성과 다른 커뮤니티가 이 작업의 혜택을 받을 수 있도록 공개합니다. 이 저장소는 기여, 이슈 또는 PR을 받지 않습니다. 표준 atproto 구현을 원한다면 bluesky-social/atproto를 사용하세요.

변경 사항

모든 변경 사항은 packages/bsky (appview 로직), services/bsky (런타임 설정) 및 하나의 사용자 정의 마이그레이션에 있습니다. 나머지는 업스트림과 동일합니다.

기본 제공 Firehose Consumer를 사용하지 않는 이유

업스트림 데이터플레인에는 이벤트를 직접 인덱싱하는 TypeScript firehose 컨슈머(subscription.ts)가 포함되어 있습니다. 여러 가지 이유로 이를 Rust 인덱서인 rsky-wintermute로 교체했습니다.

  • 규모 성능: TypeScript 컨슈머는 이벤트를 순차적으로 처리합니다. 네트워크 규모(초당 약 1,000개 이벤트, 총 185억 개 레코드)에서 초당 약 90개 레코드의 전체 백필은 6.5년이 걸립니다. Wintermute는 병렬 큐 처리를 통해 초당 10,000개 이상의 레코드를 목표로 합니다.
  • 백필 아키텍처: Wintermute는 라이브 인덱싱과 백필을 독립적인 큐(firehose_live, firehose_backfill, repo_backfill, labels)로 분리합니다. 라이브 이벤트는 백필 작업에 의해 차단되지 않습니다.
  • 운영 도구: Wintermute에는 특정 계정의 직접 인덱싱, PLC 디렉터리 대량 가져오기, 레이블 스트림 재생, blob 참조 복구 및 큐 관리 유틸리티가 포함되어 있습니다. 이는 AppView를 처음부터 부트스트래핑할 때 모두 필요합니다.

이 저장소의 데이터플레인과 appview는 그대로 실행됩니다. wintermute가 쓰는 PostgreSQL 데이터베이스에서 읽습니다. 기본 제공 firehose 구독을 시작하지 않을 뿐입니다.

성능 및 운영 수정 사항

이러한 수정 사항은 대규모로 AppView를 자체 호스팅하는 모든 사람에게 광범위하게 유용합니다.

LATERAL JOIN 쿼리 최적화 (packages/bsky/src/data-plane/server/routes/feeds.ts)

  • getTimeline 및 getListFeed가 PostgreSQL LATERAL JOIN을 사용하도록 다시 작성되어 전체 테이블 스캔 대신 사용자별 인덱스 사용을 강제합니다. 수천 개의 계정을 팔로우하는 사용자에게 큰 개선입니다.

Redis 캐싱 계층 (packages/bsky/src/data-plane/server/cache/)

  • 액터 프로필(60초 TTL), 레코드(5분), 상호작용 수(30초), 게시물 메타데이터(5분)
  • 프로덕션 트래픽에서 데이터베이스 부하 감소
  • 알려진 문제: 액터 캐시에 protobuf 타임스탬프 직렬화 버그가 있습니다. Timestamp 객체가 Redis를 통한 JSON 왕복 후 .toDate() 메서드를 잃어버려 캐시 적중 시 프로필 하이드레이션이 불완전해집니다. 현재 Redis 캐싱을 비활성화한 상태로 실행 중입니다. 수정 방법은 캐시 쓰기 시 타임스탬프를 ISO 문자열로 직렬화하고 읽을 때 재구성하는 것입니다.

알림 기본 설정 서버 측 적용 (packages/bsky/src/api/app/bsky/notification/listNotifications.ts)

  • 클라이언트가 reasons를 지정하지 않으면 서버는 사용자의 저장된 알림 기본 설정을 적용합니다. 이것이 없으면 기본 설정은 클라이언트 측에서만 적용되며 효과가 없습니다.

인증 검증기 만료된 서명 키 수정 (packages/bsky/src/auth-verifier.ts)

  • JWT 검증 재시도(forceRefresh) 시 데이터플레인의 인메모리 ID 캐시를 우회하고 PLC 디렉터리에서 직접 DID 문서를 확인합니다. 계정 마이그레이션 후 서명 키가 회전되지만 캐시에 이전 키가 남아 있을 때 발생하는 인증 실패를 수정합니다.

JSON 정리 (packages/bsky/src/data-plane/server/routes/records.ts)

  • 저장된 레코드에서 JSON 파싱 전에 null 바이트(\u0000) 및 제어 문자를 제거합니다. 이는 RFC 8259에 유효하지만 Node.js JSON.parse()에서 거부되어 데이터플레인에서 rowToRecord 파싱이 조용히 실패하고 누락된 게시물로 나타납니다.

커뮤니티 게시물 (Blacksky 전용)

개별 PDS가 아닌 AppView에서 호스팅되는 비공개 커뮤니티 게시물을 위한 인프라입니다. Blacksky의 작동 방식에 특화되어 있지만 다른 커뮤니티의 참조가 될 수 있습니다.

  • 사용자 정의 어휘 네임스페이스 community.blacksky.feed.*와 제출, 가져오기, 삭제, 타임라인 및 스레드 보기 엔드포인트
  • 별도의 community_post 테이블 (마이그레이션: 20260202T120000000Z-add-community-post.ts)
  • 데이터플레인 및 API 계층에서의 멤버십 게이팅
  • 표준/커뮤니티 게시물 스레드 혼합을 위한 getPostThreadV2와의 통합
  • 별도의 멤버십 데이터베이스 필요 (BLACKSKY_MEMBERSHIP_DB_URL)

아키텍처

root@kitploit:~
Bluesky Relay (bsky.network)
     |
     v
rsky-wintermute -----> PostgreSQL 17 <----- Palomar
  (Rust indexer)            |                (Go search)
  - firehose consumer       |                     |
  - backfiller              |                     v
  - label indexer           |               OpenSearch
  - direct indexer          |
                            v
                    bsky-dataplane (gRPC :2585) <--- Redis (optional)
                            |
                            v
                    bsky-appview (HTTP :2584)
                            |
                            v
                    Reverse proxy (Caddy/nginx)

구성 요소 개요

rsky-wintermute 상세

Wintermute는 4개의 병렬 처리 경로를 가진 모놀리식 Rust 서비스입니다.

  • Ingester: WebSocket을 통해 bsky.network firehose에 연결, 이벤트를 Fjall(임베디드 키-값 저장소) 큐에 기록
  • Indexer: 큐에서 읽기, 레코드 파싱, ON CONFLICT로 PostgreSQL에 기록하여 멱등성 보장
  • Backfiller: PDS에서 전체 리포지토리 CAR 파일을 가져와 백필 큐에 레코드 압축 해제
  • Label indexer: 레이블러 WebSocket 스트림 구독, 레이블 생성/부정 이벤트 처리

rsky 저장소에 포함된 추가 CLI 도구:

  • queue_backfill -- CSV, PDS 검색 또는 직접 DID 목록에서 백필할 DID 큐에 추가
  • direct_index -- 큐를 우회하여 특정 리포지토리 가져오기 및 인덱싱 (개별 계정 수정에 유용)
  • label_sync -- 커서 0에서 레이블 스트림 재생하여 누락된 부정 처리 따라잡기
  • plc_import -- PLC 디렉터리에서 핸들/DID 매핑 대량 가져오기
  • palomar-sync -- 팔로워 수 및 PageRank를 OpenSearch에 동기화

rsky-video

PDS가 Bluesky의 video.bsky.app를 지원하지 않는 사용자를 위한 동영상 업로드 서비스입니다. 자체 DID(did:web:video.blacksky.community)를 사용하여 서비스 인증 JWT를 통해 사용자 PDS에 인증합니다. 흐름:

  1. 클라이언트가 PDS에서 서비스 인증 토큰을 가져옴 (대상: 동영상 서비스 DID)
  2. 클라이언트가 동영상 바이트를 rsky-video에 업로드
  3. rsky-video가 CID를 생성하고 blob을 사용자의 PDS에 업로드
  4. 동영상이 Bunny Stream CDN으로 전달되어 트랜스코딩
  5. 완료 시 클라이언트가 blob을 참조하는 게시물 생성 -- PDS가 blob 존재를 확인

레이블 처리

조정 레이블은 WebSocket 구독을 통해 레이블러 서비스(예: Bluesky의 Ozone)에서 제공됩니다. Wintermute의 ingester는 전용 label_live 큐(낮은 볼륨, 기본 firehose와 분리)에서 레이블을 처리합니다. label_sync 도구는 레이블러의 전체 스트림을 재생하여 레이블을 다시 삽입하지 않고도 누락된 부정(레이블 제거)을 따라잡을 수 있습니다.

설정

전제 조건

  • Node.js 18+ 및 pnpm (데이터플레인 및 appview 빌드 용)
  • PostgreSQL 17 (bsky 스키마)
  • Redis (선택 사항, 캐싱 용 -- 위의 알려진 문제 참조)
  • firehose를 소비하고 데이터베이스를 채우는 rsky-wintermute
  • OpenSearch (Palomar 검색 실행 시)

데이터베이스

bsky 스키마는 데이터플레인의 마이그레이션에 의해 생성됩니다. 처음 실행 시 데이터플레인이 모든 마이그레이션을 자동으로 적용합니다. Blacksky 전용 마이그레이션은 20260202T120000000Z-add-community-post.ts(커뮤니티 게시물 테이블)뿐입니다. 커뮤니티 게시물이 필요하지 않으면 제거할 수 있습니다.

rsky-wintermute는 동일한 스키마에 씁니다. 모든 INSERT 문은 ON CONFLICT를 사용하므로 wintermute와 데이터플레인 마이그레이션을 어떤 순서로 실행해도 안전합니다.

빌드

root@kitploit:~
pnpm install
pnpm build

데이터플레인 실행

root@kitploit:~
node services/bsky/dataplane.js

AppView 실행

root@kitploit:~
node services/bsky/api.js

대규모 운영

백필 타임라인

전체 네트워크 백필(약 4,200만 사용자, 약 185억 레코드)은 wintermute의 병렬 처리에도 불구하고 몇 주가 걸립니다. 예상:

  • 라이브 인덱싱: 첫날부터 실시간으로 유지(초당 약 1,000개 이벤트)
  • 전체 백필: PDS 응답성 및 네트워크 상태에 따라 초당 10,000개 레코드로 2-4주
  • 부분 백필: 사용자 하위 집합(예: 커뮤니티 회원만)의 경우 몇 시간에서 며칠

백필 중에 AppView는 기능하지만 아직 백필되지 않은 사용자에 대해 불완전한 데이터를 표시합니다. 라이브 이벤트는 백필 진행 상황에 관계없이 즉시 인덱싱됩니다.

이 과정에서 해결한 문제

전체 네트워크 AppView를 부트스트래핑할 때 겪었던 문제들입니다. 동일한 작업을 수행하는 경우 이러한 문제 중 일부를 겪을 가능성이 높습니다.

COPY 텍스트 형식 JSON 손상: PostgreSQL의 COPY 텍스트 프로토콜은 백슬래시를 이스케이프 문자로 취급합니다. 대량 로더가 JSON 문자열에서 백슬래시를 이스케이프하지 않으면 \"가 "가 되어 조용히 레코드가 손상됩니다. record.json 열은 text 유형(jsonb 아님)이므로 PostgreSQL이 이를 포착하지 않습니다. 약 66,000개의 손상된 레코드를 찾았으며 공개 API에서 다시 가져와 복구해야 했습니다.

JSON의 null 바이트: 일부 AT 프로토콜 레코드에는 \u0000(null 바이트)가 포함되어 있으며 이는 RFC 8259에 유효하지만 Node.js JSON.parse()에서 거부됩니다. 데이터플레인은 이러한 레코드에 대해 조용히 null을 반환합니다. 데이터베이스에 쓰기 전에 null 바이트를 제거하십시오.

타임스탬프 형식 민감성: 데이터플레인은 밀리초 정밀도와 Z 접미사(2026-01-12T19:45:23.307Z)가 있는 타임스탬프를 예상합니다. 나노초 정밀도나 시간대 오프셋 형식(+00:00)은 미묘한 정렬 및 비교 문제를 일으킵니다.

알림 테이블 비대화: (did, recordUri, reason)에 고유 제약 조건이 없으면 알림 테이블이 중복으로 무한히 커집니다. 우리의 경우 이를 발견하기 전에 13억 행(663GB)에 도달했습니다. INSERT에 ON CONFLICT DO NOTHING을 추가하는 것은 고유 인덱스가 먼저 존재하는 경우에만 도움이 되며, 인덱스를 생성하려면 기존 데이터의 중복 제거가 필요합니다.

게시물 임베드 테이블: post_embed_image 및 post_embed_video 테이블은 인덱서가 처리하지 않으면 기본적으로 채워지지 않습니다. 이것이 없으면 getAuthorFeed의 미디어 필터가 아무것도 반환하지 않습니다. 이들은 별도로 백필해야 합니다.

레이블 부정 순서: 레이블 부정(제거) 이벤트는 소스, URI 및 값으로 원래 레이블을 참조합니다. 부정이 원래 레이블보다 먼저 도착하면(백필 중 일반적) 조용히 삭제됩니다. label_sync 도구는 전체 스트림을 재생하여 이를 따라잡습니다.

Fjall 큐 중독: 임베디드 데이터베이스인 Fjall(wintermute의 큐에서 사용)은 충돌 후 "중독" 상태가 되어 모든 큐 작업을 차단할 수 있습니다. 해결 방법은 큐 데이터베이스 디렉터리를 삭제하고 다시 시작하는 것입니다. wintermute는 릴레이의 커서에서 따라잡습니다(릴레이는 약 72시간의 기록 유지).

TLS 공급자 초기화: Rust의 rustls는 모든 TLS 연결 전에 암호화 공급자를 명시적으로 설치해야 합니다. 시작 시 rustls::crypto::aws_lc_rs::default_provider().install_default()가 없으면 firehose에 대한 첫 번째 WebSocket 연결이 패닉을 일으킵니다.

계정 마이그레이션 후 서명 키 회전: 사용자가 PDS 간에 마이그레이션하면 서명 키가 변경됩니다. 데이터플레인은 ID 데이터를 staleTTL 1시간으로 캐시합니다. 이 기간 동안 마이그레이션된 사용자에 대한 JWT 검증이 실패합니다. 해결 방법은 검증 재시도 시 캐시를 우회하고 PLC 디렉터리에서 직접 확인하는 것입니다.

리소스 요구 사항

전체 네트워크 AppView(약 4,200만 사용자, 약 185억 레코드) 실행 기준.

스토리지 세부 내역 (대략, 전체 네트워크):

테이블 그룹

더 작은 커뮤니티가 부분 AppView(커뮤니티 회원만 인덱싱)를 실행하는 경우 요구 사항은 인덱싱된 계정 수에 대략 선형적으로 확장됩니다.

업스트림과 동기화

root@kitploit:~
git remote add upstream https://github.com/bluesky-social/atproto.git
git fetch upstream
git merge upstream/main

충돌은 일반적으로 packages/bsky/src/data-plane/server/routes/ 및 packages/bsky/src/api/에서 발생합니다. 업스트림 변경 사항과 함께 당사의 추가 사항을 유지하여 해결하십시오.

라이선스

업스트림과 동일: MIT 및 Apache 2.0 이중 라이선스. LICENSE-MIT.txt 및 LICENSE-APACHE.txt를 참조하십시오.

도구 다운로드
구성 요소소스목적
rsky-wintermuteblacksky-algorithms/rskyRust firehose 인덱서: 이벤트 소비, 리포지토리 백필, 레코드를 PostgreSQL에 인덱싱
rsky-relayblacksky-algorithms/rsky레이블러 서비스의 조정 레이블을 수신하기 위한 AT 프로토콜 릴레이
rsky-videoblacksky-algorithms/rsky동영상 업로드 서비스: Bunny Stream CDN을 통해 트랜스코딩, blob 참조를 사용자 PDS에 업로드
bsky-dataplane이 저장소 (services/bsky)PostgreSQL 위의 gRPC 데이터 계층
bsky-appview이 저장소 (services/bsky)app.bsky.* XRPC 엔드포인트용 HTTP API 서버
Palomarblacksky-algorithms/indigo전체 텍스트 검색: 팔로워 수 부스팅으로 프로필 및 게시물을 OpenSearch에 인덱싱
palomar-syncblacksky-algorithms/rsky팔로워 수 및 PageRank 점수를 PostgreSQL에서 OpenSearch로 동기화
변수필수설명
DB_PRIMARY_URL예?options=-csearch_path%3Dbsky가 포함된 PostgreSQL 연결 문자열
DB_REPLICA_URL아니오읽기 복제본 연결 문자열
BSKY_DATAPLANE_PORT아니오gRPC 포트 (기본값 2585)
BSKY_REDIS_HOST아니오Redis 호스트:포트 (캐싱 용, 현재는 비활성화 상태로 두는 것이 좋음)
BLACKSKY_MEMBERSHIP_DB_URL아니오커뮤니티 멤버십용 별도 DB (Blacksky 전용)
변수필수설명
BSKY_APPVIEW_PORT아니오HTTP 포트 (기본값 2584)
BSKY_DATAPLANE_URLS예쉼표로 구분된 데이터플레인 gRPC URL
BSKY_DID예AppView의 DID (예: did:web:api.example.com)
BSKY_MOD_SERVICE_DID예Ozone 조정 서비스 DID
BSKY_ADMIN_PASSWORDS예기본 인증용 쉼표로 구분된 관리자 비밀번호
리소스최소권장
CPU16 코어48+ 코어
RAM64 GB256 GB
스토리지10 TB NVMe28+ TB NVMe (RAID)
PostgreSQL전용, 동일 머신 또는 저지연동일 머신 권장
네트워크지속 100 Mbps1 Gbps+
크기
게시물 + 레코드~3.5 TB
좋아요~2 TB
팔로우~500 GB
알림~600 GB
인덱스~4 TB
OpenSearch (Palomar)~500 GB