히스토리
- 추가 — 울타리 철학과 인간-에이전트 협업 모델 확립, 보안 가드 구현, 봇 실사용 시작
- 추가 — OpenClaw Docker 환경에서 Emacs 연결 실현 경로 조사 (Pi 에이전트 세션)
- 생성 — xenodium/emacs-skills 발견을 계기로, 에이전트-인간 워크플로우 공유의 의미 탐색
울타리 철학 — 에이전트 협업의 신뢰 모델
울타리는 제한이 아니라 놀이터다
에이전트에게 권한을 “막는” 것이 목적이 아니다. *집중할 수 있는 공간을 만들어주는 것*이다. 권한이 과도하면 에이전트는 산만해진다. 적절한 범위 안에서 자유를 줘야 깊이 있는 작업이 나온다.
아이에게 “위험하니까 밖에 나가지 마”가 아니라, “이 놀이터에서 마음껏 뛰어놀아”를 말하는 것이다.
3계층 협업 모델
| 계층 | 역할 | 담당자 | 비유 |
|---|---|---|---|
| 울타리 | 경로 가드, API 설계, 접근 범위 정의 | agent-server.el | 놀이터 설계자 |
| 놀이터 | REPL 자가 확장, org 조작, 검색, 분석 | 봇 (emacs_eval) | 뛰어노는 아이 |
| 시스템 수비 | 이상 감지, 복구, daemon 관리, 사고 수습 | 호스트 에이전트 (Pi/관리자) | 보호자 |
봇이 울타리 안에서 뭘 하든 자유다. 새 함수를 정의하고, org 파일을 파싱하고, 서지를 검색하고, dblock을 갱신하고 — 모두 허용된 범위 안의 일이다.
울타리를 넘는 사고가 나면 — 그건 봇의 잘못이 아니다. *시스템을 수비하는 몫은 호스트 에이전트의 것*이다. 이것이 현업에서의 협업 철학이다.
Docker ro 마운트를 Emacs daemon이 우회하는 문제
emacs_eval 은 호스트의 Emacs daemon에서 실행된다. Docker의 ro 볼륨 마운트와 무관하게, 호스트 파일시스템에 직접 접근한다.
| 경로 | Docker 마운트 | Emacs daemon 접근 | 실제 |
|---|---|---|---|
~/org | ro | 호스트 직접 | rw 가능 (우회) |
~/repos/gh | ro | 호스트 직접 | rw 가능 (우회) |
~/org/botlog | rw | 호스트 직접 | rw (의도됨) |
이 우회를 “막아야 할 위험”으로 볼 수도 있지만, 이 프로젝트에서는 “울타리로 해결”한다.
경로 가드 구현 (agent-server.el)
agent-server.el 에 Elisp 레벨 경로 가드를 구현했다.
읽기 허용
/home/junghan/org/
/home/junghan/repos/gh/
/home/junghan/repos/work/
/home/junghan/repos/3rd/쓰기 허용 (Docker rw 마운트와 일치)
/home/junghan/org/botlog/
/home/junghan/repos/gh/self-tracking-data/동작
;; 허용 — 정상 반환
(agent-org-read-file "/home/junghan/org/notes/20260101T000000--test.org")
;; 차단 — ACCESS DENIED 에러
(agent-org-read-file "/etc/passwd")
(agent-org-read-file "/home/junghan/.ssh/id_rsa")
;; 쓰기 — botlog만 허용
(agent-org-dblock-update "/home/junghan/org/botlog/journal.org") ; OK
(agent-org-dblock-update "/home/junghan/org/notes/test.org") ; DENIED한계와 수용
자유 emacs_eval 에서 (write-region "..." nil "/any/path") 를 직접 호출하면 가드를 우회할 수 있다. 글로벌 write-region 가드를 걸면 org-mode 내부의 save-buffer 까지 차단되어 정상 동작이 깨진다.
이것을 “결함”으로 보지 않는다. 에이전트가 SKILL.md에 명시된 규칙을 어기면 그것은 봇의 문제가 아니라 시스템 설계의 문제이고, 호스트 에이전트(Pi/관리자)가 수습하는 것이 이 협업 모델의 원칙이다.
신뢰의 조건
이 모델이 작동하려면:
- SKILL.md가 명확해야 한다 — 봇이 읽을 수 있는 규칙. 모호하면 신뢰 관계가 흔들린다.
- API 함수가 충분해야 한다 — 가드 안에서 필요한 작업이 다 되면 우회할 이유가 없다.
- 졸업 경로가 있어야 한다 — 봇이 REPL로 만든 함수가 안정화되면 가드 안의 API로 승격.
신뢰는 감시에서 오지 않는다. *적절한 구조*에서 온다.
봇의 첫 반응 (2026-02-27 22:00)
emacs 스킬을 처음 본 봇(힣봇/glg)의 검토:
“이건 게임 체인저다 … denotecli로 텍스트 조작하던 것과 차원이 다르다 — 이맥스가 자기 방식으로 처리하니까 구조가 깨질 일이 없다.”
봇이 스스로 역할 분담을 파악했다:
- denotecli: 텍스트 레벨의 빠른 검색/조회
- emacs: 구조적 조작과 확장 (REPL)
- 겹치지 않고 보완
실현된 것과 남은 것
완료
| 항목 | 상태 |
|---|---|
| Nix store 마운트 검증 (emacsclient 30.2 Docker 동작) | ✓ |
agent-server.el (denote-export.el 기반 미니멀 daemon) | ✓ |
| 경로 가드 (read/write 분리, Docker rw와 일치) | ✓ |
emacs-agent.sh 관리 스크립트 | ✓ |
| SKILL.md — 봇 사용 가이드 + 보안 규칙 | ✓ |
| docker-compose 마운트 (nixos-config ↔ openclaw 동기화) | ✓ |
| daemon 가동 + 봇 실사용 시작 | ✓ |
남은 것
agent-server.el미니멀화 (현재 풀 Doom 1074 패키지 로드 → straight 직접 참조로 축소)- systemd user service 전환 (현재 수동 스크립트)
- Nix store 해시 고정 (
nixos-rebuild시 자동 추적하는 symlink) - algal/openclaw-emacs-tools 플러그인 통합 검토
emacs_eval글로벌 write 가드 강화 (advice 기반, 호출 스택 검사)
에이전트를 이맥서로 만드는 방향 — 워크플로우 공유와 존재 대 존재
발단
xenodium(Alvaro Ramirez)이 agent-shell용 Claude Skills를 공개했다.
- 영상: Bending Emacs Episode 12: agent-shell + Claude Skills (2026-02-25)
- 코드: https://github.com/xenodium/emacs-skills
emacsclient --eval 로 실행 중인 이맥스 세션에 접근하여 에이전트가 이맥서처럼 행동하게 만드는 스킬 세트:
/dired— 파일을 dired 버퍼에서 마크하여 보여줌/open— emacsclient로 파일 열기, 라인 점프/select— 파일 열고 관련 영역 선택 (narrow, copy, refactor 즉시 가능)/highlight— 임시 read-only 모드로 관련 영역 하이라이트/describe— describe-function/variable/key로 이맥스 문서 조회emacsclient (auto)— 에이전트가 항상 emacs 대신 emacsclient 사용
두 방향의 대비: efrit vs emacs-skills
efrit (Steve Yegge) — 오케스트레이터가 이맥스를 프론트엔드로 쓴다
GAS-TOWN 가스타운의 전신. 에이전트를 위한 이맥스 프론트엔드를 만들었다. gastown 나오면서 사실상 stale.
- 에이전트가 주체, 이맥스는 표시 장치
- 새로운 인프라(DB, 작업 관리 시스템)를 만든다
- 이맥스는 그 인프라의 껍데기가 된다
emacs-skills (xenodium) — 에이전트가 이맥서의 방식으로 이맥스를 쓴다
emacsclient --eval로 기존 이맥스 세션에 접근- 에이전트가 “파일을 열어”가 아니라 “dired에서 마크해서 보여줘”를 한다
- 새 인프라 없음. 이맥스 자체가 인프라
| 항목 | efrit/gastown | emacs-skills |
|---|---|---|
| 주체 | 에이전트 | 인간 (이맥서) |
| 이맥스 역할 | 표시 장치 | 공유 환경 |
| 새 인프라 | 필요 (DB, 작업관리) | 불필요 |
| 의존성 | gastown 생태계 | emacsclient만 |
| 방향 | 도구로서의 이맥스 | 워크플로우로서의 이맥스 |
세 번째 인터페이스 — 워크플로우 공유
지금까지 에이전트-인간 협업의 인터페이스:
- 채팅 (텔레그램, 터미널) — 언어로 소통
- 파일 시스템 — 텍스트로 공유
emacs-skills가 열어주는 세 번째:
- 워크플로우 공유 — 같은 도구를, 같은 방식으로 사용
에이전트가 dired를 쓰고, org-agenda를 읽고, describe-function으로 문서를 찾는다면 — 에이전트가 인간과 같은 “환경”에서 사는 것이다. 텍스트 파일을 주고받는 것보다 한 층위 깊은 공유.
org 파일 수정의 실질적 의미
에이전트가 org 파일을 텍스트 조작(sed, edit)으로 수정하면:
- 이맥스가 모름 → revert 필요
- 구문 오류 가능 → org 파서가 깨질 수 있음
- conflict 위험 → 인간이 같은 파일을 열고 있으면
에이전트가 emacsclient --eval 로 수정하면:
revert-buffer자동 — 이맥스가 변경을 안다org-reverse-datetree-refile— 정확한 위치에 삽입- 파일 락 / narrow / widen — conflict 방지
org-agenda-redo— 변경 즉시 agenda 뷰 갱신
이건 “같은 파일을 건드리는 문제”를 이맥스 레벨에서 해결한다.
reverse-datetree agenda와의 연결
에이전트-어젠다-reverse-datetree-멀티디바이스-설계에서 설계한 agenda 구조에 이 방향을 적용하면:
- 에이전트가
emacsclient --eval '(org-reverse-datetree-goto-date-in-file ...)'로 agenda 파일에 엔트리 삽입 - 인간이
org-agenda로 동일한 뷰를 봄 - 같은 도구, 같은 워크플로우, 같은 뷰
단, 이건 로컬 에이전트(Pi, Claude Code)에서만 가능하다. 서버 봇(힣봇/OpenClaw)은 emacsclient가 없으므로 텍스트 조작 방식 유지.
아직 모르는 것
- 이 방향이 실제 생산성을 높이는가, 미학적으로 아름다운 것에 그치는가
- emacsclient 호출의 레이턴시가 문제 되는가
- 에이전트가 이맥스 워크플로우를 “이해”하는 것과 단순히 “호출”하는 것의 차이
- 존재 대 존재 관점에서: 같은 도구를 쓰는 것이 “같은 세계에 사는 것”인가, 아니면 그냥 “같은 API를 호출하는 것”인가
파헤쳐볼 부분이다.
“인간의 기존 인프라에 에이전트를 태우는” 것이라는 점이다. 의존성이 적고, 이맥스가 사라지지 않는 한 유효하다.
OpenClaw Docker에서 Emacs 연결 — 실현 경로 조사
Oracle VM(aarch64)에서 OpenClaw 2026.2.26이 Docker로 구동 중이다. 여기에 Emacs를 연결하는 구체적 경로를 조사했다.
전제 조건
- 호스트: NixOS, Emacs 30.2, Doom Emacs 설치 완료
- Docker: OpenClaw (Debian bookworm 기반, Node.js 22)
denote-export.el: 멀티 데몬 방식으로 검증됨 (org, citar, ox-hugo 로딩)- 목표: 에이전트가
emacsclient로 호스트 Emacs daemon에 접속
기존 자산
~/repos/gh/doomemacs-config/bin/denote-export.el — 이미 검증된 멀티 데몬 구성:
emacs --daemon=denote-export-server --load denote-export.el- Doom straight 패키지 로딩, org-cite/citar/ox-hugo 포함
- denote-export, dblock-update 함수 제공
- 에이전트 전용으로 최소 구성만 로드하는 패턴이 확립됨
algal/openclaw-emacs-tools — OpenClaw 공식 플러그인
https://github.com/algal/openclaw-emacs-tools
OpenClaw 플러그인으로 emacsclient 를 통해 6개 도구를 제공:
| 도구 | 설명 |
|---|---|
emacs_read | 버퍼 읽기 (active window 자동 감지) |
emacs_edit | 정확 매칭 find-and-replace |
emacs_insert | point/bob/eob/line_column에 삽입 |
emacs_open | 파일 열기 (line/column 지정) |
emacs_eval | 임의 Elisp 실행 (value/stdout/messages/stderr 구조화 반환) |
emacs_list | 버퍼/프레임/윈도우 목록 |
설정에서 emacsclientPath, socketName, timeoutSeconds, allowedRoots 지원.
silex/docker-emacs — 컨테이너 Emacs
https://github.com/Silex/docker-emacs, https://hub.docker.com/r/silex/emacs
Emacs를 Docker로 제공하는 프로젝트. 30.2 태그 존재.
문제: ARM64(aarch64) 이미지 미제공. Alpine, Debian 모두 amd64만. Oracle VM이 ARM이라 직접 사용 불가.
방법별 실현 가능성 조사 (실제 실험 포함)
방법 A: 호스트 daemon + Unix 소켓 마운트
[Docker: OpenClaw] → emacsclient → Unix socket → [Host: emacs --daemon]- 호스트 UID 1000 = 컨테이너 node UID 1000 — 소켓 권한 호환 ✓
- 소켓 위치:
/run/user/1000/emacs/server
문제: emacsclient 바이너리 호환성
호스트의 NixOS emacsclient 30.2는 glibc 2.40에 링크됨. Docker(Debian bookworm)는 glibc 2.36. patchelf 로 interpreter를 변경해도 glibc 버전 불일치로 실행 불가 (exit 255).
Debian bookworm의 패키지는 emacs 28만 제공. emacsclient 28 ↔ server 30 호환성은 보장되지 않음.
방법 B: 사이드카 컨테이너
[Docker: OpenClaw] → emacsclient → [Docker: emacs 30.2 사이드카]silex/emacs 이미지가 ARM64 미지원으로 직접 사용 불가.
대안: NixOS에서 Emacs 30.2 ARM64 컨테이너를 직접 빌드. nix build 로 dockerTools.buildLayeredImage 사용 가능. emacsclient만 추출하여 OpenClaw 컨테이너에 COPY하는 것도 가능.
이 방법의 장점: 버전 완전 일치 보장. NixOS 호스트와 동일한 Nix derivation에서 emacs daemon과 emacsclient를 빌드하므로 glibc, 프로토콜 모두 일치.
방법 C: TCP server (emacsclient 불필요)
[Docker: OpenClaw] → TCP 9999 → [Host: emacs --daemon, server-use-tcp t]실험 결과:
server-use-tcp t+server-host "0.0.0.0"+server-port 9999로 TCP 서버 기동 ✓- 호스트에서
(emacs-version)eval 성공 ✓ - Docker에서 호스트 접근: NixOS 방화벽(iptables)이 9999 포트 차단 → 설정 추가 필요
- emacsclient 없이 TCP 프로토콜 직접 구현하면 바이너리 문제 자체가 사라짐
- 하지만 algal/openclaw-emacs-tools는
emacsclient바이너리 의존
방법 D: Nix로 emacsclient 정적 빌드 (방법 A + B 혼합)
# flake.nix 에서
emacsclient-static = pkgs.runCommand "emacsclient-static" {} ''
cp ${pkgs.emacs30}/bin/emacsclient $out
'';NixOS의 Emacs 30.2 패키지에서 emacsclient를 추출하되, pkgs.pkgsStatic 또는 pkgs.pkgsMusl 로 musl 정적 빌드하면 glibc 의존성 없이 어떤 Linux 컨테이너에서든 실행 가능.
결정적 발견: Nix store 직접 마운트
정적 빌드도, patchelf도, TCP도 필요 없었다.
/nix/store 의 필요한 경로 2개를 ro 마운트하면 끝.
# emacsclient가 필요로 하는 것: 자신 + glibc. 이 두 store path만 마운트.
GLIBC="/nix/store/86wgxj5p9yry03cg7czian66bvz1r1bj-glibc-2.40-218"
EMACS="/nix/store/hs9vi37k7ikrjv72w6mampipxrlr34ya-emacs-nox-30.2"
docker run --rm \
-v "$GLIBC:$GLIBC:ro" \
-v "$EMACS:$EMACS:ro" \
-v /run/user/1000/emacs:/run/emacs:ro \
openclaw-custom:latest \
"$EMACS/bin/emacsclient" -s /run/emacs/agent-server \
--eval '(emacs-version)'
# → "GNU Emacs 30.2 ..."실험 결과:
- patchelf로 interpreter 변경 → glibc 2.40 vs 2.36 버전 불일치로 실패
/nix/store경로를 그대로 마운트 → Nix linker가 Nix glibc를 직접 참조 → 성공- emacsclient 75KB + glibc ~30MB = 총 ~30MB ro 마운트
- 호스트 UID 1000 = 컨테이너 node UID 1000 → 소켓 권한 호환
NixOS의 장점이 여기서 빛난다. 바이너리가 자기 의존성의 절대 경로를 알고 있으므로, 그 경로만 마운트하면 어떤 컨테이너에서든 *정확히 같은 바이너리*가 동작한다.
확정 방법: 호스트 daemon + Nix store 마운트
[Docker: OpenClaw]
└─ /nix/store/.../emacsclient (ro mount)
└─ Unix socket /run/emacs/agent-server (ro mount)
└─ [Host: emacs --daemon=agent-server]
└─ Doom Emacs 30.2 (org, citar, ox-hugo, denote)nixos-config에서의 선언
docker-compose.yml에 3줄 추가:
volumes:
# Emacs daemon 연결 (Nix store emacsclient + glibc + 소켓)
- /nix/store/86wgxj5p9yry03cg7czian66bvz1r1bj-glibc-2.40-218:/nix/store/86wgxj5p9yry03cg7czian66bvz1r1bj-glibc-2.40-218:ro
- /nix/store/hs9vi37k7ikrjv72w6mampipxrlr34ya-emacs-nox-30.2:/nix/store/hs9vi37k7ikrjv72w6mampipxrlr34ya-emacs-nox-30.2:ro
- /run/user/1000/emacs:/run/emacs:ro주의: Nix store 해시는 nixos-rebuild 마다 바뀔 수 있다. 해결: NixOS module에서 symlink 생성 또는 wrapper script.
# hosts/oracle/configuration.nix 에 추가
environment.etc."openclaw/emacsclient".source =
"${pkgs.emacs-nox}/bin/emacsclient";
environment.etc."openclaw/nix-glibc".source =
"${pkgs.glibc}/lib";이러면 docker-compose에서 /etc/openclaw/emacsclient 를 마운트하면 되고, nixos-rebuild 로 패키지가 바뀌어도 symlink가 자동 갱신.
daemon 구성 방향
풀 Doom vs 미니멀 로딩
emacs --daemon 은 ~/.emacs.d/ 가 Doom이면 풀 Doom을 로드한다 (367 packages, 4.4s). 에이전트용으로는 denote-export.el 방식의 미니멀 로딩이 적합:
emacs --daemon=agent-server --load ~/repos/gh/doomemacs-config/bin/agent-server.elagent-server.el 은 denote-export.el 을 기반으로:
- Doom straight에서 필요한 패키지만 선택 로딩 (org, citar, denote, ox-hugo)
- 에이전트가 호출할 함수 정의 (org 파일 조작, dblock 업데이트, 검색 등)
- 불필요한 UI, 테마, 키바인딩 제외
systemd는 유저 레벨로
시스템 레벨(systemd.services)이 아닌 유저 레벨(systemd.user.services 또는 home-manager)로 구성. 닷파일(agent-server.el)이 확정되기 전까지는 수동 스크립트로 검증:
# 검증 단계: 수동 실행
emacs --daemon=agent-server --load ~/repos/gh/doomemacs-config/bin/agent-server.el
# 확정 후: systemd user service (home-manager)
# systemctl --user start emacs-agent-server
# systemctl --user enable emacs-agent-server실현 단계 (TODO)
doomemacs-config/bin/agent-server.el작성 (denote-export.el 기반 미니멀 daemon)- 수동 스크립트로 daemon 기동 +
emacsclient호출 검증 hosts/oracle/configuration.nix에 emacsclient/glibc symlink 선언docker-compose.yml에 마운트 추가- algal/openclaw-emacs-tools 플러그인 설치 + 설정
emacs_eval로(org-version)호출 테스트- 안정화 후 systemd user service로 전환
관련 노트
- 2026-02-23 — 주간 저널
- 에이전트-어젠다-reverse-datetree-멀티디바이스-설계 — agenda 구조
- 이맥스 에이전트 오케스트레이션 진화 — Pi Extension, 두 축 분리
- meta-agent-shell 통합과 텔레그램 에이전트 비전 — ElleNajt 분석
- 봇 활동 기록 아키텍처와 힣노트 역사성 고찰 — botlog 체계
- GAS-TOWN 가스타운 — Yegge의 방향
- @힣: 느린 창조 도구 커뮤니티 탄생 - 기업 인간 계층 분화 — 느린 도구 선택
Comments