macmini host setup
본 페이지의 위치: 비전 = “macmini 10대 × customer 직원 1-2명/customer 시스템 개선” (D-dev-platform-1). 각 customer 머신 셋업의 SSOT. 신규 customer onboard 시 본 페이지 위에서 자동화 CLI (B-axe-host-bootstrap-cli) 가 진행할 task의 인간이 읽을 reference.
운영자 ritual: 본 페이지 변경 시 = 운영 실패 / 함정 발견 / 신규 layer 추가의 trigger. 모든 macmini 변경의 SSOT.
13 layer + 본 머신 (axe-macmini) 의 현재 값을 짝지어 정리.
Layer 0 — Hardware
- Mac mini (Apple silicon 권장 — M1/M2/M3/M4). 외장 SSD 1개 (backup tier B).
- 본 머신 = AXEs-Mac-mini (Soohun 운영).
Layer 1 — macOS + system tunables
| 항목 | 값 (본 머신) | 신규 setup |
|---|---|---|
| macOS version | 26.3.1 (Build 25D771280a) | 26.0+ 권장 |
| Remote Login | on (sshd active port 22) | sudo systemsetup -setremotelogin on |
kern.tty.ptmx_max | 511 (default) | leak 함정 — Claude Desktop app 의 PTY leak 으로 한도 도달 가능. sudo sysctl -w kern.tty.ptmx_max=2047 + launchd plist 영구화 (B-axe-pty-max-launchd) |
launchctl limit maxfiles | 256 / unlimited | 낮음 — heavy MCP/Docker 운영 시 부족할 수 있음 |
launchctl limit maxproc | 5333 / 8000 | 충분 |
| FileVault | (확인 필요) | 권장 ON (외장 SSD backup 도 암호화) |
| Time Machine | (선택) | restic 백업이 primary, TM 은 보조 |
Layer 2 — macOS users + dev group (D-dev-platform-1)
본 머신:
axe(UID 501) — ai@ 봇 전용 (자동화/cron/MCP container 운영자)soohun.kang(UID 502) — 운영자 본인 dev 계정taehun.kang(UID 503) — axev 대표 dev 계정- group
dev(GID 501) — 위 3명 모두 가입
신규 setup:
sudo bash <<'OPSCRIPT'
set -e
dseditgroup -o create -r "AXE Developers" -n . dev 2>/dev/null || true
dseditgroup -o edit -a axe -t user dev
OPSCRIPT
# user 추가 (각 사람마다)
sudo sysadminctl -addUser <shortname> -fullName "<Full Name>" -password <random> -shell /bin/zsh -home /Users/<shortname> -GID 501
sudo createhomedir -c -u <shortname>
sudo dseditgroup -o edit -a <shortname> -t user dev
sudo install -d -o <shortname> -g dev -m 700 /Users/<shortname>/.ssh
sudo install -m 600 -o <shortname> -g dev /dev/null /Users/<shortname>/.ssh/authorized_keys
# password 는 Vaultwarden item 'axe-macmini local: <shortname>' 에 등재repo 공유 — /Users/axe/{blueprint,frame,hive,axelabs-docs} = group dev + setgid + git core.sharedRepository=group:
for d in blueprint frame hive axelabs-docs; do
sudo chgrp -R dev "/Users/axe/$d"
sudo find "/Users/axe/$d" -type d -exec chmod g+rwsX {} +
git -C "/Users/axe/$d" config core.sharedRepository group
doneLayer 3 — sudoers (/etc/sudoers.d/ai-axe, D-dev-platform-1)
ai@ subprocess 가 NOPASSWD 로 실행 가능한 좁은 명령 set:
Cmnd_Alias AI_USER = /usr/sbin/dseditgroup, /usr/sbin/sysadminctl, /usr/bin/dscl ., /usr/sbin/createhomedir
Cmnd_Alias AI_SSH = /bin/launchctl kickstart -k system/com.openssh.sshd, /usr/sbin/sshd -t
Cmnd_Alias AI_FS = /usr/bin/install, /usr/sbin/chown, /usr/bin/tee /Users/*, /usr/bin/tee /etc/ssh/sshd_config*, /bin/mv /etc/ssh/sshd_config*
axe ALL=(ALL) NOPASSWD: AI_USER, AI_SSH, AI_FS설치: sudo install -m 440 /dev/stdin /etc/sudoers.d/ai-axe <<<'<content>' + sudo visudo -cf /etc/sudoers.d/ai-axe.
금지: tee 의 wildcard 가 /Users/* 또는 /etc/ssh/sshd_config* 로 제한 — tee 자체 무제한 NOPASSWD = root takeover.
Layer 4 — SSH 강화 (D-dev-platform-1)
/etc/ssh/sshd_config.d/200-axe-hardening.conf:
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitRootLogin no검증: sudo sshd -t → reload: sudo launchctl kickstart -k system/com.openssh.sshd.
Layer 1 보호 = Cloudflare Tunnel + Zero Trust (Microsoft Entra SSO 게이트). host SSH 는 layer 2 = key-only.
Layer 5 — Tailscale + Cloudflare Tunnel
| 항목 | 본 머신 |
|---|---|
| Tailscale IP | 100.127.210.30 (host: axe-macmini) |
| Cloudflare tunnel UUID | d8efecdd-2c3f-42de-9925-501433e21394 (container axelabs-tunnel) |
| 외부 hostname (axe.axelabs.ai) | /blueprint/mcp /frame /hive /vault /index (catch-all → blueprint-app) |
| 추가 hostname | ssh.axe.axelabs.ai (host SSH, Zero Trust App) [D-dev-platform-5] |
| Cloudflare Zero Trust App | b903d8cd-... (Self-hosted, Email policy *@axellc.com) |
| Zone | axelabs.ai (zone id 850053fec240de9d83f165cd3e6f10a5) |
| Account | 6e6bbb05187c8bfea52d63fa1a06460b |
신규 customer 머신:
- Tailscale install + tailnet 합류 (Microsoft Entra SSO)
- 새 cloudflared tunnel UUID 발급 (Cloudflare 대시보드)
- DNS CNAME
<customer>.axelabs.ai→ tunnel hostname - Zero Trust App
ssh.<customer>.axelabs.ai(선택 — 외부 dev access 시)
Layer 6 — Microsoft Entra ID
| 앱 | 용도 |
|---|---|
| Frame MCP | 회계 backend 접근 (PKCE public client) |
| Hive MCP | HR backend 접근 (PKCE public client) |
| Blueprint MCP | platform MCP at apex (PKCE public client) |
| Vaultwarden | password vault SSO (OIDC confidential, secret 보관) |
| Blueprint Web | web UI NextAuth (OIDC) |
axe tenant ID = 122fb574-7efa-476a-95b6-bee81bce2cce (axellc.com).
신규 customer = 자기 tenant 별도. 본 머신의 5 앱 패턴 미러. 자동 등록: ops/runbook/customer-onboarding.
Layer 7 — Vaultwarden (D-ops-11 Timshel fork, D-ops-24/26/27/29 SSO)
- 컨테이너:
axe-vaultwarden(hostaxe.axelabs.ai/vault) - bw CLI: 2025.7.0 (Timshel fork schema 호환 — D-ops-28)
- SSO: Microsoft Entra (SSO_AUTHORITY single-tenant)
- 환경 vars:
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION=true,SIGNUPS_ALLOWED=true,SSO_ONLY=false,ORGANIZATION_INVITE_AUTO_ACCEPT=true - org: AXE (
0c5d8bbd-ad85-42b4-8b8a-2849031981b1) - collections: Default · frame-jwt-axec · frame-jwt-axev · frame-jwt-operators
신규 customer = vault 인스턴스 별 (또는 same vault 다른 org). axe customers vault-bootstrap <id> (D-dev-platform-3) 가 3 collection 자동.
Vault session 관리 (D-dev-platform-6): axe vault unlock 운영자 1회 → macOS Keychain (axe.vault.session / [email protected]) 에 BW_SESSION push. ai@ subprocess 가 _vault_env() 로 자동 fetch. BW_SESSION 채팅 회신 패턴 폐기.
Layer 8 — Docker stacks
| Container | 용도 |
|---|---|
| axe-tunnel · axelabs-tunnel · artemis-tunnel | Cloudflare cloudflared (외부 ingress) |
| axe-caddy · axe-blueprint-mcp-proxy · axe-frame-proxy · axe-hive-proxy · axe-vault-caddy | reverse proxy (Caddy 2) |
| axe-vaultwarden | Vaultwarden (password vault) |
| blueprint-app · blueprint-postgres · blueprint-mcp-blue · blueprint-mcp-green | Blueprint platform |
| frame-mcp-blue · frame-mcp-green · frame-postgres · frame-worker | frame 회계 |
| hive-mcp-blue · hive-mcp-green · hive-postgres | hive HR |
| axelabs-docs | docs.axelabs.ai (Nextra) |
| axelabs · axe-console-app | platform UIs |
| artemis-api · artemis-web · artemis-db · artemis-collector_* | artemis (filings/news) |
| mysrt-app · mysrt-nginx · mysrt-postgres | mysrt SRT 자동 예매 |
Docker networks: artemis_default · blueprint_default · frame_default · hive_default · mysrt_default · vault_default · console_default.
신규 customer = 동일 stack 일부 (frame + hive + blueprint + vault + cloudflared 가 핵심). axe customer deploy <id> (B-dev-platform-customer-deploy) 자동화 예정.
Layer 9 — launchd jobs
본 머신 활성:
com.cortex.frontend/com.cortex.backend/com.cortex.cloudflared(Cortex)com.artemis.filings/collectors/index/index_intraday/reporters/logrotate/backupcom.mysrt.app(간접 — mysrt container)com.axe.blueprint-usage-digest(Teams 일일 사용량 09:00 KST)com.axe.blueprint-skills-sync(git webhook → ~/.claude/skills/)com.axe.blueprint-rebuild-watcher(rebuild trigger file polling)com.axe.health-check(매분 7 도메인 + bind-mount probe)com.axe.secret-check(09:00 KST vault drift)com.axe.operator-alert-notify(docker logs tail → osascript notification)com.axe.backup.local(외장 SSD restic)com.axe.restore-drill(분기별 restore 검증)com.axe.ring.push(frame data ring push)com.axe.console.refresh(axe-console-app refresh)
신규 customer = 본 머신 set 의 subset (customer 별 deploy script 가 깔아줌).
Layer 10 — CLI tools
| 도구 | 설치 | 비고 |
|---|---|---|
| Homebrew | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | macOS 표준 |
axe CLI | /Users/axe/.axe/bin/axe (local Python single file, git 미관리) | 별도 git repo 화 backlog |
bw CLI | brew install [email protected] (D-ops-28 다운그레이드) | Timshel fork 호환 |
cloudflared | brew install cloudflared | Tunnel + Access SSH |
gh CLI | brew install gh + gh auth login --with-token | axe-labs-ai PAT (Vault) — D-dev-platform-4 |
docker | Docker Desktop for Mac | file sharing + memory 8GB+ |
python3 | macOS 기본 (또는 brew install [email protected]) | axe CLI venv 자동 |
pnpm / node | brew install pnpm (node 자동) | Blueprint dev |
git config (global):
git config --global user.email [email protected]
git config --global user.name "AXE Labs AI"Layer 11 — Git repos + GitHub
본 머신:
/Users/axe/blueprint, frame, hive, axelabs-docs(4 core)/Users/axe/artemis, cortex, axelabs, mysrt, kolon-discussion, distributa, magnet, stream(별 service)/Users/axe/.axe/— local-only (git 미관리)
GitHub org = axelabs-ai (Free plan, D-dev-platform-5). 4 core repo 에 CODEOWNERS skeleton + PR template (B-dev-platform-cf-zerotrust 완료).
신규 customer = 자기 GitHub user 발급 + axelabs-ai/<customer>-developers team 가입 (axe customers github-team <id> — D-dev-platform-3).
Layer 12 — OneDrive sync
운영자 Microsoft OneDrive client 설치 (Soohun 개인 + 공유). symlink 로 ~/OneDrive → workspace 자료. Blueprint Workspace.drivePath = OneDrive 폴더 GUID.
신규 customer = 본인 OneDrive 또는 SharePoint site. Blueprint 의 trinity 모델 (Workspace × OneDrive × TeamsChat).
Layer 13 — Backup (D-config-14 DR cold storage)
axe-backuplaunchd (daily) — restic snapshot- Tier A:
blueprint-postgres+hive-postgres+customers.yaml+ Vaultwarden data dir - Tier B: 외장 SSD rotation (사용자 manual swap)
- restore drill 분기별 (
com.axe.restore-drill)
신규 customer = 자기 외장 SSD + 본 머신 patterns 미러.
신규 macmini setup 순서 (요약)
- Layer 0-1 (Hardware + macOS) — 운영자 manual
- Layer 2 (users + dev group) —
axe user add(B-dev-platform-user-add-cli) 자동화 예정 - Layer 3-4 (sudoers + SSH) —
axe host bootstrap(B-axe-host-bootstrap-cli) 자동화 예정 - Layer 5 (Tailscale + Cloudflare) — Tailscale CLI +
axe customers dns-placeholder <id>(B-dev-platform-dns-placeholder-cli) - Layer 6 (Entra ID) — 운영자 Azure CLI + 도메인 verify (수동)
- Layer 7 (Vault) —
axe customers vault-bootstrap <id>+axe vault bootstrap - Layer 8 (Docker) —
axe customer deploy <id>(B-dev-platform-customer-deploy) 자동화 예정 - Layer 9 (launchd) —
axe customer deploy가 install - Layer 10 (CLI) —
axe customer deploy가 brew install - Layer 11 (Git) —
axe customers github-team <id>+ 직원 별 collaborator - Layer 12 (OneDrive) — 운영자 수동
- Layer 13 (Backup) —
axe customer deploy가 launchd 등재
함정 + 알려진 미해결
| 함정 | 결과 | 회피 / 결정 |
|---|---|---|
kern.tty.ptmx_max=511 한도 + Claude Desktop PTY leak | forkpty: Device not configured → 새 terminal 못 열림 | Claude Desktop 재시작 또는 sysctl 늘림 + launchd plist 영구화 (B-axe-pty-max-launchd) |
launchctl limit maxfiles=256 | heavy MCP/Docker 시 fd 부족 | per-process 명령 또는 launchd plist 늘림 |
sudoers wildcard 부정확 — tee 전체 NOPASSWD | root takeover | tee 경로 한정 (/Users/*, /etc/ssh/sshd_config*) |
| Cloudflare API token 의 scope 가 notes 와 실제 다름 | API 호출 불필요 fail | curl /user/tokens/verify 로 실 권한 검증 |
| Bitwarden CLI 2025.8.0+ 가 Timshel fork schema reject | bw 명령 모두 fail | 2025.7.0 pin (D-ops-28) |
| BW_SESSION 만료 + ai@ subprocess stdin tty 없음 | bw CLI 호출 fail | Keychain-backed session (D-dev-platform-6) |
| Docker bind-mount file source 손상 | container “not a directory” 거부 | file → dir 변환 + restic restore |
| Fine-grained PAT 의 Resource owner = 본인 (org 아님) | org repo 접근 시 “Could not resolve to a Repository” (404), org membership 와 무관 | 발급 시 Resource owner dropdown = org 선택 (D-dev-platform-4) |
| GitHub UI 에서 PAT regenerate → keyring stale | gh auth status ✓ 인데 모든 API 401 (gh CLI 가 옛 token cached) | gh auth logout -h github.com -u <user> + gh auth login --with-token 새 PAT 으로 재로그인 |
| bw CLI subprocess 호출 시 BW_SESSION env 누락 | bw 가 master password prompt → subprocess stdin pipe → hang 또는 JSON parse fail | python subprocess.run(..., env={**os.environ, "BW_SESSION": ..., "NODE_EXTRA_CA_CERTS": ...}) 명시 전달 (D-dev-platform-6) |
관련 페이지
- /ops/inventory — 본 머신 자산 list (paths, ports)
- /ops/runbook/customer-onboarding — 신규 customer 절차
- /architecture/auth — Microsoft Entra + frame/hive/blueprint OAuth
- /architecture/secrets — vault 정책
- /architecture/backup — restic + 외장 SSD
- /ops/decisions — 모든 D-… 결정
- /ops/backlog —
B-axe-host-bootstrap-cli+B-axe-pty-max-launchd등 후속