Squalk: an old-school forum engine built on Nostr (NIP-29 groups, NIP-7D threads)
Squalk: Nostr로 만든 복고풍 포럼 엔진 — NIP-29 그룹과 NIP-7D 스레드
Squalk는 Nostr의 NIP-29 그룹과 NIP-7D 스레드를 사용하는 포럼 엔진입니다. 단일 포럼부터 다중 포럼까지 지원하고, 릴레이·Blossom 서버·환경 변수만으로 정적 사이트나 SSR 서비스로 배포합니다.
- 주제
AI 요약
Squalk는 Nostr 위에 구축한 오픈소스 포럼 엔진입니다. 단순한 커뮤니티는 simple 모드로 하나의 포럼만 운영하고, 여러 방을 관리하려면 full 모드를 선택합니다. 각 포럼 오른쪽 사이드바에는 채팅 영역이 붙어 있어 구성원과 빠르게 대화할 수 있습니다. 그룹과 스레드에는 각각 NIP-29와 NIP-7D를 사용합니다.
Nostr 릴레이와 파일 저장소
포럼 그룹을 호스팅할 개인 릴레이가 필요합니다. 릴레이는 NIP-29를 지원해야 합니다. 미디어 업로드에는 Blossom 서버를 사용합니다. 두 요소를 함께 제공하는 Pyramid가 권장 배포 구성으로 제시됩니다. Squalk는 PUBLIC_RELAY_URL로 그룹이 있는 WebSocket 릴레이를 지정하고, PUBLIC_BLOSSOM_URL을 설정했을 때 업로드를 활성화합니다.
설정은 전부 환경 변수로 처리합니다. 브라우저에서 읽는 값이므로 변수 이름은 모두 PUBLIC_으로 시작합니다. .env.example을 .env으로 복사한 뒤 값을 채우고, 개발 환경에서는 npm run dev, 빌드에서는 npm run build를 실행합니다. PUBLIC_MODE는 기본값이 simple이며, 단일 포럼의 그룹 ID는 PUBLIC_GROUP_ID에 지정합니다. 실행 중에도 관리자가 simple에서 full로 전환할 수 있습니다. 검색창은 PUBLIC_SEARCH=yes로 켜지만 NIP-50 검색을 지원하는 릴레이가 필요합니다. 라벨 목록, 초대 코드 입력창, 강조 색상도 환경 변수로 정합니다.
정적 배포와 SSR
기본 배포 방식은 정적 사이트입니다. PUBLIC_SSR=no 상태에서 npm run build를 실행하면 build/에 단일 페이지 번들이 생성됩니다. 웹 서버는 알 수 없는 경로를 index.html로 연결해야 하며, 데이터는 브라우저가 릴레이에서 가져옵니다.
PUBLIC_SSR=yes로 전환하면 Node 애플리케이션을 빌드합니다. 서버는 공개 릴레이를 읽어 스레드, 방, 리소스, 연락처 페이지를 HTML로 렌더링합니다. 설명과 Open Graph 태그, JSON-LD, robots.txt, sitemap.xml, 실제 404 응답도 함께 제공합니다. 페이지를 처음 보여준 뒤에는 브라우저가 정적 배포와 같은 방식으로 이어받습니다. 서버는 익명으로 릴레이를 조회하므로 공개 콘텐츠만 렌더링하며, 비공개 방은 클라이언트가 실행된 뒤 구성원에게 표시됩니다.
SSR에서는 클라이언트 내비게이션마다 서버가 페이지를 미리 가져와 캐시하도록 설정할 수 있습니다. 기본 캐시 신선도는 300초이고, 이후 최대 21,600초 동안 오래된 페이지를 즉시 반환하면서 백그라운드에서 갱신합니다. Cloudflare의 s-maxage와 stale-while-revalidate에도 같은 값을 적용합니다. SSR 배포에는 Node 22 이상, 리버스 프록시, ORIGIN을 설정한 systemd 유닛이 필요합니다. just deploy-ssr 명령은 Node 빌드와 패키지 파일을 서버로 보내고 의존성을 설치한 뒤 서비스를 재시작합니다.
Nostr 이벤트를 이용한 사이트 콘텐츠
사이드바 링크와 홈페이지, 연락처 페이지는 같은 릴레이에 게시된 NIP-23 장문 이벤트(kind 30023)에서 채웁니다. Squalk는 그룹의 NIP-29 39001 관리자 이벤트에 등록된 공개 키가 작성한 이벤트만 노출합니다. 릴레이 조회 자체는 열려 있지만 관리자 공개 키 목록을 신뢰 경계로 삼습니다.
리소스는 kind: 30023 이벤트에 t: squalk-resource 태그와 d 태그를 붙여 등록합니다. d 값은 /resource/<slug> 경로가 됩니다. title은 사이드바 라벨이고 position은 정렬 순서입니다. 위치를 지정한 항목을 먼저 오름차순으로 배치하며, 나머지는 제목 기준으로 정렬합니다. 같은 d 값을 다시 게시하면 최신 이벤트가 기존 리소스를 갱신합니다.
사용자 정의 마크다운을 넣는 partial은 home과 contacts 두 슬롯만 지원합니다. t: squalk-partial과 해당 d 값을 가진 관리자 이벤트를 게시하면 됩니다. 슬롯마다 가장 최신 관리자 이벤트를 사용하며, 홈페이지 partial은 방 목록과 토론 피드 위에 표시됩니다. 첫 줄에 이미지 URL만 넣으면 배너 이미지로 렌더링합니다. 저장소 루트의 about.md, guidelines.md, homepage.md, contacts.md 파일을 시작 템플릿으로 활용할 수 있습니다.
Lobsters 반응
- @tonyarkles — 저는 완전히 다른 무언가를 작업하고 있지만 같은 이론을 따릅니다. HTTPS는 읽기 전용 보기를 제공하고, Nostr와 키는 신원 확인, 인증, 게시에 사용합니다. Nostr를 통해 모든 내용을 볼 수도 있습니다. 개발자 친화적이고 주류에서 벗어난 모바일 앱으로 어떤 것이 있는지 추천해 주실 수 있나요? 덧붙이면, 여기서 Squalk도 살펴봤고 아이디어가 마음에 듭니다. 개방형 클라이언트와 색다른 신원·인증 방식으로 이 생태계가 어떻게 발전할지 정말 궁금합니다.
- @conartist6 — 이름이 정말 멋집니다.
원문: GitHub / 번역·요약: Trawling