개발자 문서
한국어

서버 운영

관리와 설정

관리 화면, 가입 코드 전달(SMS·이메일), 서버 설정 값, 백업과 보안 — 운영자를 위한 참조.

서버에는 브라우저로 여는 관리 화면이 들어 있습니다. 서버 상태를 보고, 가입·로그인 코드를 어떻게 전달할지 정하고, 사용자와 봇을 관리합니다. 이 문서는 관리 화면과 서버 설정 값을 한곳에 정리한 참조입니다. 설치가 먼저라면 서버 설치를 보세요.

관리 화면 열기

관리 화면은 관리 HTTP 포트(운영 구성에서 8080)의 /admin/ 이고, 서버 자신(127.0.0.1)에서만 열립니다. 운영자는 SSH 터널로 들어갑니다.

# 내 컴퓨터에서 (켜 둔 채로)
ssh -N -L 8080:127.0.0.1:8080 <사용자>@<서버>
# 브라우저: http://localhost:8080/admin/

# 토큰 (서버에서)
sudo cat /srv/pabal/pabal_server/data/admin-token

토큰을 넣고 열기. 브라우저가 토큰을 기억하며, 오른쪽 위 토큰 지우기로 지웁니다. 토큰을 직접 정하려면 TELEGRAM_ADMIN_TOKEN(16자 이상)을 주세요 — 그러면 파일을 쓰지 않습니다.

탭별로 보이는 것

탭내용갱신
대시보드지금 접속 중인 사람·세션·연결, 사람·봇·그룹·메시지·사진·파일 수, 가동 시간·포트·DB, JVM, 상태 점검. 테스트 번호가 켜져 있으면 경고 띠5초
가입 코드기다리는 코드(번호·전달 방식·발송 상태·코드·남은 시간·틀린 입력 횟수)와 최근 기록. 코드 복사·취소3초
사용자모든 계정 — 전화번호, 로그인 이메일(편집), 접속 여부, 로그인 기기 수, 메시지 수. 모든 기기 로그아웃, 번호 차단, 계정 삭제(본인 확인 메모와 함께 — 지운 계정은 id 와 메모만 남음)10초
봇BotFather 로 만든 봇 — 만든 사람, 명령어 메뉴, 연결 방식(MTProto · HTTP 폴링 · 웹훅 주소와 실패 이유), 기다리는 업데이트 수10초
음성음성 봇 — 게이트웨이·중계·방침의 상태, 차단 스위치, 공개, 대화 기록 허용, 진행 중인 통화와 끊기, 봇 허용·멈추기, 시험자, 한도(아래)5초
신고앱의 "신고"로 들어온 것 — 신고 당시 내용의 사본(보낸 사람이 지운 뒤에도 읽힘). 삭제(대화의 양쪽, 그룹이면 모든 멤버에게서), 계정 정지(모든 기기 로그아웃, 다시 로그인·가입 못 함), 정지된 계정 목록. 정지된 채 탈퇴한 계정의 번호로는 탈퇴 뒤 1년(기본) 새로 가입하지 못함 — 정지할 때 고르거나 목록의 재가입 막기에서 더 늦은 끝 날짜·무기한으로 늘림(1년과 고른 날짜 가운데 늦은 쪽까지 — 줄이지는 못함. 관리 API: POST /admin/api/reports/<id>/suspend {"userId": …, "blockSignupUntil": "2028-01-01"} 또는 "blockSignupForever": true, 뒤에 바꾸기는 POST /admin/api/suspensions/<id>/block-signup, 빈 본문은 기본으로). 처리 끝난 신고도 볼 수 있음. 처리 끝난 신고는 처리 끝난 날부터 3년이 지나면 사본까지 지움(카드에 그 날짜) — 분쟁·수사·소송이 시작되면 보존으로 풀 때까지 남김(관리 API: POST /admin/api/reports/<id>/hold, 풀기는 /release)10초
고객센터@PabalSupport 로 온 대화. 대화를 골라 답장하면 고객센터 봇의 이름으로 그 대화에 갑니다10초
설정가입·로그인 규칙, 앱 심사 계정, SMS, 이메일(SMTP), 전화번호 차단, 웹사이트에 실리는 운영자 정보직접 저장
예약 아이디아무도 가져갈 수 없게 막아 둔 사용자명 — 검색, 추가, 목록 내려받기(.txt), 서버가 기본으로 막는 아이디. 이미 누가 쓰는 이름은 예약해도 뺏지 않음열 때 한 번
저장소데이터 디렉터리·DB 주소(비밀번호는 가림), 사진·파일 개수와 용량, 종류별 저장 스트림 수10초

위험한 동작(로그아웃·차단·코드 취소)은 두 번 눌러야 실행됩니다.

가입·로그인 코드

사람이 앱에 전화번호를 넣으면 서버가 코드를 만들어 설정 탭에서 고른 방법으로 보냅니다.

전달 방식코드가 가는 곳앱 화면
관리 화면가입 코드 탭. 운영자가 복사해 직접 알려 줌번호 → "코드를 보냈습니다" → 코드 (새 번호면 이름)
SMSTwilio · Solapi · 웹훅 중 하나로 문자관리 화면과 같음
이메일SMTP 로 메일번호 → 이메일 입력 → "이메일로 코드를 보냈습니다" → 코드
  • 이메일 방식에서 새 가입은 아무 주소나 쓸 수 있고, 그 주소가 계정의 로그인 이메일이 됩니다. 기존 계정은 등록된 로그인 이메일로만 받습니다 — 남의 번호에 자기 주소를 넣어 들어오는 것을 막습니다. 로그인 이메일이 없는 기존 계정은 SMS(설정돼 있으면) 또는 관리 화면으로 대신 보냅니다. 사용자 탭에서 로그인 이메일을 넣어 줄 수 있습니다.
  • 발송은 뒤에서 이루어져 앱은 바로 코드 화면으로 갑니다. 발송 결과(성공·실패와 이유)는 가입 코드 탭에 뜹니다.
  • 가입(이름 입력)은 코드를 맞힌 뒤에만 됩니다. 틀린 코드는 허용 횟수만큼만 받고 그 뒤로는 맞는 코드도 거절합니다.
  • 번호 바꾸기(앱의 설정 → 전화번호)는 기본으로 닫혀 있어 앱에 오류가 뜹니다. 번호 바꾸기 허용을 켜면 코드는 전달 방식과 상관없이 새 번호로 SMS 로만 가고(가입 코드 탭에는 코드 표시를 켜도 보이지 않습니다 — 거기서 건네받는 사람이 그 번호의 주인이라는 보장이 없습니다), 문구는 SMS 설정의 번호 바꾸기 문구(기본 [파발] 번호 바꾸기 확인 코드: {code})입니다. 재요청 간격·하루 최대는 새 번호와 계정 모두에 걸리고, 차단·정지된 번호와 다른 계정이 쓰는 번호는 코드를 받을 때와 옮길 때 두 번 거절합니다. 코드는 그 계정·그 번호에만 맞고 한 번만 쓰이며, 그 번호를 기다리는 남의 로그인 코드는 그대로 둡니다. 옮긴 뒤 옛 번호로는 그 계정에 로그인할 수 없습니다.

설정 — 가입·로그인

항목뜻기본
새 가입 허용끄면 이미 있는 계정만 로그인. 새 번호는 "잘못된 번호"로 거절켬
코드 전달 방식관리 화면 / SMS / 이메일관리 화면
관리 화면에 코드 표시SMS·이메일 방식에서도 가입 코드 탭에 코드를 보여 줌 (발송 실패 대비)켬
테스트 번호 (+99966…)서버 설정대로 / 켜기 / 끄기. 운영에서는 끄기서버 설정대로
코드 자릿수 · 유효 시간5~6자리 · 1~60분5자리 · 5분
재요청 간격 · 하루 최대같은 번호가 코드를 다시 받기까지 초(0~3600) · 24시간 안 최대 횟수(1~1000)60초 · 10회
틀린 입력 허용 횟수넘으면 그 코드는 잠김 (1~20)5회
번호 바꾸기 허용로그인한 계정이 앱의 "번호 바꾸기"로 다른 번호로 옮김. SMS 가 설정돼 있어야 켤 수 있음끔

설정 — SMS

제공자넣을 값비고
웹훅받을 주소(https://…), Authorization 헤더(선택)서버가 POST {"phone":"+8210…","code":"12345","text":"…"} 를 보냅니다. 2xx 면 성공. 자체 문자 서버나 다른 서비스에 연결할 때
TwilioAccount SID, Auth Token, 발신 번호 또는 Messaging Service SID해외 번호 포함 전 세계
Solapi (구 CoolSMS)API Key, API Secret, 발신 번호국내 문자. 발신 번호는 Solapi 에 사전 등록된 번호. +82 번호는 010… 형식으로 보냅니다

문구에는 {code}(코드)와 {minutes}(유효 시간)를 넣을 수 있습니다. 기본값: [파발] 인증 코드: {code}. 저장한 뒤 테스트 발송으로 확인하세요.

설정 — 이메일 (SMTP)

서비스서버 · 포트 · 보안사용자명 · 비밀번호
Gmailsmtp.gmail.com · 587 · STARTTLSGmail 주소 · 앱 비밀번호 (Google 계정 → 보안 → 2단계 인증 → 앱 비밀번호)
네이버smtp.naver.com · 587 · STARTTLS아이디 · 비밀번호 (메일 환경설정에서 POP3/SMTP 사용 켜기)
사내 메일 릴레이릴레이 주소 · 25 · 없음비움

보내는 주소는 SMTP 계정이 보낼 수 있는 주소여야 합니다. 제목·본문에도 {code}, {minutes} 를 씁니다.

사용자 관리

  • 모든 기기 로그아웃: 그 계정의 모든 로그인(인증 키)을 끊습니다. 기기를 잃어버린 사용자에게 씁니다.
  • 번호 차단: 그 번호는 코드를 받을 수 없고, 그 번호의 계정은 모든 기기에서 즉시 로그아웃됩니다. 설정 탭의 전화번호 차단 목록과 같습니다.
  • 로그인 이메일: 이메일 방식에서 기존 계정이 이메일로 로그인하려면 필요합니다.
  • 계정 삭제: 계정 삭제를 요청한 사용자에게, 본인임을 확인한 뒤 씁니다. 앱의 계정 삭제와 똑같이 지우며 되돌릴 수 없습니다. 계정 id 나 전화번호를 다시 입력하고, 요청이 어디로 왔고 본인을 어떻게 확인했는지 메모를 남겨야 실행됩니다. 메모는 id·날짜와 함께 남습니다(전화번호·메일 주소와 그 계정의 이름·사용자명은 지웁니다). 등록된 로그인 이메일이 아닌 곳에서 온 이메일 요청은, 같은 창의 확인 코드 문자 보내기로 그 계정의 번호에 코드를 보내고, 요청한 사람이 메일로 돌려준 코드를 넣어 확인합니다. 봇과 서버 자체 계정은 여기서 지울 수 없습니다.
  • @BotFather 는 서버 안의 봇이라 로그아웃 대상이 아닙니다.

봇 관리

봇 탭에서 각 봇의 연결 방식을 봅니다 — MTProto 로 붙어 있는지, HTTP 로 최근 1분 안에 getUpdates 를 불렀는지, 웹훅 주소가 무엇이고 지금 실패 중인지(이유 포함). 기다리는 업데이트가 계속 늘면 봇 프로그램이 멈췄거나 웹훅이 실패하는 중입니다. 봇 삭제·토큰 재발급은 봇을 만든 사람이 @BotFather 에서 합니다.

음성 탭

음성 봇(사람이 전화를 걸면 AI 음성 모델이 받는 봇, 음성 봇)을 운영하는 곳입니다. 서버에 음성이 켜져 있지 않아도 탭은 보이고, 상태 칸이 무엇이 빠졌는지 알려 줍니다. 켜는 법은 서버 설치 — 음성 봇. 5초마다 새로 고칩니다.

칸보이는 것과 할 수 있는 것
맨 위 숫자진행 중인 통화 · 오늘 쓴 시간(서버 하루 한도, 통화 중 잡아 둔 시간) · 연결된 게이트웨이 · 음성을 켠 봇(허용한 봇) · 시험자(공개 여부)
상태게이트웨이 링크(주소, 또는 "꺼짐 (링크 비밀 없음)"), 통화 중계(TURN, 없으면 "모든 봇 통화가 통화 중"), 개인정보처리방침의 bot-calls 절이 있는지(없으면 "어떤 봇도 받지 않음"), 기다리는 동의, 그리고 통화 중 신호로 끝낸 전화의 이유별 횟수(예: 워치가 못 거는 모드 — 애플 워치로 도구·MCP·자기 키·원음 모드·주인 대화 기록을 쓰는 봇에 건 경우, WATCH_NOT_YET)
차단 스위치모든 음성 통화 멈추기: 누르는 순간 이 서버에서 켜져 진행 중인 봇 통화가 모두 끝나고, 새 통화를 받지 않으며, 어떤 봇에도 통화 버튼이 보이지 않습니다. 그다음 저장되어 서버를 다시 켜도 그대로입니다(저장이 실패하면 경고). 음성 통화 다시 열기로 풉니다
공개공개 전에는 아래 시험자만 음성 봇에 전화할 수 있습니다. 모두에게 열려면 칸에 공개합니다 를 그대로 입력하고 모두에게 공개(서버의 받는 곳 키 TELEGRAM_VOICE_RECIPIENTS 가 처리 지역을 정하지 않은 …@unset 이면 거부 — 먼저 지역을 넣은 앱 빌드를 내야 합니다). 시험자만으로 되돌리기로 되돌립니다. 공개하기 전에 법률 검토와 제공자(Google)의 서면 답변을 마치는 것은 운영자가 판단할 일입니다 — 코드가 막지 않습니다
게이트웨이연결된 게이트웨이마다 id, 맡은 통화/용량, 소리를 보내는 곳(google.vertex.gemini-live@us-central1 등), 기능(tools·byok·transcripts·stream …), 엔진, boot
대화 기록 · 자기 키대화 기록 허용: 칸에 대화 기록을 허용합니다 를 입력하고 대화 기록 허용. 법률 검토 전에는 허용하지 마세요. 끄면 곧바로 꺼지고, 진행 중 통화와 봇들이 아직 가져가지 않은 대화 글도 버립니다. 발신자가 고르는 대화 남기기는 이 허용과 상관없고, 관리 화면에 스위치가 없습니다 — 개인정보처리방침의 bot-calls-keep-text 절이 켭니다(서버 설치 — 대화 남기기). 자기 키: 받는지와 받지 않는 이유, 마스터 키 id, 키가 있는 봇 수(키 자체는 어디에도 나오지 않음)
진행 중인 통화통화 id, 봇, 발신자(이름·id), 상태, 경과/최대, 게이트웨이, 열린 도구 질의 수. 끊기(두 번 눌러야 실행)
음성 봇음성을 쓸 수 있는 봇: 허용한 봇만(기본) 또는 @BotFather 로 만든 모든 봇. 봇 id 나 @사용자명으로 허용. 표: 봇, 만든 사람, 상태(켜짐·꺼짐·받을 수 있는지와 이유, 멈춤, 원음 모드면 소리가 가는 주소, 도구와 답·오류·시간 초과 횟수), 목소리·언어, 한도, 오늘 통화. 할 수 있는 것: 허용·허용 취소, 확인하고 다시 허용, 멈추기(사유 필수, 200자, 봇 주인에게 보임, 진행 중 통화가 끝남)·다시 열기, 설정 보기
봇의 설정 보기지시문·인사말 원문(검토용), 도구 정의와 도구별 응답 시간(p50·p95), 도구 질의가 가는 곳, 규칙 동의 판, 사칭 검사 적용·면제, 최근 바뀐 것, 달마다의 통화·시간·받지 못함·거절
시험자사람의 id 나 @사용자명(@ 는 있어도 없어도, 대소문자 상관없음)으로 시험자로 넣기·빼기(봇과 서버 자체 계정은 안 됨). 서버 설정 TELEGRAM_VOICE_TESTERS 로 넣은 사람은 "서버" 로 표시되고 여기서 뺄 수 없습니다
한도아래 표 + 사칭 검사에 더할 이름(한 줄에 하나). 저장하면 이제 통화를 받지 못하게 된 봇을 알려 줍니다
봇이 거는 전화전역 스위치: 칸에 봇이 거는 전화를 켭니다 를 그대로 입력하고 켭니다 — 법률 검토 뒤에만(허락 창 글의 callme.legal_review 가 done 이 아니면 거절. 개발·시험 서버의 예외는 TELEGRAM_VOICE_CALLME_UNREVIEWED). 끄면 곧바로 새 요청이 503. 나라(기본 82, 999 는 시험 서버만), 한도(운영 서버는 바닥보다 느슨하게 저장되지 않음), 주인이 켠 봇의 표(검토 상태, 토큰 멈춤, 자동 정지, 오늘 요청), 봇 상세(이름·@사용자명·사진·지시문·인사말·도구·목소리·언어·모드·목적·주인 이름과 그 outboundReviewHash)에서 허락·허락 거둠, 자동 정지 풀기, 판정 단계별 거절 횟수

허용은 검토입니다. "허용한 봇만" 모드에서 봇을 허용하면 지금의 지시문·인사말·도구(원음 모드면 원음 주소)를 허용한 것이 됩니다. 봇 주인이 그중 하나라도 바꾸면 봇 표에 "지시문·인사말이 바뀜 — 확인하고 다시 허용해야 전화를 받습니다" 가 뜨고, 확인하고 다시 허용을 누를 때까지 그 봇은 전화를 받지 않습니다. 봇 언어가 한국어·영어가 아닌 봇은 "@BotFather 로 만든 모든 봇" 모드에서도 같습니다 — 저장할 때의 자동 규칙 검사가 한국어와 영어만 읽기 때문입니다. 그런 봇의 표에는 "한국어·영어가 아닌 봇 (자동 규칙 검사가 읽지 못함): 지시문·인사말을 읽어 주세요 — 확인하고 다시 허용해야 전화를 받습니다" 가 뜨니, 그 언어를 읽을 수 있는 사람이 지시문·인사말을 읽고 허용하세요.

한도기본범위
한 통화 최대 (초)60060~3600, 서버 설정 TELEGRAM_VOICE_MAX_SECONDS 까지
봇 하나의 동시 통화21~100, 서버 설정 TELEGRAM_VOICE_BOT_CONCURRENCY 까지
한 사람의 시간당 통화 수201~200
한 사람의 하루 통화 (분)601~600
봇 하나의 하루 통화 (분)601~6000
봇 주인 한 명의 하루 통화 (분)1201~6000
서버 전체의 하루 통화 (분)3001~100000
가장 짧게 받을 통화 (초)6030~300 — 남은 시간이 이보다 적으면 받지 않음
새 계정 제한 (일)70~90 (0 = 끔) — 시험자, 그리고 봇이 하루 이상 전에 메시지를 보낸 사람은 예외
  • 하루는 서버의 시간대(TELEGRAM_VOICE_TIME_ZONE, 기본 Asia/Seoul) 기준입니다. 통화를 받아들일 때 그날 남은 시간 안에서 최대 길이를 정해 모든 한도에 잡아 두고, 끝나면 서버 시계로 실제 초만 청구합니다. 봇 주인의 자기 키로 받는 통화는 발신자의 한도만 씁니다.
  • 봇 주인은 자기 봇의 한 통화 길이(기본 300초)와 동시 통화를 이 한도 안에서만 정할 수 있습니다.
  • 모든 조치(차단 스위치, 공개, 허용, 멈추기, 끊기, 한도 …)는 가입 코드 탭의 활동 기록에 음성: … 으로 남습니다.
  • 음성 통화의 품질 수치(첫 소리까지 걸린 시간, 끼어들기 때 멈추기까지, 도구의 답 시간 등)는 이 탭에 없고 관리 포트의 /metrics 에만 있습니다(JSON 에서는 voice., Prometheus 형식 /metrics?format=prometheus 에서는 voice_ 로 시작하는 이름). 사람·봇·말은 없고 합계만입니다.

같은 일을 관리 API 로도 할 수 있습니다(모두 Authorization: Bearer <토큰>): GET /admin/api/voice, GET /admin/api/voice/bots/<id>, POST 로 voice/kill {"on": true}, voice/launch {"launched": true, "confirm": "…"}, voice/transcripts {"allowed": true, "confirm": "…"}, voice/testers·voice/testers/remove {"userId": …}, voice/gate {"botGate": "ALLOWLIST"}, voice/bots/allow·voice/bots/disallow {"botId": …}, voice/bots/<id>/suspend {"reason": "…"}·…/lift·…/name-exempt, voice/calls/<id>/end, 한도는 /admin/api/settings/voice. 봇이 거는 전화는 voice/outbound {"on": true, "confirm": "…"}, voice/outbound/settings {"countries": […], "limits": {…}}, GET /admin/api/voice/outbound/bots/<id>, voice/outbound/bots/allow {"botId": …, "expect": "<outboundReviewHash>"}·voice/outbound/bots/disallow, voice/outbound/bots/<id>/lift.

환경 변수

서버는 설정 파일(server-config.json)을 읽은 뒤 환경 변수로 덮어씁니다. 운영 컴포즈 파일이 아래 값을 정해 주므로 보통은 .env 만 고치면 됩니다.

변수뜻운영 컴포즈 값
TELEGRAM_PORTMTProto 포트MTPROTO_PORT (8443)
TELEGRAM_HOSTMTProto 가 듣는 주소0.0.0.0
TELEGRAM_PUBLIC_HOST앱에게 알려 줄 서버 주소 (help.getConfig)PUBLIC_IP
TELEGRAM_WEB_PORT웹사이트·문서·Bot API·관리 화면 포트8080
TELEGRAM_WEB_HOST그 포트가 듣는 주소. 기본 127.0.0.10.0.0.0 (컨테이너 안. 호스트에는 127.0.0.1 로만 공개)
TELEGRAM_PUBLIC_URL웹사이트 주소. 링크(me_url_prefix)·초대 링크·문서 예제·미리보기 이미지https://DOMAIN/
TELEGRAM_DATA_DIR사진·파일·admin-token·operations.json 위치/app/data
TELEGRAM_RSA_KEY서버 RSA 비밀키 경로 (없으면 처음에 만듦, 공개키는 .pub)/app/keys/private.pem
TELEGRAM_DC_ID이 서버의 DC 번호. 앱이 집으로 쓰는 DC(텔레그램 앱은 모두 2)와 같아야 합니다 — 다르면 사진·파일 내려받기가 시작되지 않습니다DC_ID (2)
TELEGRAM_DB_TYPEmemory · h2 · postgresqlpostgresql
TELEGRAM_DB_URL, TELEGRAM_DB_USERNAME, TELEGRAM_DB_PASSWORDJDBC 접속jdbc:postgresql://pabal-postgres:5432/pabal, pabal, POSTGRES_PASSWORD
TELEGRAM_DB_MAX_POOL_SIZEDB 연결 수20
TELEGRAM_ADMIN_TOKEN관리 화면 토큰 (비우면 data/admin-token 에 만듦)ADMIN_TOKEN
TELEGRAM_MAX_FILE_SIZE보낼 수 있는 가장 큰 파일 (2GB·512MB·바이트 수). 조각은 도착하는 대로 디스크에 쓰이므로 메모리가 아니라 디스크가 한도입니다MAX_FILE_SIZE (2GB)
TELEGRAM_TEST_NUMBERS+99966… 테스트 번호. 개발 전용false
TELEGRAM_WEBHOOK_ALLOW_LOCAL봇 웹훅이 http:// 와 내부 주소를 쓸 수 있게. 개발 전용(정하지 않음 = false)
TELEGRAM_FCM_CREDENTIALS안드로이드 푸시용 Firebase 서비스 계정 키 파일 경로. 비우면 푸시가 꺼집니다(서버 설치 — 10단계)FCM_CREDENTIALS (비어 있음)
TELEGRAM_FCM_PROJECT_ID키 파일의 project_id 가 아닌 Firebase 프로젝트로 보낼 때만FCM_PROJECT_ID (비어 있음)
TELEGRAM_PUSH_PREVIEWSfalse 면 푸시 알림에 메시지 내용 없이 보낸 사람만PUSH_PREVIEWS (true)
TELEGRAM_TURN_HOST, TELEGRAM_TURN_PORT, TELEGRAM_TURN_SECRET통화 중계(TURN) — 서로 다른 망의 두 앱이 만나는 곳. 비밀을 비우면 직접 연결만 됩니다(서버 설치 — 통화 중계)TURN_HOST(비우면 DOMAIN), TURN_PORT (3478), TURN_SECRET (비어 있음)
TELEGRAM_APNS_KEY, TELEGRAM_APNS_KEY_ID, TELEGRAM_APNS_TEAM_ID, TELEGRAM_APNS_TOPIC, TELEGRAM_APNS_MIN_BUILD, TELEGRAM_APNS_VOIP_MIN_BUILD아이폰 푸시(APNs) — 키 파일 경로, 키 ID(비우면 파일 이름에서), 앱에 서명한 팀, 번들 ID, 보낼 가장 낮은 빌드, CallKit 전화 화면을 쓸 첫 빌드. 키가 없으면 아이폰 푸시가 꺼집니다(서버 설치 — 11단계)APNS_* (키 없음, 비어 있음 — 파일 이름에서, 4Z8R468M2D, me.pabal.ios, 4, 비어 있음)
TELEGRAM_STATIC_MAPS_KEY, TELEGRAM_STATIC_MAPS_PER_DAY, TELEGRAM_STATIC_MAPS_PER_USER_HOUR위치 메시지의 지도 그림 — Google Maps Static API 키, 하루에 새로 받는 그림 수, 한 사람이 한 시간에 새로 받게 할 수 있는 수. 키가 없으면 지도 그림이 꺼집니다(서버 설치 — 지도 그림)STATIC_MAPS_* (키 없음, 300, 120)
JAVA_OPTSJVM 옵션 (메모리).env 의 값

환경 변수 — 음성 봇

운영 구성에서는 덧붙이는 파일 deploy/docker-compose.voice.yml 이 그중 링크·중계·시간대·한도·시험자·최소 빌드를 .env 값으로 넣고, 나머지는 기능마다 검토한 뒤에 더합니다(서버 설치 — 음성 봇). 게이트웨이(pabal-voice)의 VOICE_* 설정은 저장소의 voice/README.md 에 있습니다.

변수기본뜻
TELEGRAM_VOICE_LINK_SECRET_FILE—게이트웨이와 같은 32바이트 링크 비밀(64자리 hex). 없거나 틀리면 음성 꺼짐
TELEGRAM_VOICE_LINK_HOST, TELEGRAM_VOICE_LINK_PORT127.0.0.1, 8095링크가 듣는 곳. 루프백이 아니면 TELEGRAM_VOICE_LINK_ANY_ADDRESS=true 필요, 공인 주소는 늘 거절
TELEGRAM_VOICE_LINK_ANY_ADDRESS—"이 망은 서버와 게이트웨이만 쓴다"는 선언
TELEGRAM_VOICE_TURN_HOST, TELEGRAM_VOICE_TURN_PORT—게이트웨이에게 줄 중계 주소(서버의 LAN 주소). 루프백이면 무시
TELEGRAM_VOICE_MAX_SECONDS600이 서버가 허용하는 가장 긴 통화(60~3600)
TELEGRAM_VOICE_BOT_CONCURRENCY2봇 하나의 동시 통화 상한(운영자·봇 주인은 낮추기만)
TELEGRAM_VOICE_TIME_ZONEAsia/Seoul하루·달 한도의 기준
TELEGRAM_VOICE_TESTERS—시험자 사용자 id 목록(관리 화면 목록과 합쳐짐). * 는 시험 서버만
TELEGRAM_VOICE_RECIPIENTSgoogle.vertex.gemini-live@us-central1앱의 확인 창이 말하는 받는 곳. 게이트웨이의 것과 같아야 통화가 감
TELEGRAM_VOICE_MIN_BUILD_DESKTOP, _ANDROID, _IOS, _WATCHOS1, 62151, 5, 1확인 창이 실제로 든 첫 앱 빌드(데스크톱은 태그 pabal-bc 뒤의 숫자). 받는 곳이 바뀌면 새 빌드를 내고 올림. 데스크톱은 한국어·영어가 아닌 확인 창을 처음 승인할 때 2 로 올림
TELEGRAM_VOICE_MIN_SHEET_VERSION1받아들일 가장 낮은 확인 창 판
TELEGRAM_VOICE_TOOL_RESULT_BYTES16384도구의 답 최대 크기(1024~16384)
TELEGRAM_VOICE_KEYS_MASTER_FILE, TELEGRAM_VOICE_KEYS_PREVIOUS_MASTER_FILE—자기 키·원음 비밀을 감싸는 마스터 키, 교체 중인 옛 키. 백업 밖 700 폴더에
TELEGRAM_VOICE_OWN_KEYS_APPS_READY—앱이 자기 키 봇의 확인 창(2판)을 보여 준 뒤에만 true. 그 전에는 자기 키를 받지 않음
TELEGRAM_VOICE_OWN_KEY_MIN_SHEET_VERSION, TELEGRAM_VOICE_TRANSCRIPT_MIN_SHEET_VERSION2, 1자기 키 통화·대화 기록 안내에 필요한 확인 창 판(운영 서버는 자기 키 판을 2 밑으로 내리지 않음)
TELEGRAM_VOICE_STREAM_MIN_BUILD_DESKTOP, _ANDROID, _IOS0 (없음)원음 확인 창이 든 첫 앱 빌드. 0 이면 원음 봇에 아무도 걸 수 없음
TELEGRAM_VOICE_STREAM_MIN_SHEET_VERSION2원음 확인 창의 판
TELEGRAM_VOICE_STREAM_DENY_ADDRESSES—원음 주소가 닿으면 안 되는 이 배포의 주소(공개 호스트와 사이트 호스트는 저절로)
TELEGRAM_WATCH_API_PORT, _HOST, _ANY_ADDRESS, TELEGRAM_WATCH_AUDIO_URL, TELEGRAM_WATCH_ALLOW_PLAIN, TELEGRAM_WATCH_SESSION_DAYS, TELEGRAM_WATCH_AUTH_PER_MINUTE—, 127.0.0.1, —, —, —, 7, 30애플 워치 API 와 소리 입구 주소(서버 설치 — 애플 워치 입구). ALLOW_PLAIN 은 개발 전용
TELEGRAM_VOICE_EVIDENCE_KEY_DIR—봇이 거는 전화의 허락 기록(HMAC 증거)에 쓰는 키의 폴더. 700, 백업되는 폴더 밖. scripts/voice-evidence-key.py 로 만들고 바꿈. 없으면 아무도 허락을 켤 수 없음. 컴포즈에서는 .env 에만 두면 서버에 닿지 않음 — 봇이 거는 전화를 켤 때 deploy/docker-compose.voice.yml 의 pabal-server 에 environment(컨테이너 안 경로)와 volumes(호스트의 그 폴더)로 더함
TELEGRAM_VOICE_OUTBOUND_MIN_BUILD_DESKTOP, _ANDROID, _IOS, _WATCHOS0 (없음)허락 창이 든 첫 앱 빌드. 이보다 옛 빌드는 허락을 켤 수 없고 봇의 전화도 울리지 않음. 0 이면 그 플랫폼은 없음
TELEGRAM_VOICE_CALLME_DEVICE_AGE_SECONDS86400허락을 켤 수 있는 기기의 로그인 뒤 시간(초). 시험 서버만 바꿀 수 있음(운영 서버는 경고하고 무시)
TELEGRAM_VOICE_KEEP_TEXT_UNREVIEWED—법률 검토 전의 대화 남기기 체크 상자 문구를 워치에 보임. 내부 시험 서버 전용
TELEGRAM_MCP_*, TELEGRAM_MCP_APPS_READY—MCP 부품(deploy/docker-compose.mcp.yml 이 넣음). TELEGRAM_MCP_EGRESS_OWN_ADDRESSES(파발의 공인 주소)는 MCP 가 아니어도 웹훅·도구 요청이 파발 자신에게 가지 못하게 쓰입니다
TELEGRAM_VOICE_BOTS, TELEGRAM_VOICE_NOTICE_CHECK=off, TELEGRAM_VOICE_STREAM_ALLOW_PRIVATE—시험 서버 전용(TELEGRAM_TEST_NUMBERS=true 일 때만): 나열한 봇(id·@이름, * 은 모든 봇) 허용 · 법률 절 확인 건너뛰기 · 원음 주소에 사설 주소
TELEGRAM_VOICE_CALLME_UNREVIEWED, TELEGRAM_VOICE_DEV_ECHO_OUTBOUND, TELEGRAM_VOICE_TEST_DIAL, TELEGRAM_VOICE_TEST_DIAL_USERS—시험·개발 서버 전용: 법률 검토 전에 봇이 거는 전화의 스위치를 켤 수 있게(개발·시험 서버) · 에코 게이트웨이가 봇의 전화를 걸게(개발 서버, TELEGRAM_TEST_MODE) · 관리 API 의 시험 걸기와 그것이 울릴 계정(+99966 시험 번호 말고 20개까지, 시험 서버). 운영 서버는 경고하고 무시

데이터 파일

파일내용권한
keys/private.pem서버 RSA 비밀키. 절대 밖으로 내보내지 않음600
keys/private.pem.pub공개키. 앱 빌드와 /docs/server-key.pem 에 쓰임—
keys/firebase-service-account.json(선택) 안드로이드 푸시용 Firebase 비밀 키. 새어 나가면 Firebase 콘솔에서 지우고 새로 받음600
data/admin-token관리 화면 토큰600
data/operations.json설정 탭의 값 — 가입 규칙, SMS·SMTP 비밀값, 차단 번호, 음성 탭의 설정(차단 스위치·공개·시험자·허용한 봇·한도)600
keys/voice-link.secret(음성 봇) 게이트웨이와 같은 링크 비밀400
/etc/pabal/voice/voice-master.key(음성 봇) 봇 주인의 자기 키·원음 비밀을 감싸는 마스터 키. 데이터 폴더 밖, 백업에 넣지 않음600 (폴더 700)
data/media/사진과 파일 원본—
PostgreSQL events 테이블계정·대화·메시지·로그인·봇·웹훅 설정 — 모든 변경의 기록—

보안 메모

  • 관리 포트를 인터넷에 직접 열지 마세요. 운영 구성의 Caddy 는 /admin, /health, /metrics 같은 관리 경로를 404 로 막습니다.
  • 관리 데이터 요청은 모두 Authorization: Bearer <토큰> 이 필요하고, 다른 사이트의 페이지는 읽을 수 없습니다.
  • SMS·SMTP 비밀값은 operations.json 에만 있고, 화면과 API 는 "저장됨"만 알려 줍니다. 비밀값 칸을 비워 두고 저장하면 기존 값이 유지됩니다.
  • 토큰·비밀값은 로그에 찍히지 않고, 로그의 전화번호는 가려서 남깁니다. Bot API 주소(토큰 포함)도 로그에 남기지 않습니다.
  • 푸시 알림의 기기 토큰은 로그에 지문으로만 남고, Firebase 키 파일의 내용과 알림 내용은 로그에 남지 않습니다.
  • 인증 키가 DB 에 들어 있으므로, DB 에 접근할 수 있는 사람은 사용자 트래픽을 풀 수 있습니다. DB·백업을 RSA 키만큼 지키세요.

한계

  • 기다리는 코드와 최근 기록은 메모리에 있어 서버를 다시 켜면 사라집니다(앱에서 다시 요청하면 됨).
  • SMS 제공자의 실제 발송은 각 제공자 계정으로 확인해야 합니다. 서버는 각 제공자의 문서대로 요청을 만듭니다.
  • 아직 없는 것: 봇 강제 삭제, 메시지 열람, 로그 보기, 그래프, 푸시 알림 통계(푸시는 서버 로그로 확인합니다).