기존 운영 macmini → customer 전환
표준 onboarding (/partner → /partner/macmini-prep) 은 신품 macmini 를 전제로 한다 (D-7 절전 OFF + Tailscale 합류 + Docker 설치부터). 본 페이지는 다른 출발점 — 운영자가 이미 띄워 운영 중인 macmini (예: axe stack 이 live 인 operator-grade 박스, 또는 사전 셋업을 끝낸 staging 박스) 에 새 customer 신원을 얹어 customer-served host 로 전환하는 경우를 다룬다.
🎯 핵심: 하드웨어·OS·Docker·Tailscale 은 이미 준비됐다. 따라서 표준 흐름의 인프라 prep (macmini-prep 6단계) 은 건너뛰고, 논리 식별 4가지만 바뀐다 — ①
customers.yamlentry, ② vault organization/collection, ③ cloudflared tunnel route, ④ container 식별 (실제 rename 은 아직 없음 — 아래 참조). 그 위에 customer-scoped service stack 을axe deploy로 올린다.
언제 이 경로인가
| 상황 | 이 페이지 적용 |
|---|---|
운영자가 자기 axe stack 돌리던 박스를 customer 에게 인계 (이관) | ✅ |
| 사전에 운영자가 prep 끝낸 staging macmini 를 customer 용으로 승격 | ✅ |
| 운영자 박스에 customer 의 추가 service (stream/magnet 류) 만 co-tenant | ✅ (부분 — § 부분 전환) |
| 완전 신품 macmini, customer IT 가 직접 prep | ❌ → 표준 onboarding |
| customer 가 자기 Vaultwarden 을 이미 운영 중 (vault 유지) | ❌ → /partner/deploy § Vault OIDC SSO 통합 |
⚠️ AXE 의 격리 모델은 customer-per-macmini 다 (/architecture/secrets § 컨테이너 이름 컨벤션). 같은 host 에 서로 다른 customer 둘을 영구 co-tenant 하는 것은 표준이 아니다 — 컨테이너 이름이 아직 customer-prefix 가 아니라 (
frame-postgres등 고정명) 두 customer 의 frame stack 이 같은 박스에서 포트·이름 충돌한다. 따라서 “전환” 의 정상 종착지는 그 박스가 한 customer 의 host 가 되는 것 (operatoraxe신원에서 customer 신원으로 소유 이전), 단일 host 에 영구 다중 customer 가 아니다.
무엇이 바뀌는가 (4가지)
① customers.yaml entry
운영자 macmini 는 axe customer 로 동작한다 — axe CLI 의 is_local = (customer_id == "axe") 분기가 이 박스를 local exec (SSH 없이 직접) 로 다룬다. customer 로 전환하면 그 박스는 더 이상 axe local-exec 호스트가 아니라 별도 customer entry 가 되고, axe CLI 가 Tailscale SSH 로 접근한다.
- customers.yaml 에 customer 블록 추가 (stub 은
axe customers add {cid}):
{customer}:
legal_name: "<법인명>"
primary_domain: "<회사 도메인>"
public_domain: "{customer}.axelabs.ai"
entities: ["<entity_id>"]
tailscale_host: "{customer}-macmini" # 이미 가입돼 있으면 현 hostname
ssh_user: "<현 macOS 사용자명>" # 기존 박스의 실제 user (axe 가 아니면 명시)
sso:
provider: "microsoft_entra_id"
tenant_id: "<...>"
apps: { blueprint: {...}, vaultwarden: {...}, frame_mcp: {...} }
services:
frame: { env_file: "/Users/<user>/frame/.env.local", secrets: [...] }
blueprint: { env_file: "/Users/<user>/blueprint/.env", secrets: [...] }
hive: { env_file: "/Users/<user>/hive/.env.local", secrets: [...] }
vaultwarden:{ env_file: "/Users/<user>/.axe/vault/.env", bootstrap_only: true }
onboarded: "<date>"전체 entry 스키마 (sso.apps + 각 service secrets[]) 는 /ops/runbook/customer-onboarding § D-7 와 동일하다. customer IT 가 Entra app 3개를 새로 등록 (axelabs-bootstrap.sh) 해서 JSON pack 을 회신하면 axe customers ingest {cid} <pack>.json --apply 가 sso 블록 + client_secret 3개 vault push 를 한 번에 흡수한다 (/partner/registration, /partner/handoff).
핵심 차이: 기존 운영 박스는
axe신원으로 컨테이너가 이미 돌고 데이터가 들어있을 수 있다. customer 로 전환하면 그 데이터의 소유·접근 경계가 customer org 로 옮겨간다. 운영 중 frame/hive 회계·HR 데이터가axe법인 것이면 customer 박스로 넘기기 전에 반드시 분리/정리 (customer 의 host 에 운영자 법인 데이터가 남지 않도록). 이건 코드가 자동으로 못 한다 — 운영자 판단.
② vault organization / collection
운영자 vault (/Users/axe/.axe/vault/) 의 AXE org (0c5d8bbd-...) 는 운영자 자신의 비밀을 담는다. customer 박스는 자기 Vaultwarden 인스턴스 + 자기 org 를 가져야 한다 (customer sovereignty). 전환 시:
- 그 박스의
axe-vaultwarden이 customer 의 org 를 호스트하도록 한다 — 운영자 org 데이터를 customer 박스에 남기지 않는다. - customer org 생성 + collection 구조 v1 (D-ops-32):
- entity 1개 customer: 4-collection (
Platform — Service Secrets/Platform — Infrastructure(operator only) ·{Customer Entity}/Shared Tools(전직원 RW))
- entity 1개 customer: 4-collection (
- dual-identity 계정 (D-ops-29):
ai@{customer-domain}(bot) + 사람 admin, 둘 다 Owner + access_all. - vault env 4-key 패치 (JIT 가입 차단 회피) —
axe vault bootstrap {customer} --apply가 SSH 로 customer macmini 의docker-compose.yml에 in-place 적용 + force-recreate (D-ops-24/26/27, D-ops-33).
서비스 비밀 흐름 자체는 동일 — customers.yaml services.<svc>.secrets[] → axe secret push → customer vault org → axe secret pull <svc> --customer {cid} → env_file. 단 vault item 네이밍의 customer 세그먼트가 axe → {customer} 로 바뀐다 (frame/axe/db-password → frame/{customer}/db-password). 상세 = /services/vaultwarden + /architecture/secrets.
③ cloudflared tunnel route
운영자 박스의 tunnel 은 axelabs-tunnel (historical 명, customer infix 없음 — axe 전용) 이고 docs.axelabs.ai / admin.axelabs.ai 등 운영자 자기 서비스 를 서빙한다 (axe CLI TUNNEL_NAME_FMT = "axelabs-{customer}", axe 는 예외적으로 infix 생략).
customer 로 전환하면 그 박스는 자기 customer tunnel 을 가진다:
axe onboard {customer} --apply가 customer macmini 에 독립 tunnel (axelabs-{customer}-tunnel) 을 생성 + launchd 등록 + boot 한다 (_render_cloudflared_config). 중앙 tunnel 편집은 불필요 — 2026-05-23 drift 정정.- ingress route 는
{customer}.axelabs.ai의 path 들 (/,/vault,/frame/mcp,/blueprint,/hive/mcp) 을 host 내부 포트로 매핑. - Cloudflare DNS 의
{customer}CNAME (<tunnel-uuid>.cfargotunnel.com) 은axe onboardstep 5 가 API 로 자동 생성.
즉 운영자 박스가
axelabs-tunnel로 운영자 도메인을 서빙하던 것에서, customer 박스는axelabs-{customer}-tunnel로{customer}.axelabs.ai를 서빙한다. 한 박스를 인계하는 전환이면 운영자 tunnel/launchd 를 그 박스에서 제거하고 customer tunnel 만 남긴다.
④ container 식별 — 실제 rename 은 아직 없음
여기가 가장 오해하기 쉬운 부분이다. 컨테이너 이름은 customer 와 무관하게 고정이다:
| 패턴 | 예시 | customer 식별 방식 |
|---|---|---|
| customer-agnostic (service-prefixed) | frame-postgres, frame-mcp-blue, hive-mcp-blue, blueprint-app | host (macmini hostname) 로 식별. customer-per-macmini 격리라 같은 host 에 다른 customer 없음 → 충돌 0 |
historical axe- prefix 잔존 | axe-frame-proxy, axe-hive-proxy, axe-vaultwarden, axe-vault-caddy | compose 의 container_name 이 axe- 박혀 있음 — customer 박스에서도 이름은 axe-... (misleading 하지만 동작엔 무해) |
| customer-prefix 적용 | axelabs-{customer}-tunnel (cloudflared) | ✅ 유일하게 customer ID 가 들어가는 것 = tunnel |
즉 전환해도 frame/hive/blueprint 컨테이너 이름은 안 바뀐다 (frame-postgres 그대로). 변수화는 backlog (B-container-name-customer-prefix, compose container_name 을 ${CUSTOMER_PREFIX}- 로 — 실 운영 영향 없어 우선순위 中). 그래서 “container prefix 변경” 이라기보다 container 의 소유 host 가 바뀌는 것 으로 이해해야 한다.
전환 절차
전제: vault session 살아있음 (bw unlock 8h cache, Phase A) + customer Entra pack 회신 받음 + 그 박스 Tailscale reachable.
# 0. (인계 전환이면) 박스에서 운영자 axe 신원 정리:
# - 운영자 법인 데이터 (frame/hive) 분리 — 코드 자동화 없음, 운영자 판단
# - 운영자 axelabs-tunnel launchd 제거 (customer tunnel 로 대체될 것)
# - 운영자 org 데이터가 그 박스 vault 에 남지 않도록 정리
# 1. customers.yaml entry 흡수 (Entra pack → sso 블록 + client_secret 3개 vault push)
axe customers ingest {customer} ~/Downloads/axelabs-bootstrap-{customer}.json --apply
# (먼저 --apply 빼고 dry-run 으로 변경 계획 확인)
# 2. cloudflared customer tunnel + DNS + launchd (그 박스에 독립 tunnel 생성)
axe onboard {customer} --apply --skip-frame
# 3. customer 측 bw vault session bootstrap (customer IT interactive 1회)
scp /Users/axe/.axe/bw-bootstrap.sh {customer}-macmini:~/bw-bootstrap.sh
ssh -t {customer}-macmini "brew install bitwarden-cli && ~/bw-bootstrap.sh https://<vault-url> <login-email>"
# 4. customer-scoped service stack 배포 (각 13-step, idempotent)
axe deploy frame {customer} --apply
axe deploy blueprint {customer} --apply
axe deploy hive {customer} --apply
# 5. vault env 4-key 패치 + force-recreate (JIT 가입 차단 회피)
axe vault bootstrap {customer} --apply각 axe deploy {svc} {customer} 는 13-step (preflight → clone → vault_check → secrets_bootstrap → env_local → wrapper push → network → (blueprint) frame_mcp_token → compose up → (frame) proxy push → health → (frame+hive) register_entities → ingress swap) 을 SSH 로 그 박스에서 수행한다. 표준 onboarding 의 D-day 와 동일한 명령 — 차이는 macmini-prep 6단계를 건너뛴다 (인프라 이미 준비됨) 는 것뿐.
전환 후 검증 + D+1/D+2/D+7 후속 (admin 부여 · entity 등록 · 직원 SSO 안내 · 안정화 점검) 은 /ops/runbook/customer-onboarding 의 해당 시점 절차를 그대로 따른다.
부분 전환 — 운영자 박스에 customer service 만 co-tenant
(엄밀 격리 모델 외 예외) customer 의 기존 자산 service (예: realchoice 의 stream :8780 / magnet :8770) 가 운영자 manifest 에만 정렬돼 도는 형태가 있다 — 이 경우 컨테이너는 customer 의 것이되 customers.yaml manifest 만 운영자 SoT 에 등재된다 (서비스 카탈로그 의 stream/magnet live (realchoice) 표기). 이건 “박스 전환” 이 아니라 manifest 정렬 이므로 위 ①만 (customers.yaml services 슬롯 추가) 적용하고 ②③④ 는 customer 측 기존 인프라를 그대로 둔다. 포트 충돌만 사전 확인 (portcheck / portdup).
함정
| 함정 | 결과 | 회피 |
|---|---|---|
| 운영자 법인 데이터가 customer 박스에 잔존 | customer 가 운영자 회계/HR 데이터 접근 가능 (sovereignty 위반) | step 0 에서 frame/hive 데이터 분리 — 코드 자동화 없음, 운영자 책임 |
운영자 axelabs-tunnel launchd 가 그 박스에 남음 | customer tunnel 과 ingress 경합 / 운영자 도메인이 customer 박스로 샘 | step 0 에서 운영자 tunnel launchd 제거 |
ssh_user 가 axe 가 아닌데 미명시 | axe CLI 가 is_local 분기 오판 또는 SSH user 틀림 | customers.yaml entry 에 실제 macOS 사용자명 명시 |
| 같은 host 에 두 customer 영구 co-tenant 시도 | frame-postgres 등 컨테이너 이름·포트 충돌 | customer-per-macmini 유지 — co-tenant 는 부분 전환 (manifest 정렬) 한정 |
컨테이너 이름이 axe-... 라 customer 박스인데 운영자 것으로 오인 | 운영 혼동 (실 동작 무해) | host (macmini hostname) 로 식별. rename 은 B-container-name-customer-prefix 대기 |
docker compose restart 로 env 변경 반영 시도 | env reload 안 됨 | docker compose up -d --force-recreate <container> |
참고
- 표준 (신품 macmini) onboarding: /partner → /partner/macmini-prep → /partner/registration → /partner/handoff → /partner/deploy
- 운영자 측 D-day 절차 (동일 명령): /ops/runbook/customer-onboarding
- vault 서비스 정의 + org 모델: /services/vaultwarden
- 비밀 흐름 + 컨테이너 이름 컨벤션: /architecture/secrets
- 기존 vault 유지 customer: /partner/deploy § Vault OIDC SSO 통합