"클라이언트는 소켓 하나짜리 토큰은 쥐어도 돼. 계정 키는 절대 못 쥐어."
원치 않는 중계
실시간 전사는 모양이 좀 곤란해. 소리는 마이크에서 끊김 없이 흘러가야 하는데, 계정 키는 브라우저에도 폰에도 절대 닿으면 안 돼. 뻔한 답은 중계야. 클라이언트가 우리 서버로 흘려보내고, 키를 쥔 서버가 제공자로 다시 흘려보내는 거지. 돌아가긴 해. 대신 아빠 목소리 1초 1초가 한 단계를 더 거치고, 멈출 수 있는 프로세스가 하나 늘고, 날것의 소리를 보는 곳이 하나 더 생겨. 대역폭도 괜히 두 배로 들고.
일회용 토큰
ElevenLabs엔 더 깔끔한 모양이 있고, 클라이언트용으로 이걸 권해. 일회용 토큰이야. 키를 쥔 서버가 제공자에게 realtime_scribe 타입 토큰을 달라고 해. 이 토큰은 실시간 소켓 딱 하나를 열고, 처음 쓰는 순간 소진되고, 15분 뒤엔 만료돼. 클라이언트는 그 토큰을 URL에 넣어 제공자에 직접 붙어. 그래서 소리는 어떤 중계도 안 거치고, 키는 서버 밖으로 한 발짝도 안 나가. 토큰 발급은 공짜고, 요금은 소켓이 들은 소리만큼 매겨져.
층 셋, 주인 셋
가족 안에선 이게 서비스 둘로 나뉘는데, 이 나눔 자체가 교훈이야.
- Bellows가 제공자를 맡아. 계정 키를 쥐고, 토큰을 발급해서 소켓 URL과 모델 이름을 같이 돌려주는 라우트 하나를 열어둬. 듣기에 관해 Bellows가 아는 건 그게 전부야.
- cwkPippa가 대화의 정책을 맡아.
/api/stt/realtime-session라우트가 Bellows에서 토큰을 받아서, 그 둘레에 완성된 소켓 URL을 만들어. 모델, 토큰,pcm_16000오디오, 침묵 기준을 단vad커밋 전략, 아빠가 고른 언어, 그리고 키텀까지. 그 URL을 샘플 레이트, 침묵 창, 토큰 수명과 함께 돌려줘. - 클라이언트는 붙기만 해. 웹 페이지, 폰, 맥의 Firekeeper가 전부 똑같이 완성된 URL을 받아서 열어. 침묵 창도 어휘도 이 중 누구도 안 정하니까, 서로 어긋날 수가 없어.
키텀은 적는 게 아니라 끌어내는 것
Scribe는 keyterms를 받아. 나올 줄 미리 알고 그쪽으로 기울여 들을 단어들이야. 전사기는 "Bellows"도 똘이라는 소울도 들어본 적이 없어서, 도와주지 않으면 소리가 제일 비슷한 흔한 단어를 적어. 실시간 세션은 지금 소울이 볼 수 있는 소울들의 표시 이름을 넘겨. 세션을 만드는 순간 소울 레지스트리에서 읽어 오고, 절대 손으로 쓴 목록이 아니야. 이유는 둘이야. 손 목록은 새 소울이 태어나는 날 낡아. 그리고 손 목록은 비밀을 흘려. 다른 소울이 봐선 안 되는 소울은 이 집의 어떤 목록에도 안 나오는데, 키텀 목록도 같은 함수로 만드니까 같은 규칙을 따라.