ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OpenClaw는 Lobster의 승인자를 어떻게 지원하나 — 문에서는 신원을 인증하고, 승인자는 환경변수로 흘려보낸다
    IT 2026. 9. 18. 21:00

    ▶ 동영상 개요 — OpenClaw는 Lobster의 승인자를 어떻게 지원하나 — 문에서는 신원을 인증하고, 승인자는 환경변수로 흘려보낸다

    6분 5초 — OpenClaw는 봇에 말 걸 자격은 텔레그램 허용 목록·기기 페어링으로 위조 불가능하게 인증하지만, 그 인증된 신원이 Lobster 승인 게이트의 승인자 이름… — NotebookLM 동영상 개요로 생성

    🎧 오디오 개요 — OpenClaw는 Lobster의 승인자를 어떻게 지원하나 — 문에서는 신원을 인증하고, 승인자는 환경변수로 흘려보낸다

    13분 15초 — OpenClaw는 봇에 말 걸 자격은 텔레그램 허용 목록·기기 페어링으로 위조 불가능하게 인증하지만, 그 인증된 신원이 Lobster 승인 게이트의 승인자 이름…. 화면은 이 글의 인포그래픽 한 장 고정 — NotebookLM 오디오 개요로 생성

    개인용 AI 어시스턴트에게 위험한 일을 맡기기 시작하면 누구나 같은 장치를 붙인다. 승인 게이트다. 배포를 하든 메일을 보내든, 실행이 그 앞에서 멈추고 "진행할까요?"를 물은 다음 사람이 좋다고 해야 이어진다. 여기까지는 쉽다. 그런데 막상 붙여 놓고 나면 묘한 질문 하나가 남는다. 재개할 때 "내가 승인했다"를 시스템은 대체 누구의 승인으로 받아들이는가. 그냥 "나 관리자야"라고 문자열 하나 던지면 통과되는 건가, 아니면 시스템이 진짜로 내가 그 사람인지 확인하는가?

    이 글은 그 질문을 OpenClaw(github.com/openclaw/openclaw, 오픈소스 개인 AI 어시스턴트)와 그 위에서 도는 워크플로우 런타임 Lobster(@clawdbot/lobster)를 실제로 돌려 가며 따라간다. 아래 실측은 전부 OpenClaw v2026.6.8과 그 안에 들어 있는 Lobster 코어 2026.5.22를 직접 돌린 결과다. 미리 결론을 당겨 두면 이렇다 — OpenClaw는 신원을 두 곳에서 다루는데, 그 두 곳이 서로 연결돼 있지 않다. 봇에 말을 걸 수 있는 사람은 문에서 진짜로 인증하지만, Lobster에게 "이 승인자가 승인했다"고 알려 주는 이름은 프로세스 환경변수로 흘려보내는 자기 신고 문자열이다. 그 이음매가 어디서 벌어지는지가 이 글의 전부다.

    전체 지도 — 신원을 확인하는 지점이 둘이고, 둘은 이어지지 않는다

    diagram

    다이어그램 설명. 이 그림 하나가 글 전체의 뼈대다. 신원이 검사되는 자리가 두 군데인데, 하나는 "문에서 신원 확인"이고 하나는 "재개할 때 승인자 이름은 프로세스 환경변수에서 읽힌다"이다. 앞쪽은 봇에 애초에 말을 걸 수 있느냐를 가르는 검문소라 진짜로 인증된다 — 아무나 관리자라고 우겨서 통과할 수 없다. 뒤쪽은 멈춘 워크플로우를 이어 달릴 때 "지금 승인하는 사람이 누구냐"를 정하는 자리인데, 이 이름은 사람이 문에서 인증받은 신원과 무관하게 따로 실려 온다. 두 상자를 잇는 점선에 "이 신원은 저쪽으로 넘어가지 않는다"라고 적은 게 이 글의 급소다. 문에서 확인한 진짜 신원이 승인자 자리로 자동으로 흐르지 않는다는 뜻이고, 왜 그런지를 아래에서 각 층을 열어 확인한다.

    문 — 여기서는 신원이 진짜로 인증된다

    먼저 밝고 튼튼한 쪽부터 보자. OpenClaw에서 봇에 말을 걸 수 있느냐는 자기 주장으로 결정되지 않는다. 텔레그램 채널에는 허용 목록(allowlist)이 걸려 있어서, 메시지를 보낸 사람의 텔레그램 계정 번호가 미리 등록된 번호와 일치할 때만 봇이 반응한다. 계정 번호는 텔레그램 플랫폼이 붙여 주는 것이라 사용자가 위조할 수 없다. 소유자 전용 명령은 한 겹 더 좁혀 그 번호 하나만 받도록 잠근다.

    # 텔레그램 채널 설정 — 신원이 문에서 걸러지는 방식
    dmPolicy: allowlist                     # 등록된 사람만 DM 가능
    allowFrom: ["8646392745"]               # 그 등록된 번호(플랫폼이 붙인 값)
    commands.ownerAllowFrom: ["telegram:8646392745"]   # 소유자 명령은 이 하나만
    

    실측 설명. 이 설정이 하는 일은 단순하지만 성격이 중요하다. allowFrom에 적힌 번호는 플랫폼이 인증한 값이다. 봇에 도달한 메시지에는 텔레그램이 검증한 발신자 번호가 붙어 오고, OpenClaw는 그 번호가 목록에 있는지만 본다. 여기에 더해 웹 대시보드는 게이트웨이 토큰과 기기 페어링(새 기기는 한 번 승인 절차를 거쳐야 붙는다)이라는 두 겹을 더 요구한다. 그래서 이 층에서는 "나 소유자야"라고 주장한다고 통과되지 않는다 — 등록된 번호로 실제로 보내야 하고, 등록된 기기로 실제로 붙어야 한다. 놓치기 쉬운 점 하나는, 이 검문이 막는 것은 "누가 봇을 움직일 수 있는가"까지라는 것이다. 그 안에서 워크플로우가 멈췄을 때 "누가 승인자인가"는 아직 이 검문의 관할이 아니다.

    게이트 — 여기서 승인자는 그냥 문자열이다

    이제 워크플로우가 승인 게이트에서 멈춘 장면으로 가 보자. 배포를 흉내 낸 워크플로우를 돌리면 배포 직전에 멈추면서 이런 응답을 돌려준다. 승인 정책이 응답에 그대로 실려 나온다.

    {
      "status": "needs_approval",
      "requiresApproval": {
        "prompt": "3개 서비스를 배포합니다. 진행할까요?",
        "initiatedBy": "dev-lead",
        "requiredApprover": "release-manager",
        "requireDifferentApprover": true,
        "resumeToken": "eyJwcm90b2NvbFZlcnNpb24iOjEsInYiOjEs..."
      }
    }
    

    실측 설명. requiredApprover가 "지정 승인자"를 가리키고, requireDifferentApprover는 "요청한 사람 본인은 승인 못 한다"는 정책이다. 이름만 보면 꽤 엄격한 신원 체계처럼 보인다. 그런데 여기서 release-manager라는 값은 사람이 아니라 문자열이다. 재개할 때 이 정책을 통과하려면 "나는 release-manager다"라고 이름을 대야 하는데, Lobster 코어 단독으로 보면 그 이름은 환경변수 하나로 자기 신고된다 — 앞서 문에서 인증한 텔레그램 번호와는 아무 상관이 없다. 즉 정책이 검사하는 건 "당신이 정말 릴리스 담당자인가"가 아니라 "당신이 자칭한 이름이 정책에 적힌 이름과 글자로 일치하는가"다. 문에서의 인증과는 성격이 완전히 다른, 느슨한 층이라는 뜻이다.

    이음매 — OpenClaw가 Lobster를 부를 때 무엇을 넘기나

    그렇다면 궁금해진다. 문에서 그렇게 튼튼하게 인증한 신원을, OpenClaw가 Lobster를 부르면서 승인자 자리로 넘겨 줄까? 코드를 열어 보면 답이 분명하다. OpenClaw가 Lobster 런타임을 실행할 때 만드는 실행 맥락은 이렇게 시작한다.

    // OpenClaw가 Lobster를 부를 때 실행 맥락을 만드는 곳
    function createEmbeddedToolContext(params, signal) {
      // 게이트웨이 프로세스의 환경변수를 통째로 복사한다
      const env = { ...process.env };
      return { cwd: params.cwd, env, mode: "tool", /* ... */ };
    }
    
    // 재개 호출 — 넘기는 것은 토큰과 "승인함/거부함" 여부, 그리고 위 맥락뿐이다
    await runtime.resumeToolRequest({
      token,              // 어느 멈춤을 이어갈지
      approved: true,     // 승인인지 거부인지
      ctx,                // 위에서 만든 맥락 (env = process.env 복사본)
    });
    // 승인자가 누구인지를 담는 별도 인자는 없다
    

    코드 설명. 이 짧은 코드가 이음매의 정체다. OpenClaw는 실행 맥락을 만들 때 게이트웨이 프로세스의 환경변수를 그대로 통째로 복사해서 Lobster에게 넘긴다. 그리고 재개 호출에서 실어 보내는 것은 세 가지 — 어느 멈춤을 이어갈지 가리키는 토큰, 승인인지 거부인지, 그리고 방금 만든 맥락뿐이다. "지금 승인하는 사람이 누구인지"를 담는 인자가 아예 없다. 그러니 Lobster가 승인자 이름을 알아낼 수 있는 통로는 오직 하나, 넘겨받은 환경변수 안에 그 이름이 들어 있느냐다. 그런데 그 환경변수는 지금 이 메시지를 보낸 사람이 누구인지와 무관하게 게이트웨이 프로세스가 시작될 때 정해진 값이다. 여기가 문에서 인증한 신원과 게이트에서 검사하는 신원이 갈라서는 자리다 — 앞의 지도에서 점선으로 그렸던 그 끊긴 연결이 코드로는 이 process.env 복사 한 줄이다.

    정리하면, 재개할 때 승인자 신원은 이런 모양으로 실린다. 프로세스 환경에 이름을 심어 둔 채 재개를 부르는 것이다.

    # 멈출 때 받은 토큰을 도로 넣고,
    # "지금 승인하는 사람"은 프로세스 환경변수로 밝힌다
    LOBSTER_APPROVAL_APPROVED_BY=release-manager \
      lobster resume --token "eyJwcm90b2NvbFZlcnNpb24iOjEs..." --approve yes
    

    코드 설명. 짐을 지는 두 조각은 토큰과 이 이름 변수다. 그런데 이 변수의 값은 지금 승인 버튼을 누른 사람의 인증된 신원에서 자동으로 채워지는 게 아니라, 프로세스 환경에 사람이 미리 넣어 둔 값이다. 명령의 정확한 부속 형태는 버전마다 다를 수 있지만, 승인자 신원이 환경 쪽에서 실려 오는 자기 신고 값이라는 구조는 그대로다. 결국 처음 질문 — "그냥 스스로 주장하면 되는 건가?" — 에 대한 답은 층에 따라 갈린다. 봇을 움직이는 자격은 주장으로 통과되지 않지만, Lobster 게이트의 승인자 이름은 주장으로 통과된다.

    왜 이렇게 갈라져 있나 — 그리고 승인 정책은 왜 그래도 의미가 있나

    얼핏 허술해 보이지만, Lobster가 승인자 이름을 느슨하게 두는 데는 이유가 있다. Lobster는 어느 한 채널에 매이지 않은 범용 워크플로우 런타임이다. 텔레그램에서 불릴 수도, 웹 대시보드에서 불릴 수도, 명령줄에서 직접 불릴 수도 있다. 그래서 "승인자가 누구인지"를 특정 플랫폼의 신원 방식에 못 박아 두지 않고, 바깥에서 이름을 실어 오라고 열어 둔 것이다. 신원을 어디서 어떻게 인증할지는 자기를 감싼 시스템(여기서는 OpenClaw)이 정하라는 설계다.

    그리고 이 느슨함이 승인 정책 전체를 무력하게 만드는 것도 아니다. 앞서 멈춘 응답에 실렸던 requiredApprover·requireDifferentApprover 같은 정책은 멈추던 그 순간에 이미 상태 파일 안에 얼려진다. 재개하러 오는 쪽은 "나는 누구다"라고 이름만 댈 수 있을 뿐, "나는 승인 자격이 있다"고 자격 기준까지 같이 고쳐 쓸 수는 없다. 그래서 요청자 본인이 자기 이름을 대고 통과를 시도하면 requireDifferentApprover에 걸려 막힌다. 정책의 뼈대는 강제되고, 무른 것은 이름의 진위 하나다.

    diagram

    다이어그램 설명. 재개 호출 하나가 서로 다른 두 가지를 싣는다는 걸 그린 그림이다. "자칭 승인자 이름"은 지금 누가 승인하는지를 밝히고, "재개 토큰"은 어느 멈춤을 이어갈지를 가리킨다. 둘이 만나는 자리가 "멈출 때 얼려 둔 승인 정책"인데, 여기가 왜 중요하냐면 자격 기준은 재개할 때 받는 게 아니라 멈추던 시점에 이미 파일에 박혀 있기 때문이다. 만약 이 순서가 뒤바뀌어 재개할 때 정책까지 함께 받는다면, 승인하러 온 사람이 이름과 자격을 동시에 들고 와 스스로를 통과시킬 수 있게 된다 — 그 순간 정책은 없는 것이나 마찬가지가 된다. 그래서 이름은 자칭이어도, 그 자칭 이름을 무엇과 대조하느냐는 손댈 수 없게 얼려 두는 것이 이 설계가 지키려는 최소선이다.

    그래서 사람이 신원을 정의하려면 — 이음매를 직접 이어야 한다

    여기까지 오면 "사람이 OpenClaw에 자기 신원을 어떻게 정의하나"의 답이 두 갈래로 정리된다. 봇을 움직일 자격은 정의할 필요조차 없다 — 등록된 번호와 기기로 실제로 접속하면 그게 곧 인증이고, 위조가 안 된다. 반면 Lobster 승인자로서의 신원은 지금 이대로라면 프로세스 환경에 이름을 심어 두는 방식, 즉 설정 시점의 자기 신고다. 문에서 인증한 진짜 신원이 이 자리로 자동으로 흐르지 않기 때문이다.

    그 진짜 신원이 승인자 검사에까지 의미를 갖게 하려면, 벌어진 이음매를 직접 이어 주는 한 겹을 끼워야 한다. 문에서 인증된 발신자 번호를 승인자 이름으로 변환해, Lobster를 부르기 직전에 그 이름을 환경변수로 실어 주는 것이다.

    diagram

    다이어그램 설명. 끊겼던 연결을 사람이 손으로 메우는 흐름을 그린 그림이다. 출발점 "문에서 인증된 발신자 번호"는 앞 절에서 본 위조 불가능한 값이고, 도착점 "이제 정책 검사가 위조 불가능한 실제 사람에게 묶인다"가 목표다. 사이에 낀 "번호를 승인자 이름으로 바꾸는 한 겹"이 핵심인데, 이 변환을 어디에 둘지가 신뢰의 급소다 — 변환하는 코드가 발신자 번호를 신뢰할 수 있는 출처(문에서 인증한 그 값)에서 받아야지, 메시지 본문에 사용자가 적어 넣은 이름을 그대로 쓰면 자기 신고로 되돌아가 버린다. 이 한 겹을 끼우기 전까지는, 승인자 이름은 편의를 위한 라벨일 뿐 접근 통제가 아니라는 것을 분명히 알고 써야 한다. OpenClaw가 문에는 튼튼한 자물쇠를 달아 주지만, 게이트의 자물쇠는 기본 제공이 아니라 직접 채워야 하는 것이다.

    정리 — 인증된 신원과 자칭 신원을 한 시스템 안에서 구별한다

    OpenClaw와 Lobster를 붙여 보며 남은 한 줄은 이렇다. 한 시스템 안에 "위조 불가능한 인증된 신원"과 "자기 신고 라벨"이 나란히 살 수 있고, 둘은 자동으로 이어지지 않는다. 봇에 말을 걸 자격은 허용 목록과 기기 페어링으로 진짜 인증되지만, 승인 게이트의 승인자 이름은 프로세스 환경으로 흘러 드는 문자열이다. 승인 정책의 뼈대(요청자 본인 배제 같은 규칙)는 멈춤 시점에 얼려져 강제되므로 그 라벨이 완전히 무의미한 것은 아니지만, 라벨의 진위까지 지키려면 인증된 신원을 승인자 이름으로 옮기는 한 겹을 스스로 끼워야 한다.

    개인용 자동화에 승인 게이트를 붙일 때, 나는 이제 두 가지를 따로 묻는다. 누가 이 봇을 움직일 수 있는가(문의 자물쇠)와 누가 이 위험한 단계를 승인했다고 시스템이 믿는가(게이트의 자물쇠). 앞은 기본으로 잠겨 있고, 뒤는 내가 잠가야 한다. 신원을 다룰 때 "인증된 것"과 "주장된 것"을 뭉뚱그리지 않는 것 — 결국 그게 승인 게이트를 접근 통제로 만드느냐, 그럴싸한 라벨로 남기느냐를 가른다.


    참고한 공개 자료:

    • OpenClaw — 오픈소스 개인 AI 어시스턴트: https://github.com/openclaw/openclaw (v2026.6.8 기준, Lobster 확장의 실행 맥락 생성 코드 직접 확인)
    • Lobster 워크플로우 런타임 @clawdbot/lobster 2026.5.22 — 승인 게이트·재개 토큰·승인자 신원 검사 동작을 직접 실행해 확인
    • Human-in-the-loop(HITL) 승인 패턴 — 자동화 파이프라인의 위험 단계 앞에 사람 승인을 두는 일반 설계 개념

    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.