Skip to main content

nixos-config

nixos-config 오퍼레이터의 운영 면 — oracle/nuc/laptop/thinkpad 멀티 디바이스 NixOS + oracle에 사는 OpenClaw 봇 런타임을 실제로 손볼 때. AGENTS.md가 '현재 상태', NEXT.md가 '할 일', ROADMAP.md가 '이력'을 담는다면 이 스킬은 그 문서들이 front-load 못 하는 운영 반사신경을 담는다: 자리(디바이스) 인식 먼저, run.sh 엔트리, oracle/openclaw 분리 원칙, rebuild/rollback, OpenClaw 업그레이드(Dockerfile FROM 한 줄 + doctor read-only), 공개면 변경 시 서빙 URL 전수 검수(caddy 8-세트 + CF Tunnel + Tailscale Serve), 봇 스킬 심볼릭 배포, 커밋/스탬프/git-hooks 규율. 트리거: 'nixos-config', 'nixos-rebuild', 'rebuild', 'switch', 'rollback', 'flake update', 'oracle', 'openclaw', '봇 업그레이드', 'caddy', 'claw', 'control ui', 'geworfen', 'authelia', 'agenda 안돼', '디바이스', 'run.sh', '롤백', 'nixos 정리', 'tailscale', '터널', '안드로이드 앱', '앱이 안 붙어', 'trustedProxies', '403', '페어링'.

Jump to install

Source facts

Repository
junghan0611/nixos-config
Last source activity
September 13, 2026 at 03:19
Detected SKILL.md language
Korean
Stars
3
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
nixos-config
description
nixos-config 오퍼레이터의 운영 면 — oracle/nuc/laptop/thinkpad 멀티 디바이스 NixOS + oracle에 사는 OpenClaw 봇 런타임을 실제로 손볼 때. AGENTS.md가 '현재 상태', NEXT.md가 '할 일', ROADMAP.md가 '이력'을 담는다면 이 스킬은 그 문서들이 front-load 못 하는 운영 반사신경을 담는다: 자리(디바이스) 인식 먼저, run.sh 엔트리, oracle/openclaw 분리 원칙, rebuild/rollback, OpenClaw 업그레이드(Dockerfile FROM 한 줄 + doctor read-only), 공개면 변경 시 서빙 URL 전수 검수(caddy 8-세트 + CF Tunnel + Tailscale Serve), 봇 스킬 심볼릭 배포, 커밋/스탬프/git-hooks 규율. 트리거: 'nixos-config', 'nixos-rebuild', 'rebuild', 'switch', 'rollback', 'flake update', 'oracle', 'openclaw', '봇 업그레이드', 'caddy', 'claw', 'control ui', 'geworfen', 'authelia', 'agenda 안돼', '디바이스', 'run.sh', '롤백', 'nixos 정리', 'tailscale', '터널', '안드로이드 앱', '앱이 안 붙어', 'trustedProxies', '403', '페어링'.
user_invocable
true
# nixos-config — 오퍼레이터의 운영 면 Repo: `~/repos/gh/nixos-config` (심볼릭 → `/home/junghan/nixos-config`, 같은 real dir). 멀티 디바이스 NixOS(`oracle`/`nuc`/`laptop`/`thinkpad`) + **oracle에 사는 OpenClaw 봇 런타임**을 운영하는 자리. `AGENTS.md`(현재 상태)·`NEXT.md`(할 일)·`ROADMAP.md`(이력)이 *무엇/언제*를 담는다면, 이 스킬은 그 문서들이 못 담는 **매번 다시 당하는 운영 반사신경**을 담는다. > ⚠️ 이건 generic NixOS 문서가 아니다. **이 repo 오퍼레이터의 핸드북**이다. > 실사용자(가족 포함)가 oracle 봇에 의존한다 — oracle 작업은 service reliability 작업이다. ## 0. 자리 인식 먼저 (correctness starts here) 어느 디바이스인지 모르면 아무것도 하지 마라. SessionStart hook이 `device=`/`time_kst=`를 준다. 안 보이면: ```bash cat ~/.current-device TZ='Asia/Seoul' date '+%Y%m%dT%H%M%S' ``` `run.sh` normalization: `oracle-nixos` → `oracle` (첫 `-` 앞 토큰 = flake profile 이름). **핵심 분리 원칙**: `oracle`이 아니면 OpenClaw를 볼 필요가 없다. oracle/openclaw 작업이 아니면 `ORACLE.md`를 열지 마라 — `AGENTS.md`만으로 충분하다. ## 1. 문서 라우팅 — 뭘 열까 | 작업 맥락 | 펼칠 문서 | |---|---| | **OpenClaw 런타임 자체를 만짐** (봇 설정·heartbeat·cron·채널·배달·스키마) | → **`openclaw` 스킬** (`.claude/skills/openclaw/` — 같은 리포, 별개 스킬). 기억으로 찍지 말고 그 버전의 사실로 만지는 절차 | | OpenClaw 주제별 소스 좌표 (`src/…:줄`) | `docs/openclaw-reference-map.md` (v2026.8.2 기준, 미확인은 미확인이라 적힘) | | oracle 디바이스 또는 OpenClaw 관련 | `ORACLE.md` (ownership·model routing·env/secret·업그레이드·함정) | | thinkpad 로컬 AI (Ollama) | `THINKPAD.md` | | nuc/laptop 일반 NixOS | `AGENTS.md` + 표준 NixOS 흐름 | | OpenClaw 함정 카탈로그 | `docs/openclaw-gotchas.md` | | 지금 무슨 상태인가 | `AGENTS.md` (날짜 박히면 → ROADMAP로 이관 신호) | | 앞으로 할 일·후속 검증 | `NEXT.md` (끝난 항목 지우고 새 후속 추가) | | 어떻게 여기까지 왔나(버전·결정 이력) | `ROADMAP.md` | | 라이브 봇 런타임 SSOT | `~/openclaw/` (config/auth/workspaces — **커밋 금지**) | > **OpenClaw 축은 이 스킬이 지지 않는다.** 디바이스·rebuild·배포·커밋은 여기, > **런타임 내부는 `openclaw` 스킬**이다. 일부러 나눠놨다 — 한쪽을 안다고 다른 쪽을 > 안다고 생각하는 순간 옛 기억으로 라이브를 만지게 된다. ## 2. run.sh — 오퍼레이터 엔트리 사람+에이전트 공용 인터페이스. 이미 `run.sh` 경로가 있는 작업이면 중복 만들지 말고 확장하라. 범위: flake update, rebuild/switch/rollback, cleanup, Oracle service helper, OpenClaw tunnel/restart/status/pairing, skill deploy(`k)`). 상세 oracle helper는 `ORACLE.md` §7. `br` 쓰지 마라. rigid tracker 대신 **agenda 스탬프**를 쓴다(이 repo 선호). ## 3. 표준 rebuild / rollback (nuc/laptop/thinkpad) ```bash sudo nixos-rebuild switch --flake .#<profile> # profile = oracle|nuc|laptop|thinkpad ``` 문제 시 rollback: `run.sh` 또는 `sudo nixos-rebuild switch --rollback`. NixOS는 generation이 롤백 표면이다. 디스크 여유 부족하면(oracle 특히) 정리부터 — 먼저 `run.sh c)` safe로 재고, 여유가 부족할 때만 `run.sh C)` deep을 고른다. ## 4. oracle / openclaw 작업 (safety-critical) 라이브 truth = `~/openclaw/`. 공개 백업/레퍼런스 = `docker/openclaw/`, `docker/*`. 절대 secret/auth를 공개 repo로 새게 하지 마라. 자세한 건 `ORACLE.md`. 자주 쓰는 반사신경만: - **업그레이드는 두 종류다 — 먼저 어느 쪽인지 판정하라.** - **패치/마이너 (`X.Y-1 → X.Y-2` 등) = Dockerfile `FROM` 한 줄 bump**: `~/openclaw/Dockerfile` + `docker/openclaw/Dockerfile` 둘 다 `ghcr.io/openclaw/openclaw:X → :Y` + 업글 로그 주석 → 재빌드 → `up -d --force-recreate`. 플러그인은 stock(번들)이라 자동으로 따라옴. - **메이저 hop (7.1 → 8.1 같은 한 달치) = 한 줄 bump가 아니다.** 새 이미지는 **옛 state를 거부**한다 (agent DB v1 → v19). 게이트웨이를 **끄고** 오프라인으로 마이그레이션한 뒤 올린다: 백업 → 정지 → `doctor --fix` **직렬 반복**(lease를 잃으면 남은 DB를 건너뛴다) → 기동 → 검수. 실측 39분. 절차·함정 전문 = `docs/openclaw-gotchas.md` "8.1 컷오버" (2026-08-31). - **~~업글 후 `--fix` 금지~~ → 2026-08-31 폐기.** 그 금지는 gemini가 `google-gemini-cli/`였을 때의 방어였고, 8/27 Copilot 이관으로 겨눌 대상이 사라졌다. **8.1부터 `doctor --fix`는 필수**다 — agent DB 스키마를 올리는 건 doctor뿐이다. 단 **반드시 게이트웨이를 끈 상태**에서(라이브 writer가 있으면 "stopped-writer maintenance" 로 전부 skip), 그리고 **끝난 뒤 6봇 prefix를 재확인**한다. - **6봇 모델 prefix 전수 재확인** (gemini는 `github-copilot/gemini-3.7-flash` — `google*` 아님), claude-cli 봇 Anthropic auth GREEN, memory 4096d, `fallbacks` 빈 상태, catch-all 1번(8.1부터 `modelPolicy.allow` 배열 첫 항목 = `openai/gpt-5.6-terra`). - **모델은 claude-cli 봇이면 카탈로그 등록만으로 태운다** — `agentRuntime claude-cli`는 구독 API에서 모델을 동적 해결(예: Sonnet 5 = `anthropic/claude-sonnet-5` + primary, 재빌드 불필요). 서빙 미보장 모델을 primary로 박지 말 것(auto-fallback catch-all이 정체성 훼손). - **gemini는 DOWN 아니다 (2026-08-27~)** — 서빙 레일 = **`github-copilot/gemini-3.7-flash`**(Copilot 구독). 옛 "403 → agy 이관 대기 → DOWN 유지" 반사신경은 **화석이다**. Google 구독·gemini-cli·agy 안 쫓는다. api-key(`google/`) 폴백은 여전히 금지 — 그 키는 나노바나나 이미지 전용. ## 5. 반사신경 — 다시 당하지 말 것 - **공개면 변경 = 서빙 URL 전수 검수**: `docker/caddy/Caddyfile` 이든 gateway 설정이든 공개면을 건드렸으면 **caddy 8-세트 + 밖의 3개**를 한 번에 확인한다. 하나만 보고 넘기지 마라. (`docs/openclaw-gotchas.md` "caddy 변경 = 8-세트 검수" 참조) ```bash for h in agenda analytics ax claw comments forge ha map; do printf '%-28s %s\n' "$h" "$(curl -sS -m 12 -o /dev/null -w '%{http_code}' https://$h.junghanacs.com/)" done curl -sS -m 12 -o /dev/null -w 'aionsclubs %{http_code}\n' https://aionsclubs.org/ # CF Tunnel curl -sS -m 10 -o /dev/null -w 'tailnet %{http_code}\n' https://oracle.tailb0e905.ts.net/ # Tailscale Serve ``` **기대값을 알고 봐야 한다 — 200 이 아닌 게 셋이고 전부 정상이다** (2026-09-08 실측): | 기대 | 호스트 | 왜 | |---|---|---| | 200 | agenda · analytics · ax · forge · ha · aionsclubs · tailnet | | | **302** | claw · map | authelia forward_auth 게이트. 설계대로 | | **404** | comments | remark42 는 루트에 페이지가 없다. **장애 아님** — `/api/v1/ping` 이 `pong` 이면 정상 | caddy 밖의 두 경로(`aionsclubs.org` CF Tunnel, `oracle.tailb0e905.ts.net` Tailscale Serve)는 caddy 를 안 타므로 Caddyfile 검수에서 빠지기 쉽다. **세트에 넣어라.** - **계획 재부팅 뒤에는 재기동만 보고 끝내지 마라**: `systemctl --failed`·`docker ps`로 실패/health를 먼저 보고, `emacsclient -s server --eval '(+ 1 1)'`로 agenda의 호스트 socket을 확인한다. 이어 위 **8+3 URL 세트**와 `comments /api/v1/ping`, `./scripts/turnwatch.sh 24`를 통과해야 복구 완료다. caddy 443 선점과 emacs socket ownership은 과거에 재부팅에서만 드러난 레이스다. - **`/health` 200 은 게이트웨이가 멀쩡하다는 증거가 아니다**: `/health` 는 무인증 라우트라 proxy attribution·토큰 검사를 **건너뛴다**. 그래서 `/health` 는 200 인데 `/` 는 403 인 비대칭이 나오고, 이 비대칭 자체가 "네트워크·프로세스는 살아있고 **인증 계층**에서 막혔다"는 진단이다. 앱/UI 가 못 붙을 때 `/health` 만 보고 "게이트웨이 정상"이라 결론내지 마라 — **인증이 걸리는 `/` 를 같이 찍고, 403 이면 본문(JSON `type`)을 읽어라.** 본문이 원인을 이름으로 말해준다(`proxy_attribution_required` 등). - **도커 네트워크가 갈리면 `gateway.trustedProxies` 가 조용히 화석이 된다**: gateway 는 두 네트워크에 붙어 있다 — `proxy`(`172.18.0.0/16`, caddy 경로) 와 `openclaw-config_default`(`172.26.0.0/16`, 호스트 루프백 포워딩 = tailscale serve·SSH 터널). 후자는 compose IPAM으로 고정했고 `trustedProxies` 는 **`172.18.0.0/16` + `172.26.0.1/32`**다 (2026-09-13 실측). 남아 있을 수 있는 옛 `openclaw_default`/`172.19.0.0/16`를 다시 믿지 마라. compose project/default network를 다시 만들었으면 추측하지 말고 `docker inspect openclaw-gateway`와 컨테이너가 보는 소스 IP(`/proc/net/tcp`를 폴링하며 요청을 흘린다 — 전문은 gotchas)를 재측정한다. 넓히지 말고 `/32`로 좁게 더하고, **config 변경이므로 restart로 충분**. ⚠️ **대가를 알고 하라**: 도커 NAT 는 tailscale serve 와 호스트 루프백을 **같은 IP 로 뭉갠다.** 그 IP 를 신뢰하는 순간 `run.sh t)` SSH 터널로 Control UI 를 보던 길이 403 이 된다. `/32` 로도 분리되지 않는다. 대체는 tailnet URL 직행(thinkpad 도 tailnet 에 있다). - **claw.junghanacs.com = 인증 뒤에 원격 셸이 있는 유일한 vhost**: OpenClaw Control UI 공개면. 자물쇠 3겹(Authelia forward_auth → gateway token → device pairing)을 전부 유지한다. **`gateway.auth.mode`를 `trusted-proxy`로 바꾸지 마라** — gateway가 `proxy` 도커 네트에 붙어 있어 같은 네트 컨테이너가 18789로 직결(Authelia 우회)하므로, 그 순간 우회가 곧 인증 우회가 된다. Authelia claw 규칙은 **bypass → operator one_factor → deny catch-all 3단**(default가 one_factor라 catch-all 없으면 family가 통과). WS 재연결이 조용히 죽으면 F5. (`docs/openclaw-gotchas.md` claw 항목) - **ax.junghanacs.com = 첫 static vhost(관리 대상)**: 기존 6개는 `reverse_proxy`지만 ax는 백엔드 없이 caddy `file_server`가 `/srv/ax`(호스트 `docker-data/ax` ro 마운트)를 직접 서빙. web root는 junghan0611 repo `apply/ax make publish`가 채운다(재시작 불요, 즉시 라이브·leak gate 유일 관문). **compose 볼륨 추가/제거는 `restart` 아니라 `up -d --force-recreate`**. 향후 umami/remark는 정본에 스니펫으로(caddy 주입 금지). ax 요청은 이 레인이 대응. - **agenda 000 ≠ caddy**: `agenda.junghanacs.com`(geworfen)은 호스트 emacs `server` 데몬에 의존한다. 데몬 hang이면 caddy와 무관하게 agenda 000. `emacsclient -s server --eval '(+ 1 1)'` 타임아웃이면 데몬이 근인. 복구는 **geworfen 담당자에게 entwurf 핸드오프** — 호스트 데몬을 nixos-config에서 직접 만지지 마라. - **Caddyfile 편집 후 reload 아니라 `docker restart caddy`**: single-file bind-mount inode 함정 — atomic rename이 새 inode를 만들어 `caddy reload`가 "unchanged"로 무시한다. 재시작 후 `docker exec caddy grep <domain> /etc/caddy/Caddyfile`로 컨테이너가 새 내용을 보는지 확인. - **봇 스킬은 심볼릭 배포** — workspace 스킬을 sibling repo SSOT(예: butlercli)로 절대 심볼릭. 봇이 고친 게 SSOT로 흐르고 SSOT 수정이 봇에 즉시 반영(redeploy 불필요). 이중 마운트 덕에 `/home/junghan/...` 절대경로가 host·container 양쪽 resolve. (`NEXT.md` "스킬 심볼릭 배포") - **디스크 보수적으로** — oracle storage 빠듯. 압박의 첫 수는 `run.sh c)` safe(14일 generation 보존, docker·pnpm 제외) → 여유를 재측정하는 것이다. `run.sh C)` deep/docker prune은 그 다음 선택지다(`docker system df` 명목치 ≠ 실 회수량, builder prune이 본 회수원). safe의 `--yes`는 스크립트 확인만 넘긴다. root GC·journal 같은 `sudo` 단계는 비대화형에서 건너뛸 수 있으므로, 출력의 실패를 확인하고 필요하면 visible terminal에서 sudo 인증 후 처리한다. 현재 journal은 선언적 1GiB 상한으로 사후 진단 여지를 보존한다. - **restart vs recreate — "무엇을 바꿨느냐"로 가른다** (매번 까먹는 지점): - **env/mount 변경 → `up -d --force-recreate`**. `docker compose restart`는 기존 env 재사용, recreate만 새 env·볼륨 픽업. - **config 파일(`~/openclaw/config/openclaw.json`) 변경 → `docker compose restart openclaw-gateway`로 충분.** restart도 gateway 프로세스·manager cache·Node compile state를 콜드 리셋한다. config-only에 recreate는 과하다(느리고 불필요하게 더 깊은 콜드). recreate는 env/mount일 때만. - **restart/recreate 후 memory prewarm** — 콜드 첫 `memory_search`는 세션 전수 스캔(glg 808 files/211MB, ARM)으로 15s 하드타임아웃(`memory-core` tools.ts 하드코딩, config로 못 늘림)에 걸릴 수 있다. gateway healthy 뒤 각 봇에 `openclaw agent --agent <id> --session-key "agent:<id>:prewarm-$(date +%s)" --message '기억 검색' --timeout 60`을 (텔레그램 발송 없이) 한 번 돌려 **봇이 콜드를 대신 맞게** 하면 실제 유저는 웜만 만난다. 1차 방어는 인덱스 정비(dirty:no 유지 — family-turns 스킬 memory 섹션): 정비돼 있으면 콜드도 15s를 버틴다(2026-07-16 실측, 정비 후 콜드 3연속 성공). upstream PR로 timeout 늘리는 건 안 한다 — 운영으로 커버(GLG 결정). ## 6. 커밋 / 릴리즈 규율 - 커밋 전 `commit` 스킬, 릴리즈 전 `tag-release` 스킬. 로그 깨끗하게(“Generated with Claude”· Co-Authored-By 금지). 에이전트는 commit workflow에서만 커밋, **push는 GLG**. - **git-hooks 안전벽** (global `core.hooksPath`): 공개 repo(`junghan0611/*`·`junghanacs/*`) added line의 **정체성 용어** + 모든 repo의 **secret**를 차단. nixos-config는 공개 repo다 — 실제 이메일/비번/토큰을 커밋 다이어그램에 넣지 마라(실파일은 gitignore, 템플릿은 플레이스홀더). `AGENT_ALLOW_UNSAFE_COMMIT=1`·`--no-verify`·`core.hooksPath` 변경 **금지**(GLG 명시 예외만). - 막히면: hook 출력 읽고 → 다이어그램에서 용어/secret 제거(private은 `PRIVATE.md`/`.env.local` 또는 플레이스홀더) → 재스테이지 → 재시도. false positive 같으면 멈추고 hook 출력 그대로 보고. - push 후 agenda 스탬프(commit/release 스킬 규약). ## 7. 영속 사실이 사는 곳 (썩는 문서 말고) | 사실 | 집 | |---|---| | 현재 운영 상태 | `AGENTS.md` | | 다음 할 일·후속 검증 | `NEXT.md` (완료분은 지우고 ROADMAP으로 흘림) | | 버전·업그레이드·운영 결정 이력 | `ROADMAP.md` | | oracle/openclaw 핸드북 | `ORACLE.md` | | 반복되는 운영 함정 | `docs/openclaw-gotchas.md` | | 라이브 봇 런타임 change history | `~/openclaw/README.md` (커밋 안 되는 SSOT) | 이 스킬 자체(`.claude/skills/nixos-config/SKILL.md`)는 **양쪽 하네스가 읽는다**: Claude Code는 `.claude/skills/`를 네이티브로, pi는 `.pi/settings.json`의 `["../.claude/skills"]`로. 스킬을 고치면 두 하네스에 동시에 반영된다 — 커밋해서 다른 기기로도 펼친다.
View on GitHub