AI 生成のイメージ画像
이 이미지는 AI가 생성한 이미지입니다.
Shelly의 bug #76에서는 상위(upstream) codex 바이너리가 ET_EXEC (musl, static) 방식이며, SELinux 때문에 Android 14+에서는 실행할 수 없다는 점이 기록되었습니다. Shelly는 이 문제에 대응하여, termux 포크인 codex-termux의 Android 네이티브 바이너리(ET_DYN, bionic 링크)를 직접 실행하는 방식으로, 2026-04-17 커밋 6ba1419a5에서 구현했습니다. 이하에서는 세 가지 질문에 순차적으로 답합니다. proot 경유 시작 방식은 왜 대체되었는가? 직접 실행 환경에서 로그인, TLS, TUI를 어떻게 결합했는가? 어떤 제약 사항이 남아 있는가?
상위 바이너리가 작동하지 않는 범위와 proot의 위치
2026-04-11 커밋 9c68f866c에서, Shelly는 ET_EXEC 바이너리를 구동하기 위해 proot과 libtalloc을 jniLibs에 포함했습니다. 이 커밋에서는 proot용으로 수정된 codex.js를 포함하는 @openai/codex 패키지도 추가했으며, codex.js의 spawn()과의 호환성을 확보하기 위한 ~/bin/proot 래퍼를 생성했습니다.
하지만 6ba1419a5에는 이 proot 방식이 실패했다는 내용이 기록되어 있습니다. 문제가 발생한 범위는 이 커밋에서는 Android 14+로 간주되었습니다. 이후 릴리스에서 Shelly가 동작을 확인한 기기는 Galaxy Z Fold6 / Android 16입니다. 포크 측에서 현재 제시하는 동작 요구 사항은 Android 10+ / API 29+와 ARM64 기기, Node.js >= 18이며, 배포 바이너리는 API 29의 NDK 타겟으로 빌드되었습니다 [1].
bionic ELF 직접 실행과 TUI 연결
6ba1419a5에서는 codex-termux의 Android 네이티브 바이너리를 CI 빌드 중에 다운로드하여, jniLibs에 libcodex_exec.so로 배치했습니다. 기기 상에서는 LibExtractor가 이 파일을 termux-libs/codex_exec으로 전개하고, sed를 이용한 수정 작업을 통해 상위의 binaryPath를 linker64와 codex_exec의 조합으로 대체했습니다. 출처는 github.com/DioNanos/codex-termux의 v0.121.0-termux입니다.
2026-04-20 커밋 acd13d5e6 시점까지 확보했던 codex-termux의 tarball에는, exec/resume/review용 일회성 실행 러너인 codex-exec.bin (약 106 MB)과 대화형 REPL용 전체 ratatui TUI인 codex.bin (약 154 MB)이 포함되어 있었습니다. CI는 codex-exec.bin만 jniLibs에 복사했기 때문에, 기기 상의 codex는
codex exec "foo"
와 같은 일회성 실행으로 제한되었습니다. bug #114 수정 전에는 인자 없는 codex나 codex "prompt"가 실패하거나 감지되지 않은 채 exec 모드에서 작동했습니다. acd13d5e6에서는 CI가 codex.bin도 복사하도록 변경되었습니다.
이 TUI가 사용하는 Ratatui는 백엔드를 통해 터미널 에뮬레이터와 연결되며, Terminal 타입을 거쳐 장식된 문자(formatted character)를 그리고, 커서를 조작하며, 터미널 크기 등의 속성을 조회합니다. 각 백엔드는 터미널의 입출력 처리 방식을 변경하는 Raw Mode, 셸 명령어와 별도의 버퍼에 그리는 Alternate Screen, 마우스 이벤트를 받는 Mouse Capture를 지원합니다 [2].
이후 v5.1.0 build 769에서는 codex-cli 0.125.0-termux가 작동하며, Galaxy Z Fold6에서 codex -m gpt-5.5 "Say OK"에 'OK'가 반환되는 것을 확인했습니다. 이 릴리스는 mmmbuto-codex-cli-termux-<version>.tgz 형식의 npm-pack 애셋을 지원하며, 포함 및 승격 전에 npm의 dist.integrity
에서 검증한다. 여러 페인이나 빠른 재시작으로 인해 Codex 다운로드가 중복되지 않도록 런타임 업데이트는 ~/.shelly-runtime/.update.lock을 통해 직렬화되었다. 세 개의 업데이트를 동시에 실행하는 테스트에서는 오직 하나만 실제로 업데이트되었고, 나머지 두 개는 done (skipped, locked) 상태로 즉시 종료되었다.
현재 포크의 공개 버전은 OpenAI Codex rust-v0.160.0을 기준으로 한 0.160.0-termux.3이다. 이 패키지에서는 래퍼가 CODEX_SELF_EXE를 네이티브 ELF로 설정하고, LD_LIBRARY_PATH를 정리한 후 libc++_shared.so를 포함한다. Android 바이너리는 RUNPATH=$ORIGIN을 붙여 링크되어 있다 [1]。
로그인 및 TLS 신뢰 저장소
원격 환경이나 헤드리스 환경 외에도, 네트워크 구성에 따라 localhost 콜백이 차단되는 조건에서는 브라우저를 통한 로그인이 작동하지 않을 수 있다. 이 경우 베타 기능인 디바이스 코드 인증(device code authentication)을 사용하여 codex login --device-auth를 직접 실행하는 것이 권장된다. 사전에 개인 계정의 경우 보안 설정에서, 워크스페이스의 경우 관리자 권한 설정에서 디바이스 코드 로그인을 활성화해야 한다. 그 후 브라우저에서 링크를 열어 로그인하고 일회용 코드를 입력한다 [3]。
디바이스 코드 인증을 사용할 수 없는 경우의 대안으로, 브라우저로 로그인할 수 있는 머신에서 만든 ~/.codex/auth.json 파일을 브라우저가 없는 머신으로 복사하는 방법이 있다. 자격 증명이 파일이 아닌 OS 자격 증명 스토어에 저장된 경우, 이 방법은 사용하지 못할 때가 있다. 포트 전달(port forwarding)을 사용할 수 있는 경우에는, 기본 localhost:1455를 ssh -L 1455:localhost:1455 user@remote로 터널링하고, 해당 SSH 세션 내에서 codex login을 실행하는 방법도 있다 [3]。
Shelly 측에서는 2026-04-19의 커밋 6f0b4e164가 Mozilla CA 번들과 codex-login의 device-auth를 Codex 네이티브용으로 추가했다. 이 커밋은 기기 상에서 Codex 네이티브 로그인을 가능하게 하며, 포함된 모든 TLS 클라이언트에서 발생하던 눈에 띄지 않는 HTTPS 실패들을 수정한다.
CA 번들은 2026년 2월 시점의 Mozilla cacert.pem이며, APK 에셋으로 포함되어 시작할 때마다 ~/.shelly-ssl/ca-certificates.crt로 전개된다. 아울러 SSL_CERT_FILE, SSL_CERT_DIR, CURL_CA_BUNDLE, NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE를 작성하여 curl, node, codex_exec의 rustls, python requests의 HTTPS 실패를 막는 설계였다. 배포처에서는 cacert.pem을 Mozilla의 인증서 스토어를 PEM 형식으로 만든 파일로 제공하며, 비압축 상태에서 약 200KB이다 [4]。
OpenAI의 현행 사양에서는 자체 CA PEM 번들을 CODEX_CA_CERTIFICATE로 지정할 수 있으며, 설정되지 않은 경우 SSL_CERT_FILE로 폴백(fallback)한다. 이 설정은 로그인, 일반 HTTPS 요청, 보안 WebSocket 연결 모두에 적용된다 [3]. 현재 포크는 Termux의 TLS 신뢰에 포함된 webpki roots를 사용하며, Android의 platform-verifier에는 의존하지 않는다 [1]。
다른 방식과 비교
여기서는 Android 상에서 Codex CLI를 구동하는 동일한 문제에 대한 실행 방식을, 해결된 문제와 남은 제약이라는 두 가지 축으로 비교한다.
proot을 경유하는 방식은 ET_EXEC 바이너리를 그대로 구동하는 것을 목표로 했다. Proot은 ptrace에 의존하는 사용자 공간 구현으로, 기본적으로 프로그램 실행을 게스트 rootfs 내로 제한한다 [5]。
직접 실행 방식은 상위 버전(Upstream)의 ET_EXEC 바이너리를 bionic 링크의 ET_DYN 바이너리로 대체함으로써 Android 14 이상에서 발생할 수 있는 실행 불가 문제를 피한다. 2026-04-17자 커밋 6ba1419a5에서는 바이너리 크기가 110MB에 달했기 때문에 git에는 포함하지 않고 빌드 시점에 가져왔다.
장치 외부에서 구동하는 방식의 경우, fork 개발팀은 기밀성이 높은 작업에는 Linux/macOS 환경의 OpenAI Codex CLI를 SSH를 통해 사용하는 것을 권장하고 있다 [1]가 안내되어 있다.
남은 제약과 사용처
상위 버전의 동작이 Bionic/Termux에서 아직 깨지는 부분이 남아있으며, 여기에는 Android PTY와 잠금 처리(lock handling)의 호환성 패치가 포함된다. 잠금 처리에 대해서는 파일 시스템이 잠금 미지원(non-support)을 반환할 때 기능을 비활성화하는 것은 일부 영역에 한정되며, 자격 증명 및 인증서 처리는 Fail-close 상태를 유지한다 [1]가 안내되어 있다.
Mozilla의 인증서 저장소에서 변환한 cacert 파일에는 Firefox에서 특정 도메인만 허용하도록 CA에 나중에 추가된 제약 조건이 계승되지 않는다 [4]가 안내되어 있다.
bug #139에서는 APK 크기 축소 목표가 제시되었다. 2026-04-27자 커밋 39dc291a1은 APK를 300400MB 줄이는 것을 목표로 설정했으며, 전체적으로 약 954MB에서 약 600MB로 줄일 계획을 세웠다. 그 첫 번째 항목은 arm64에만 ABI(Application Binary Interface)를 제한하는 변경으로, 예상되는 축소량은 약 80150MB였다.
fork가 처리하는 것은 상위 버전이 출시하지 않은 호환성 패치뿐이다. fork는 상위 버전의 패치 출시에 일일이 따르기보다는 의미 있는 기능 추가를 포함하는 주요 시점에 맞춰 진행하는 방침을 취하고 있다 [1]가 안내되어 있다.
Sources
- [1] Codex Termux
- [2] Backends
- [3] Authentication
- [4] Mozilla에서 추출한 CA 인증서(CA certificates extracted from Mozilla)
- [5] PRoot
- 필자의 자료
Discussion