명령에서 상주 서비스로 — Codex CLI의 기본값 데몬이 묻는 것

CLI가 창을 닫은 뒤에도 남아 스스로를 갱신한다면, 그것은 편의를 위한 설계인가, 사용자가 동의한 적 없는 상주 서비스인가?

도입

2026년 10월 12일(미국 시각) 해커뉴스 1면에 「Codex CLI는 기본값으로 영속적이고 스스로 업데이트하는 백그라운드 데몬을 설치한다」는 제목의 글이 올라와 609점을 받았다. 링크는 기사가 아니라 OpenAI의 openai/codex 저장소에 올라온 GitHub 이슈 #48195였다. 이슈 자체는 9월 25일에 등록됐고, 보름 넘게 조용히 쌓이던 불만이 해커뉴스를 거치며 한꺼번에 터진 셈이다.

이슈 작성자 Yihua-Liu-1997의 요구는 단순하다. daemon_auto_start를 CLI에서는 옵트인(기본값 false)으로 바꾸거나, 최소한 켜기 전에 한 번은 물어보라는 것이다. 근거로 든 관찰은 네 가지다. 대화형 codex 세션을 여는 것만으로 창이 모두 닫힌 뒤에도 살아남는 데몬이 뜬다. 약 374MB의 플랫폼 패키지 전체가 홈 디렉터리 아래로 복사되고, 데몬은 그 사본에서 실행된다. 분리된 업데이터가 시작 5분 뒤, 그 뒤로는 매시간 업데이트를 확인해 새 릴리스를 내려받고 백엔드를 재시작한다. 그리고 데몬은 처음 자신을 띄운 세션의 환경 변수를 계속 붙잡고 있다.

이 글은 이 사건을 버그 리포트 하나로 읽지 않는다. 개발 도구가 「사용자가 실행하는 명령」에서 「사용자 머신에 상주하는 서비스」로 성격을 바꿀 때 무엇이 함께 바뀌는지, 그리고 그 전환을 기본값으로 밀어붙일 때 어떤 비용이 누구에게 넘어가는지를 따져 본다.

현상 분석: 0.157.0에서 실제로 일어난 일

먼저 사실관계를 이슈 본문 기준으로 정리하자. 작성자의 환경은 Linux(WSL2)에서 npm install -g @openai/codex로 설치한 CLI 0.157.0이다. 이 버전부터 대화형 세션을 열면 codex app-server --managed-daemon 프로세스가 별도 세션에서 TTY 없이 시작된다. 이 프로세스는 Codex 창을 모두 닫은 뒤에도 PID 1로 재부모화되어 남아 있고, codex app-server daemon stop을 실행하거나 재부팅할 때까지 계속 돈다.

두 번째 관찰이 더 미묘하다. 데몬은 npm이 방금 설치한 것과 바이트 단위로 동일한 패키지(codex, codex-code-mode-host, 번들된 bwrap·zsh·rg)를 ~/.codex/packages/app-server-daemon/releases/<version>/ 아래로 통째로 복사하고, 그 사본에서 실행된다. 작성자의 표현을 빌리면, 이 시점부터 백엔드의 버전은 패키지 매니저로 설치한 CLI의 버전과 분리된다. 사용자는 npm으로 특정 버전을 설치했다고 믿지만, 실제로 턴을 처리하는 것은 다른 경로에 놓인 다른 세대의 바이너리일 수 있다.

세 번째는 업데이터다. codex app-server daemon pid-update-loop라는 분리된 프로세스가 시작 5분 뒤, 이후 매시간 업데이트를 확인하고, 새 릴리스를 내려받아 백엔드를 재시작한다. 관련 이슈 #40969(8월 26일 등록, 이 글을 쓰는 시점에 열려 있음)는 이 재시작이 실행 중인 턴에 60초의 드레인 시간만 주고 그 뒤 SIGKILL을 보낸다고 보고한다. 빌드나 테스트를 몇 분씩 돌리는 에이전트 작업이라면 업데이트 한 번에 작업이 잘려 나간다는 뜻이다.

네 번째는 환경의 고정이다. 작성자는 호출마다 HTTPS_PROXY를 바꿔 끼우는 래퍼로 Codex를 띄우는데, 데몬은 처음 자신을 시작한 세션의 프록시 변수를 계속 쓰고, 이후 세션이 어떤 설정으로 뜨든 외부 연결은 그 첫 프록시를 거친다. 작업 디렉터리가 세션이 아니라 데몬 기준으로 넘어간다는 #42457, #37537도 같은 뿌리로 보인다고 작성자는 적었다. 10월 13일에는 macOS에서 0.162.1을 쓰는 다른 사용자가 「데몬 서버가 세션 간에 환경 변수를 누설한다」는 제목으로 #53547을 새로 열었다.

여기에 끄는 과정에서 발견한 버그가 덧붙는다. codex app-server daemon stop은 앱 서버는 멈추지만 업데이터 프로세스는 남겨 두어, 작성자는 70초 뒤에도 살아 있는 업데이터를 직접 죽여야 했다. 이슈에 적힌 우회책은 다음과 같다.

codex features disable daemon_auto_start
codex app-server daemon stop
pkill -f 'app-server daemon pid-update-loop'   # not stopped by `daemon stop`
rm -rf ~/.codex/packages/app-server-daemon

그렇다면 OpenAI는 이 변경을 어떻게 알렸는가. 0.157.0 릴리스 노트(9월 25일 공개)의 신기능 목록에는 「적격한 대화형 세션에 대해 자동 백그라운드 서버 시작을 활성화했고, 서버 설정이 호환되지 않을 때의 복구 선택지를 추가했다」는 취지의 한 문장이 있다. 영속 프로세스, 패키지 사본, 매시간 업데이터라는 단어는 그 문장에 없다. 이슈 스레드의 한 코멘트(foma-agent)는 공식 설정 레퍼런스 페이지에 daemon_auto_start 항목이 하나도 없다고 지적했다. 이 부분은 해당 코멘트의 주장이며, 필자가 설정 레퍼런스 페이지를 직접 확인하지는 않았다.

해커뉴스 스레드의 반응은 크게 세 갈래였다. 첫째는 우회 방법의 공유다. 한 이용자(whimblepop)는 문서화된 설정은 없지만 기능 플래그로 끌 수 있다며 ~/.codex/config.toml의 [features] 아래에 daemon_auto_start = false를 넣는 법을 안내했고(직접 인용은 아니고, 코멘트의 요지를 요약), 다른 이용자(grimgrin)는 거기에 더해 셸 함수로 항상 --no-daemon을 붙이는 이중 방어를 소개했다(직접 인용은 아니고, 코멘트의 요지를 요약). 둘째는 디스크 사용량이다. 한 이용자(icantevenhold)는 오래된 버전이 쌓여 자기 시스템에서 합계 12GB 정도를 차지했다고 적었다(직접 인용은 아니고, 코멘트의 요지를 요약). 셋째는 비교다. Claude Code도 같은 운영 모델이라며, 홈 디렉터리 아래 이전 버전 바이너리들과 포그라운드 창 없이 도는 백그라운드 작업을 확인했다는 코멘트(timdorr)가 있었다(직접 인용은 아니고, 코멘트의 요지를 요약).

심층 분석: 무엇이 「명령」과 「서비스」를 가르는가

이 사건의 핵심은 자동 업데이트 자체가 아니다. 해커뉴스의 첫 코멘트(dingaling911)는 이 분야는 빨리 움직이니 자동 업데이트가 표준이고, 브라우저도 그렇게 하는데 하네스가 못 할 이유가 없다고 반론했다(직접 인용은 아니고, 코멘트의 요지를 요약). 이 반론은 절반은 맞다. 브라우저의 자동 업데이트는 보안 패치를 빠르게 퍼뜨리는 검증된 방식이다. 그러나 브라우저와 Codex CLI 사이에는 사용자가 맺은 계약의 종류가 다르다.

유닉스 전통에서 CLI의 계약은 명확하다. 사용자가 명령을 실행하면 프로세스가 뜨고, 일을 하고, 끝나면 사라진다. 그 프로세스는 호출한 셸의 환경 변수와 작업 디렉터리를 상속하고, 실행되는 바이너리는 사용자가(또는 사용자가 위임한 패키지 매니저가) 설치한 바로 그 파일이다. 한 해커뉴스 이용자(kristjansson)는 데몬 모드가 「Codex CLI는 호출한 셸의 환경 변수를 상속한다」 같은 가정을 깨뜨린다고 짚었다(직접 인용은 아니고, 코멘트의 요지를 요약). 프록시 고정과 작업 디렉터리 문제는 이 계약 위반이 겉으로 드러난 증상이다.

반면 브라우저나 데스크톱 앱은 처음부터 「상주하는 애플리케이션」으로 설치되며, 사용자는 그것이 스스로 업데이트한다는 사실을 대체로 알고 받아들인다. 이슈 작성자도 바로 이 구분을 제안한다. 공유되고 스스로 갱신하는 데몬은 원격 제어나 데스크톱 앱에는 합리적이니 거기서는 켜되, CLI의 기본값은 「창과 함께 살고 함께 죽으며, 내가 설치한 바이너리를 실행하고, 백그라운드에서 스스로 업데이트하지 않는 것」이어야 한다는 것이다. 해커뉴스에서도 데스크톱 클라이언트라면 예상하겠지만 npm으로 설치한 CLI가 이러면 놀랄 만하다는 코멘트(infinite_spin)가 같은 선을 그었다(직접 인용은 아니고, 코멘트의 요지를 요약).

두 번째 쟁점은 공급망이다. 패키지 매니저 경유 설치에는 그 자체로 통제 지점이 있다. npm이라면 lockfile과 버전 고정, Homebrew나 배포판 패키지라면 메인테이너의 검토와 서명이 있다. 한 해커뉴스 이용자(thebeardisred)는 바로 이런 순간에 배포판 모델의 가치가 드러난다며, 패키징하고 테스트하고 합리적인 기본값을 정해 주는 사람들이 이런 일을 막아 준다고 적었다(직접 인용은 아니고, 코멘트의 요지를 요약). 그런데 데몬이 설치된 패키지를 홈 디렉터리로 복사하고 그 사본을 매시간 스스로 교체하기 시작하면, 패키지 매니저는 더 이상 실제로 실행되는 코드의 정본이 아니다. 기업 환경에서 특정 버전을 승인하고 배포했다고 해도, 그 승인은 첫 한 시간만 유효하다.

이슈 스레드의 한 코멘트(aetherxeg-source)는 이 문제를 「설치된 CLI의 정체성 ≠ 데몬 실행의 정체성」이라는 경계로 요약했다. 그리고 클라이언트가 붙을 때마다 CLI 버전, 데몬 바이너리 다이제스트, 데몬 세대, 업데이터 상태, 상속된 환경의 세대를 담은 「실행 영수증」을 내보이자고 제안했다. 과한 제안처럼 들릴 수 있지만, 질문 자체는 정확하다. 지금 이 턴을 처리하고 있는 것이 정확히 어떤 코드인가라는 질문에, 현재 구조에서는 사용자가 답할 수단이 없다.

이 질문이 코딩 에이전트에서 특히 무거운 이유가 있다. 코딩 에이전트는 셸 명령을 실행하고, 저장소를 읽고 쓰고, 종종 자격 증명이 놓인 환경에서 돈다. 해커뉴스의 한 이용자(dllu)는 자동 업데이트 도중 권한이 망가져 .git이 읽기 전용이 되고 목표 작업이 막혔던 경험을 보고했다(직접 인용은 아니고, 코멘트의 요지를 요약). 이 블로그는 7월 12일에 Grok CLI가 xAI로 무엇을 보내는지 패킷 수준에서 분석한 글을 다뤘는데, 그때의 질문이 「이 에이전트는 무엇을 밖으로 보내는가」였다면 이번의 질문은 「이 에이전트는 무엇을 내 머신 안에 남기고, 그것은 누구의 의지로 바뀌는가」다. 두 질문은 같은 신뢰 경계의 안팎이다.

물론 반대편의 사정도 있다. 해커뉴스에서 법률용 IDE를 만든다는 이용자(piker)는 버그 있는 애플리케이션이 현장에 오래 남는 것 역시 사용자에게 적대적이라며, 자신들은 데몬 방식도 검토했지만 결국 매 실행 시 업데이트를 강제 확인하는 방식을 택했다고 적었다(직접 인용은 아니고, 코멘트의 요지를 요약). 이 대비가 시사하는 바가 있다. 같은 「빨리 업데이트시키고 싶다」는 동기라도, 실행 시점에 사용자 앞에서 확인하는 방식은 사용자가 그 순간을 볼 수 있고 거부할 수 있다. 백그라운드 데몬은 그 순간을 사용자 시야 밖으로 옮긴다. 차이는 업데이트 빈도가 아니라 가시성과 거부 가능성에 있다.

전망과 시사점: 잘 행동하는 CLI의 조건

이 사건에서 「잘 행동하는 개발 도구」의 조건을 몇 가지 뽑아낼 수 있다. 첫째, 상주성은 옵트인이어야 한다. 창을 닫은 뒤에도 남는 프로세스는 사용자가 명시적으로 요청했을 때만 생겨야 한다. Codex에는 이미 codex app-server daemon start라는 명시적 진입점이 있으니, 이슈 작성자의 제안대로 그 경로로만 데몬을 띄우는 것은 기술적으로 어렵지 않아 보인다.

둘째, 패키지 매니저로 설치된 경우에는 그 설치를 정본으로 존중해야 한다. 이슈는 npm, brew, pacman 등으로 설치됐다면 복사와 자가 업데이트를 하지 말고 설치된 바이너리를 실행하라고 요구하며, 외부에서 관리되는 데몬 실행 파일을 지원해 달라는 #41188(Nix, Homebrew, npm 설치를 예로 듦)도 같은 방향이다. 스스로 업데이트하는 기능은 자체 설치 스크립트로 설치한 사용자에게만 의미가 있다. 해커뉴스에서는 XDG 규격을 따르지 않는 것 자체가 앱이 시스템을 함부로 다룰 것이라는 신호라는 지적(quotemstr)도 나왔다(직접 인용은 아니고, 코멘트의 요지를 요약).

셋째, 끄는 방법은 문서화되고 완결적이어야 한다. 현재 우회책은 문서화되지 않은 기능 플래그, 업데이터를 남기는 stop 명령, 수동 pkill과 디렉터리 삭제의 조합이다. 끄는 데 네 단계가 필요하고 그중 하나가 버그 우회라면, 그 기능은 사실상 끌 수 없는 것에 가깝다.

넷째, 자동 시작된 데몬은 마지막 클라이언트가 떠날 때 함께 종료돼야 한다. 이것은 이슈 작성자가 기본값을 유지하더라도 최소한 해 달라고 한 요구 중 하나이며, 「창과 함께 산다」는 CLI 계약을 부분적으로나마 복원하는 방법이다.

조직의 입장에서 보면 시사점은 더 실무적이다. 코딩 에이전트는 이제 개발자 개인의 도구이기 전에 엔드포인트 위에서 도는 소프트웨어 자산이다. 해커뉴스와 이슈 스레드에는 컨테이너나 별도 VM, 전용 기기 안에서만 에이전트를 돌린다는 코멘트가 여럿 있었다. 이것을 과민 반응으로 볼 수도 있지만, 버전을 고정하고 변경 로그를 확인한 뒤에만 올린다는 운영 원칙은 다른 어떤 의존성에도 적용하던 것이다. 에이전트만 예외일 이유는 없다. 한편 해커뉴스의 몇몇 이용자는 OpenAI가 서드파티 하네스 사용을 허용한다는 점을 들어, 1차 하네스가 나빠도 탈출구가 있다고 지적했다. 이 경쟁 압력이 기본값을 되돌리는 동력이 될지는 지켜볼 일이다. 이 글을 쓰는 시점에 이슈는 열려 있고, 스레드에서 OpenAI 측의 공식 답변은 확인되지 않았다.

결론

Codex CLI의 데몬 문제는 자동 업데이트가 좋은가 나쁜가의 문제가 아니다. 브라우저는 자동 업데이트를 하고, 대부분의 사용자는 그것을 고마워한다. 문제는 사용자가 「명령」으로 설치한 도구가 사용자에게 묻지 않고 「서비스」로 성격을 바꿨다는 데 있다. 그 전환과 함께 실행되는 코드의 정본은 패키지 매니저에서 홈 디렉터리 안의 사본으로 옮겨 갔고, 업데이트 시점은 사용자의 시야 밖으로 나갔으며, 환경 변수 상속이라는 오래된 계약은 깨졌다. 릴리스 노트는 이 모든 것을 한 문장으로 처리했다.

에이전트가 할 수 있는 일이 늘수록, 에이전트가 사용자 머신에서 무엇으로 존재하는지에 대한 투명성은 더 중요해진다. 상주는 옵트인으로, 패키지 매니저 설치는 정본으로, 끄는 방법은 문서화되고 완결적으로. 이것은 새로운 요구가 아니라 CLI라는 형식이 수십 년간 지켜 온 계약을 다시 적은 것에 가깝다. 빠르게 움직이는 분야일수록, 사용자가 설치한 것이 곧 실행되는 것이라는 단순한 보장이 신뢰의 바닥이 된다.


출처: