신규 Customer Onboarding
AI 요청 프롬프트
https://docs.axelabs.ai/ops/runbook/customer-onboarding 따라 신규 customer ([customer id]) D-day onboard 진행해줘.
진행:
1. D-day prerequisite 확인 — vault session unlock (bw + Keychain `axe.vault.session`, 8h cache) + customer IT 회신 `axelabs-bootstrap-{customer}.json` pack 수령 + customer macmini Tailscale ACTIVE direct
2. 페이지 Phase B 표 따라 5 step 중 현재 자동화 상태 분기 — `axe customers ingest` → `axe onboard --apply` → `axe deploy blueprint --apply` → `axe deploy hive --apply` (umbrella 미구현 시 순차)
3. 각 axe 명령은 dry-run (`--apply` 생략) 먼저 실행 + 변경 계획 사용자 확인 후 `--apply`. 매 step 결과 받고 다음. SSH 진입 / cloudflared ingress / vault collection 권한 점검 포함
4. 함정 발생 시 [/ops/known-gaps](/ops/known-gaps) 의 "D-day traps" 섹션 표 따라 우회 (Tailscale short alias / SSH non-login PATH / TXT+CNAME 공존 / keychain partition / bw cache stale / vault DOMAIN path / docker volume external 부재 / customers.yaml directory 잘못 생성 등 15+ 함정)
5. 5 step 완료 후 customer 측 health 검증 (frame/blueprint/hive 외부 endpoint 200) + Ship Log 한 줄 + customer IT 회신 ("배포 완료, 접속 안내")본인 AI session = Claude Code / Cursor / ChatGPT 데스크탑 / Claude.app / 기타.
페이지 본문 = 사람이 직접 read 도 가능, AI 도 참고. AI 가 본 페이지 fetch 후 위 진행 순서대로 사용자와 step-by-step interactive 풀어나감.
신규 customer (예: realchoice) 의 macmini 에 AXE Labs 스택 전체를 배포하는 절차.
D-day TLDR — vault 먼저 풀어두면 1 명령
목표 (B-onboard-1shot): D-day 운영자 입력 = axe 명령 1 줄. vault 의
bw unlock은 session prerequisite (8 시간 cache) — D-day touchpoint 아님.
Phase A — 세션 prerequisite (한 번, 이후 8 시간 자동)
| 작업 | 명령 |
|---|---|
| vault session unlock + Keychain cache | bw unlock --raw | security add-generic-password -s axe.vault.session -a [email protected] -w "$(cat)" -U |
이후 모든 axe secret/axe onboard/axe deploy 명령은 Keychain 의 BW_SESSION 자동 사용. 비번 재입력 없음.
Phase B — Deploy touchpoint (umbrella 후 1 명령, 현재 5 명령)
| # | 운영자가 손대는 부분 | 자동화 | backlog |
|---|---|---|---|
| 0 | customer IT 에게 docs link 4 개 송부 (D-7) | ✅ 자력 — bootstrap.sh 가 docs.axelabs.ai 의 raw 로 노출 (2026-05-23 해소) | B-onboard-bootstrap-publish ✅ |
| 1 | customers.yaml 의 {customer} 블록 + services 슬롯 작성 | axe customers add = stub | B-onboard-customers-add |
| 2 | customer IT pack.json → vault + customers.yaml 흡수 | ✅ axe customers ingest {id} {pack} (2026-05-23 구현) — dry-run/--apply + ruamel round-trip (comment 보존) + idempotent | B-onboard-azure-pack ✅ |
| 3 | axe onboard {customer} --apply (cloudflared + frame, 18-step) | ✅ 자동 | — |
| 4 | axe deploy blueprint {customer} --apply (Blueprint, 11-step) | ✅ 자동 (SSO 미설정 부팅) | B-onboard-bp-sso |
| 5 | Hive stack 수동 (ssh + docker compose) | axe deploy hive 부재 | B-onboard-hive-deploy |
→ 위 5 개를 묶는 umbrella 명령 B-onboard-umbrella 가 구현되면:
# 세션 prerequisite (8 시간 1 회)
bw unlock --raw | security add-generic-password -s axe.vault.session -a [email protected] -w "$(cat)" -U
# D-day — 줄 1 개
axe deploy customer {customer} --from-pack ~/Downloads/axelabs-bootstrap-{customer}.json --apply운영자 D-day 입력 = 0 회 (vault 살아있으면) 또는 1 회 (vault 만료 시 unlock 한 번).
진정한 0 입력 D-day = (a) cron 이
bw unlockkeep-alive 유지 + (b) umbrella 구현 + (c) pack.json 사전 수신. (c) 는 customer IT 측이 axelabs-bootstrap.sh 실행해서 안전채널로 보내주는 시점 의존 — 즉 운영자는 IT 회신만 기다리면 됨.
D-day 실제 명령 (2026-05-26 시점, B-customer-deploy-generalization Phase 1 ✅)
# 0. 운영자 vault unlock (master password 1 회)
export BW_SESSION="$(bw unlock --raw)"
security add-generic-password -s axe.vault.session -a [email protected] -w "$BW_SESSION" -U
# 1. customer IT 회신 받은 axelabs-bootstrap-{customer}.json 흡수 (one-shot)
# sso.tenant_id + sso.apps.{}.client_id fill + services manifest 슬롯 추가
# + vault push × 3 (3 client_secret) 묶음
axe customers ingest {customer} ~/Downloads/axelabs-bootstrap-{customer}.json --apply
# (dry-run 으로 먼저 확인하려면 --apply 빼고 실행 → 변경 계획만 출력)
# 2. Cloudflare tunnel + DNS + cloudflared launchd 부트스트랩 (1-9 step)
axe onboard {customer} --apply --skip-frame
# --skip-frame: frame stack 은 별도 `axe deploy frame` 로. 이전 18-step 묶음 패턴 폐기.
# 3. customer 측 bw vault session bootstrap (interactive 1회 — customer IT 가 실행)
# operator 가 SSH 으로 helper 를 push 한 후 customer IT 에게 실행 요청:
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>"
# → ~/.bw-session (mode 600) 생성. wrapper 가 SSH non-interactive 호출 가능해짐.
# 4. service 3종 customer-측 deploy (각각 13-step, ~5-15 분 each)
axe deploy frame {customer} --apply
axe deploy blueprint {customer} --apply
axe deploy hive {customer} --apply⚠️ 위
{customer}는services:매니페스트를 선언한 신규 customer 자리 — sovereignty/self-deploy 로 졸업한 고객(realchoice/Truvia)은 대상 아님. 그 고객은 자기 macmini 에서 secret·compose·Caddy 를 자체 배포하고,customers.yaml에services:매니페스트가 의도적으로 없어axe deploy {svc} {customer}가services … not declared로sys.exit한다. 운영자 역할 = software supply (code/image) + 외부 노출 (DNS·cloudflared catch-all) 뿐. 근거: /ops/decisions (D-ops-40 / B-customer-sovereignty-architecture).
각 axe deploy {service} {customer} 가 동일한 13-step 흐름 수행:
preflight → clone → vault_check → secrets_bootstrap (auto-gen if missing) → env_local → wrapper push → network → (blueprint 만) frame_mcp_token → compose up → (frame 만) proxy push → health → (frame+hive) register_entities → ingress swap.
axe onboard --skip-frame 권장 이유: 이전 18-step 통합 흐름은 frame stack 까지 묶였으나, 이제 customer-측 deploy 가 일반화되어 axe deploy frame {customer} 로 분리 호출이 idempotent + 재시도 안전. onboard 는 cloudflared layer 만 담당.
D-14 ~ D-7 (사전 협의)
| 작업 | 책임 |
|---|---|
| 1. 가격·SLA 협의 | 운영자 |
2. customer ID 정함 (x, newco 등) | 운영자 |
| 3. customer 측 IT 담당자 식별 | customer |
| 4. macmini 구매 (M2/M3, 16GB+, 512GB+) | customer |
| 5. Tailscale 가입 + key 교환 | 양측 |
| 6. customer 측 Microsoft 365 tenant 확인 | customer |
| 7. customer 측 도메인 검증 준비 (TXT record 추가 권한) | customer IT |
8. axelabs.ai zone 의 {customer} A record 추가 | 운영자 (Cloudflare) |
D-7 (Azure 사전 등록 — customer IT 자력)
운영자가 customer IT 에게 보내는 것 = 링크 4 개 (별도 첨부 / 스크립트 메시지 X — 2026-05-23 B-onboard-bootstrap-publish 해소):
https://docs.axelabs.ai/partner ← 4 단계 흐름 (entry)
https://docs.axelabs.ai/partner/macmini-prep ← macmini 사전 준비 (Tailscale, SSH, 절전)
https://docs.axelabs.ai/partner/registration ← Option A CLI 1 줄 (axelabs-bootstrap.sh)
https://docs.axelabs.ai/partner/handoff ← JSON pack 회신 양식customer IT 가 30 분 (Option A) ~ 45 분 (Option B) 작업 → axelabs-bootstrap-{customer}.json 회신 (또는 텍스트 8 개 값).
JSON pack 스키마: axelabs-bootstrap/v1 — tenant_id + apps.blueprint/vaultwarden/frame_mcp 각각의 client_id + client_secret + (frame_mcp 만) application_id_uri + scope + redirect_uris. 운영자가 받으면 B-onboard-azure-pack 의 axe customer ingest 가 1 줄로 흡수 (구현 후) — 현재는 수동 paste + axe secret push × 3.
운영자가 받으면:
- client_secret 3개 → vault 로 push (Keychain 직접 push 는 폐기 — 비밀의 SoT = Vaultwarden, D-ops-17):
(각 명령은
axe secret push AZURE_{CUSTOMER}_BLUEPRINT_CLIENT_SECRET --service blueprint --customer {customer} axe secret push AZURE_{CUSTOMER}_VAULTWARDEN_CLIENT_SECRET --service vaultwarden --customer {customer} axe secret push AZURE_{CUSTOMER}_FRAME_MCP_CLIENT_SECRET --service frame --customer {customer}Value for ...:프롬프트 → 안전채널로 받은 값 붙여넣기. 매니페스트에 customer service 슬롯이 사전 등재돼 있어야 함 — B-onboard-azure-pack.) customers.yaml에 entry 추가:{customer}: legal_name: "<...>" primary_domain: "<...>" public_domain: "{customer}.axelabs.ai" entities: ["<...>"] tailscale_host: "{customer}-macmini" sso: provider: "microsoft_entra_id" tenant_id: "<...>" apps: blueprint: client_id: "<...>" client_secret_env: "AZURE_{CUSTOMER}_BLUEPRINT_CLIENT_SECRET" redirect_uri: "https://{customer}.axelabs.ai/api/auth/callback/azure-ad" vaultwarden: client_id: "<...>" client_secret_env: "AZURE_{CUSTOMER}_VAULTWARDEN_CLIENT_SECRET" redirect_uri: "https://{customer}.axelabs.ai/vault/identity/connect/oidc-signin" frame_mcp: client_id: "<...>" client_secret_env: "AZURE_{CUSTOMER}_FRAME_MCP_CLIENT_SECRET" application_id_uri: "https://{customer}.axelabs.ai/frame/mcp" redirect_uris: - "https://claude.ai/api/mcp/auth_callback" - "https://claude.com/api/mcp/auth_callback" scopes: - "openid" - "profile" - "email" - "https://{customer}.axelabs.ai/frame/mcp/mcp.access" user_entity_map: {} default_entities_by_domain: "<primary_domain>": ["<entity>"] onboarded: "<date>"cloudflared 중앙 tunnel ingress 편집— 불필요 (2026-05-23 drift 정정).axe onboard가 customer 별 독립 tunnel 을 customer macmini 에 생성/launchd 등록함 (_render_cloudflared_configin/Users/axe/.axe/bin/axe). 중앙/Users/axe/.axe/tunnels/axelabs/config.yml편집 의 D-7 의무는 사라짐. axe macmini 측 cloudflared 는 docs.axelabs.ai / admin.axelabs.ai 등 운영자 자기 서비스 전용.cloudflared 재시작— 위와 동일. customer 측 tunnel 은axe onboardstep 8 의 launchd 가 자동 boot.- Cloudflare zone 의 axelabs.ai 에서
{customer}A record 추가 — 불필요.axe onboardstep 5 가 Cloudflare API 로 자동 CNAME 생성 (<tunnel-uuid>.cfargotunnel.com).
D-day (~1 시간, 자동)
운영자 머신에서 — 상단 D-day TLDR 참조. 핵심 흐름:
# Dry-run 으로 확인 (변경 0)
axe onboard {customer}
# 실제 적용 (cloudflared + frame)
axe onboard {customer} --apply
# Blueprint (별도 명령 — 통합은 B-onboard-1shot)
axe deploy blueprint {customer} --applyaxe onboard 가 18-step 을 순차 실행 (Tailscale SSH 로 customer macmini 에 push):
| Step | 작업 (실제 axe CLI 코드 기준) |
|---|---|
| 1 | Tailscale up / customer macmini reachable 확인 |
| 2 | customer meta 검증 (customers.yaml entry, Tailscale FQDN) |
| 3 | SSH probe (key 인증) |
| 4 | cloudflared 터널 생성 (기존 있으면 reuse) |
| 5 | Cloudflare DNS A record 생성 |
| 6 | cloudflared credentials.json push → macmini |
| 7 | cloudflared config.yml push (ingress 규칙 포함) |
| 8 | cloudflared launchd plist 등록 + boot |
| 9 | cloudflared health check (curl /cdn-cgi/trace) |
| 10 | frame git clone + Dockerfile preflight |
| 11 | frame git submodule + asset 동기화 |
| 12 | macmini Keychain 에 secret push (vault, frame, blueprint, restic) |
| 13 | macmini .env.local 생성 (env_file 용) |
| 14 | docker compose up (postgres + frame-mcp-blue + frame-mcp-green + proxy) |
| 15 | frame-mcp /health/ready poll (max 60s) |
| 16 | frame register-entity × 각 entity + frame migrate |
| 17 | axe-frame-proxy alias = blue 로 설정 |
| 18 | cloudflared ingress alias swap (axe-frame-proxy 가 frame upstream) |
진행 중 어느 step 에서 실패하면 자동 rollback (기존 상태 유지). 운영자 화면에 명확한 오류 + 다음 단계 제시.
D+1 (검증 + Vault 부트스트랩)
운영자 ([email protected]) 측에서:
# 외부 health
axe health {customer}
# customer macmini ssh
ssh {customer}-macmini "docker ps"
# Blueprint admin 부여
ssh {customer}-macmini "docker exec blueprint-app pnpm prisma db execute --stdin" <<EOF
UPDATE User SET role = 'admin' WHERE email = '<ADMIN_EMAIL>';
EOF
# 첫 frame entity 등록 (chart auto-seed 적용, D-ops-21)
ssh {customer}-macmini "docker exec frame-mcp-blue python -m frame.cli register-entity --id <entity_id> --legal-name '<법인명>' --kind corporate --accounting-standard ksme"
# 첫 hive entity 등록 (frame mirror)
ssh {customer}-macmini "docker exec hive-mcp-blue python -m hive.cli register-entity --id <entity_id> --legal-name '<법인명>'"
# PII passphrase 2개 vault push + frame/hive deploy (axe customer 의 deploy-axep.sh 패턴)
# 일반화된 deploy-new-entity.sh 가 자동 처리 (D-ops-31 후속 일반화):
# bash /Users/axe/.axe/vault/deploy-new-entity.sh <customer> <entity_id> "<법인명>"Vault 부트스트랩 — axe vault bootstrap {customer} (D-ops-33)
운영자 mac 에서 1 명령:
# Dry-run (default) — 현재 4 key 적용 상태 진단
axe vault bootstrap {customer}
# 실 패치 + axe-vaultwarden force-recreate
axe vault bootstrap {customer} --apply명령 자동 흐름:
- SSH 로 customer macmini 의
/Users/{ssh_user}/.axe/vault/docker-compose.yml읽음 (axe customer 는 local exec) - 4 key (D-ops-24/26/27) 적용 상태 진단 — 이미 OK 면 skip
- 누락 key 만 patch (in-place, anchor =
SSO_SIGNUPS_MATCH_EMAIL다음 줄) docker compose up -d --force-recreate axe-vaultwarden- Idempotent — 재실행 안전
적용되는 4 key:
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION: "true" # D-ops-24
SIGNUPS_ALLOWED: "true" # D-ops-26
SSO_ONLY: "false" # D-ops-27 — emergency MP fallback 보존
ORGANIZATION_INVITE_AUTO_ACCEPT: "true" # D-ops-27 — 직원 Accept 자동(INVITATIONS_ALLOWED=false 는 기존 docker-compose 의 default — JIT 통일.)
이 명령 빠지면 첫 employee 가 “Failed to retrieve the invitation” / “Your provider does not send email verification status” 등 차단 (D-ops-24, D-ops-26, D-ops-27 참조).
D+2 (직원 onboarding + Vault 첫 멤버 invite)
직원 SSO 안내
운영자 → customer admin 으로 신규 직원 온보딩 URL 전달.
customer admin 이 자기 직원들에게 안내. 각자 Frame connector 4-step setup 따라 설정.
Vault 접속 안내 (troubleshooting 의 Vault 섹션 참조):
- 직링크:
https://{customer}.axelabs.ai/vault/#/sso - Identifier: 그 customer 의 org 이름 (예: AXE, REALCHOICE)
Vault Organization + 직원 invite
customer admin (또는 운영자 SSH proxy) 가:
- Web vault → Admin Console → org 생성 (
{CUSTOMER}대문자 권장, 예: AXE / REALCHOICE) - dual-identity 권장 패턴 (D-ops-29):
ai@{customer-domain}= 자동화 / bot identity (Graph token 발행, proactive DM, agent run)- 실제 admin 직원 별도 계정 = 사람 작업 identity
- 둘 다 vault Owner + access_all=1
- 직원 invite —
/Users/axe/.axe/vault/invite-members.sh패턴 (customer 별 변수만 교체) - Collection 구조 v1 (D-ops-32):
- entity 1개 customer (예: realchoice): 4-collection 단순 구조
Platform — Service Secrets(operator only)Platform — Infrastructure(operator only){Customer Entity}(전직원 RW)Shared Tools(전직원 RW)
- entity 다수 customer (예: axe 의 axec/axev/axep): 6-collection (entity별 분리)
- entity 1개 customer (예: realchoice): 4-collection 단순 구조
D+7 (안정화 점검)
- frame
audit_log활성? (실제 분개 1건 이상 발생?) - backup 정상? (
restic -r /Users/axe/.axe/backups/local snapshots --password-file ...) - 직원 첫 시도 시 누락 없음?
함정
| 함정 | 결과 | 회피 |
|---|---|---|
| customers.yaml entry 빠뜨림 | onboard 18-step 실패 | step 2 가 자동 검증 |
(해소 — axe onboard step 5 가 자동 생성) | — | |
| customer macmini sleep mode | onboard SSH 실패 | D-7 까지 절전 안 함 설정 |
| Tailscale key 교환 안 됨 | step 1 실패 | D-7 까지 양방향 ping 검증 |
customers.yaml services: 섹션에 customer 슬롯 없음 | axe secret push 가 매니페스트 lookup 실패 ({env_name} not in manifest) | services.{svc}.{customer}.secrets[] 슬롯 사전 등재. 현재 axe 만 등재됨 — realchoice 는 신규 작업 (B-onboard-azure-pack) |
| Cloudflare API token vault 미등재 | axe onboard step 4 의 _cf_token() 실패 | 첫 onboard 전 1 회 axe secret push 로 등재 (B-onboard-cf-token-doc) |
| Blueprint 가 SSO 미설정으로 부팅 | axe deploy blueprint 후 첫 로그인 실패 | 현재 cmd_deploy_blueprint docstring 명시 한계. customer Keychain 에 Azure secret 별도 push 필요 (B-onboard-bp-sso) |
| Hive stack 자동 배포 명령 부재 | axe deploy hive 가 없어서 매번 ssh + docker compose 수동 | B-onboard-hive-deploy |
Vault SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION 미설정 | 첫 employee SSO “Your provider does not send email verification status” 차단 | D+1 의 Vault 부트스트랩 4-block (D-ops-24) |
Vault SIGNUPS_ALLOWED=false + INVITATIONS_ALLOWED=false | 첫 employee JIT 가입 “Failed to retrieve the invitation” 차단 | D+1 의 4-block (D-ops-26) |
Vault SSO_ONLY=true 설정 | Entra 장애 시 운영자 lockout (master password 진입 불가) | SSO_ONLY: "false" 유지 (D-ops-27) |
| Vault UI 이메일-first 화면이 SSO Identifier 가림 | 직원이 가입 절차 못 찾음 | /vault/#/sso 직링크 안내 (troubleshooting) |
| frame/hive entity 추가 시 PII passphrase 빠뜨림 | 그 entity 의 PII 작업 시점에 RuntimeError | deploy-new-entity.sh 가 PII passphrase 2개 vault push + ship 자동화 (D-ops-31) |
| customer Vaultwarden 의 bw CLI 자동화 시 BW_SESSION 직접 export 만 함 | axe secret push 실패 (keychain 만 읽음) | security add-generic-password -s axe.vault.session -a ai@... -w "$BW_SESSION" -U 단계 통합 (D-ops-31 부수 발견) |
| Vaultwarden Timshel 1.34.1-6 + bw CLI 2026.x | TypeError: ... toWrappedAccountCryptographicState crash | bw CLI 2025.7.0 pin (npm install -g @bitwarden/[email protected]). 항구 해소: B-vault-upstream-migration — dani-garcia/vaultwarden:1.36.0 native SSO (D-ops-28) |