배포 협의
귀사 macmini 에 AXE Labs 스택을 배포하는 일정과 사전 요구사항.
사전 요구사항 (D-7)
| # | 항목 | 책임 |
|---|---|---|
| 1 | macmini 1대 (M2/M3, 16GB+ RAM, 512GB+ SSD) | 귀사 (구매) |
| 2 | 항상 켜진 상태 + 인터넷 안정 (유선 권장) | 귀사 |
| 3 | Tailscale 가입 + macmini 가 tailnet 에 합류 | 귀사 IT |
| 4 | 운영자의 Tailscale SSH key 허용 | 귀사 IT |
| 5 | Microsoft 365 tenant + 3개 app 등록 | 귀사 IT |
| 6 | {customer}.axelabs.ai 도메인 검증 | 귀사 IT + 액스코퍼레이션 주식회사 (DNS) |
| 7 | client_secret VALUE 안전 채널 전달 | 귀사 IT |
위 7 개 완료 = D-day 작업 진행 가능.
D-day 작업 (운영자 측, 4시간)
운영자가 axe onboard {customer} --apply 실행. 18-step 자동화:
| Step | 작업 | 다운타임 |
|---|---|---|
| 1 | Tailscale status check | 0s |
| 2 | customer registry 확인 | 0s |
| 3 | SSH probe (macmini reachable?) | 0s |
| 4 | Cloudflare tunnel credential push | 0s |
| 5-9 | cloudflared launchd 설정 | 0s |
| 10 | frame docker preflight + git pull | ~30s |
| 11-16 | frame docker-compose bootstrap | ~5min (이미지 빌드) |
| 17 | health check | 0s |
| 18 | 운영자 알림 | — |
총 ~4시간 (이미지 빌드 + 첫 데이터 sync 포함).
D+1 (검증)
- 운영자가 직접 첫 로그인 검증
- 귀사 admin 사용자 (
ADMIN_EMAIL) 에게 권한 부여 - 검증 메일 발송
D+2 ~ (운영)
- 귀사 직원 onboarding 시작
- 각자 신규 직원 온보딩 따라 setup
- 1차 helpdesk = 귀사 IT (URL/secret 확인 등), 2차 = 운영자 (서버 측)
도입 후 귀사 macmini 의 실 컨테이너 구성
axe onboard {customer} --apply + axe deploy blueprint/hive {customer} 가 끝난 뒤 귀사 macmini 에 떠 있는 컨테이너 목록 (docker ps 결과 — realchoice 예시):
realchoice-macmini (M2+, 16GB+, Tailscale 메시, 절전 OFF)
├ axelabs-realchoice-tunnel (cloudflared, customer 별 독립 tunnel — origin 한 줄만)
├ frame-postgres :3700
├ frame-mcp-blue :3710 / frame-mcp-green :3711 (blue/green, 무중단 swap)
├ axe-frame-proxy :3712 (Caddy blue/green selector) ⚠️ axe- prefix
├ frame-worker (cron + match_pending_sweep)
├ hive-postgres :3800
├ hive-mcp-blue :3810 / hive-mcp-green :3811 (blue/green)
├ axe-hive-proxy :3812 (Caddy blue/green selector) ⚠️ axe- prefix
├ blueprint-postgres
├ blueprint-app :3100 + WebSocket :3101 (blue, dev/build 시 수십초 다운)
├ blueprint-app-green (passive, B-bp-bluegreen 후 활성)
├ blueprint-mcp-blue :3152 / blueprint-mcp-green :3153 (read-only MCP, blue/green)
├ stream-mcp :8780 (귀사 기존 자산, manifest 정렬)
├ magnet-mcp :8770 (귀사 기존 자산, manifest 정렬)
├ axe-vaultwarden (Vaultwarden core) ⚠️ axe- prefix
└ axe-vault-caddy :8222 (Vaultwarden front + OIDC) ⚠️ axe- prefix컨테이너 이름 컨벤션 — 정확히 무엇이 customer-aware 인가
| 패턴 | 예시 | customer 식별 |
|---|---|---|
customer-prefix 적용 (axelabs-{customer}-...) | axelabs-realchoice-tunnel | ✅ — axe onboard (_render_cloudflared_config, axe CLI line 2829) 가 customer ID 박음. axe customer 는 axelabs-axe-tunnel 이 아니라 axelabs-tunnel (historical) |
customer-agnostic ({service}-...) | frame-postgres, hive-mcp-blue, blueprint-app, blueprint-mcp-green | ✅ — customer-per-macmini 격리로 같은 host 에 다른 customer 가 없어 충돌 위험 0. macmini hostname (realchoice-macmini) 으로 식별 |
⚠️ historical axe- prefix 잔존 | axe-frame-proxy, axe-hive-proxy, axe-vaultwarden, axe-vault-caddy | ❌ — frame/docker-compose.yml:197, hive/docker-compose.yml:124, vault/docker-compose.yml:29/66 의 container_name 이 axe- 박혀 있음. realchoice macmini 에서도 컨테이너 이름은 axe-.... customer-per-macmini 격리상 충돌은 없지만 misleading. backlog 등재: B-container-name-customer-prefix |
⚠️ 즉 받은 도식의 axe-frame-proxy / axe-vault-caddy 가 “왜 axe-?” 의문은 정확함 — 실제로 그 이름 그대로 떠 있는 게 맞음 (historical artifact). axelabs-tunnel 만 customer-prefix 누락 (이건 axe customer 전용 이름이라 realchoice 측은 axelabs-realchoice-tunnel).
향후 일반화는 B-container-name-customer-prefix — compose 의 container_name 을 ${CUSTOMER_PREFIX}- 변수화. 실 운영 영향 없어 우선순위 中.
함정
사전 요구사항 단계 (D-7 ~ D-1)
| 함정 | 결과 | 회피 |
|---|---|---|
| Tailscale 미설치 | 운영자 push deploy 불가 | D-7 까지 설치 + key 등록 |
| macmini sleep 모드 | 새벽 backup 실패 | ”절전 안 함” 설정 |
| 도메인 검증 누락 | Application ID URI 등록 거부 | 검증 후 app 등록 |
| client_secret 분실 | 재발급 + 운영자 측 swap | 회신 즉시 안전 보관 |
D-day 18-step 자동화 단계 (2026-05-25 트루비아 측 발견 — B-vault-d-day-traps-2026-05-25 + B-onboard-d-day-traps-2026-05-25)
| 함정 | step | 증상 | 회피 |
|---|---|---|---|
| Private GitHub repo clone 인증 prompt | clone (step 10/11) | axelabs-ai/frame · axelabs-ai/hive clone 시 PAT prompt → SSH 자동 deploy hang | 운영자 임시 PAT 등록 후 customer 측 team add (D-dev-platform-2) |
docker network artemis_default 사전 부재 | network create (pre step 11) | external: true declared network not found → compose up fail | docker network create artemis_default (axe deploy 의 _svc_step_network 빌트인 ✅, 2026-05-25 이후 자동) |
| Blueprint MCP app (4번째 Entra app) 등록 누락 | bootstrap.sh 또는 registry | App #4 미등록 → MCP OAuth 시 redirect 실패 | bootstrap.sh App #4 추가 (B-axelabs-bootstrap-blueprint-mcp-app 후속) |
| external volume 6개 (blueprint 측) 사전 부재 | docker compose up | external: true declared volume not found | compose 의 external 제거 또는 pre-step docker volume create (R6 후속) |
~/.axe/customers.yaml 이 directory 로 잘못 사전 생성 | onboard step | file write fail | onboard step 의 mkdir parent only 정정 |
ADMIN_TOKEN argon2 hash $ interpolation | vault env_file (step 11) | compose v2 yaml expansion 으로 $ segment 가 빈 값 — vault admin lockout | $ → $$ escape (axe secret pull 의 escape_dollar flag 빌트인 ✅) |
Timshel vault image wget 없음 | compose healthcheck | wget: command not found → unhealthy 무한 루프 | healthcheck → curl 변경 |
sso_nonce column 이름 (code_verifier → verifier) | vault recovery SQL | column not found | runbook 정정 ✅ (PostgreSQL/SQLite backend 분기) |
| bw CLI 2026.4+ Timshel SSO 호환성 | partner setup (bw login) | “Master password unlock data was not found” | bw 2025.7.0 pin: npm install -g @bitwarden/[email protected] (또는 axe.2 patch 적용 vault image) |
| bw data dir keychain restore | bw login | rm -rf "~/.config/Bitwarden CLI/" 후에도 옛 session 잔존 (macOS Keychain) | BITWARDENCLI_APPDATA_DIR=$HOME/.bw-axe-customer 로 fresh isolated dir |
Caddyfile /vault no trailing slash | vault access (step 17 health check) | SPA HTML relative path 가 root 옆 resolve → asset 503 | /vault → /vault/ permanent redirect (Caddy path-only destination trailing slash strip 함정) |
Vault OIDC SSO 통합 — 기존 Vaultwarden 운영 중인 customer 용
귀사가 이미 soohunkang/vault repo (운영자 측 vault platform) 또는 자체 fork 의 Vaultwarden 인스턴스를 운영 중이고 그것을 그대로 유지하기로 결정한 경우 (= RE^2 의 Q3 (a) 채택), axec stack 의 별도 axe-vaultwarden 신설을 skip 하고 기존 vault 에 OIDC SSO 만 추가합니다. 본 절은 그 통합 절차.
책임 분담
| 작업 | 누가 |
|---|---|
~/vault repo / compose.yaml / env_file 의 OIDC env 추가 + force-recreate | 귀사 IT (vault 운영 책임) |
| client_secret view-once URL 발사 | 운영자 (axe secret send) |
cloudflared ingress 의 /vault path 를 503 placeholder → vault-caddy 호스트 포트로 변경 | 운영자 (axelabs.ai zone tunnel 운영 책임) |
| Vault Organization 생성 + 첫 admin invite | 귀사 admin (web UI) |
절차 (양측 협업, ~30 분)
1. 운영자 — client_secret view-once URL 발사
axe secret send AZURE_{CUSTOMER_UP}_VAULTWARDEN_CLIENT_SECRET --service vault --customer {customer} --days 1 --access 1귀사가 URL 받으면 1 회만 열어서 secret 값 캡처 후 즉시 안전 보관.
2. 귀사 IT — ~/vault/ 의 env_file (또는 compose.yaml environment: 영역) 에 다음 12 key 추가/변경
먼저 현 vault 의 secrets 보관 위치 확인:
cd ~/vault
grep -E 'env_file|environment:' compose.yaml | head -5env_file: ./vault.env (또는 비슷한 경로) 가 보이면 그 파일 편집. compose.yaml environment: 블록 inline 도 가능.
추가/변경할 12 key (기존 SIGNUPS_ALLOWED 가 false 면 true 로 변경):
# === DOMAIN — axec stack 가 사용할 외부 URL (cloudflared ingress) ===
DOMAIN: "https://{customer}.axelabs.ai/vault" # 기존 Tailscale FQDN 이면 변경
# === OIDC RP — Microsoft Entra ID ===
SSO_ENABLED: "true"
SSO_ONLY: "false"
SSO_AUTHORITY: "https://login.microsoftonline.com/{TENANT_ID}/v2.0"
SSO_CLIENT_ID: "{vaultwarden_client_id}"
SSO_CLIENT_SECRET: "{받은 secret 값}"
SSO_SCOPES: "openid profile email offline_access"
SSO_DEBUG_TOKENS: "false"
# === 4-block: 첫 employee 가입 차단 회피 (D-ops-24/26/27) ===
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION: "true"
SSO_SIGNUPS_MATCH_EMAIL: "true"
SIGNUPS_ALLOWED: "true" # 기존 false 면 변경 필수
ORGANIZATION_INVITE_AUTO_ACCEPT: "true"placeholder 4:
{TENANT_ID}=axelabs-bootstrap-{customer}.json의tenant_id{vaultwarden_client_id}= 동일 JSON 의apps.vaultwarden.client_id{받은 secret 값}= step 1 view-once URL{customer}/{CUSTOMER_UP}= customer id / 대문자
3. 귀사 IT — force-recreate
cd ~/vault
docker compose up -d --force-recreate vault-app # 또는 vaultwarden / 본인 컨테이너명4. 운영자 — cloudflared ingress 의 /vault path 를 vault-caddy 로 라우팅
운영자가 customer macmini 의 ~/.cloudflared/config.yml 의 /vault block 을 503 placeholder → vault-caddy 의 host port (트루비아 vault-caddy 는 보통 :8222, HTTPS 는 :8443) 로 변경 후 cloudflared 재시작. 본 step 은 customer 측 추가 작업 없음.
5. 검증 — SSO 직링크
https://{customer}.axelabs.ai/vault/#/sso웹 UI 에 SSO Identifier 입력 화면이 나오면 OK. Identifier = 운영자 측이 customer 별 명명 (예: REALCHOICE 대문자 권장 — Bitwarden Send 본문에 명시).
첫 employee 시점 — Vault Organization + invite (D-ops-32)
귀사 admin 이 SSO 로 첫 로그인 후 web vault 의 Admin Console:
- Organization 생성 — 이름 =
{CUSTOMER_UP}대문자 (예:REALCHOICE) - Collection 4 개 신설 (D-ops-32 v1):
Platform — Service Secrets(operator only)Platform — Infrastructure(operator only){Customer Entity}(전직원 RW)Shared Tools(전직원 RW)
- 직원 invite — 기존 운영자 측
invite-members.sh패턴 또는 web UI 직접 - dual-identity 권장 (D-ops-29) — admin 직원 별도
ai@{customer-domain}(automation) + 실제 사람 계정 2 identity 둘 다 Owner
함정
| 함정 | 결과 | 회피 |
|---|---|---|
SSO_ONLY: "true" 설정 | Entra 장애 시 운영자 lockout (master password 진입 불가) | false 유지 (D-ops-27) |
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION: "false" (Timshel default) | 첫 employee SSO “Your provider does not send email verification status” 차단 | true 명시 (D-ops-24) |
SIGNUPS_ALLOWED: "false" | 첫 employee JIT signup “Failed to retrieve the invitation” 차단 | true (D-ops-26) |
| Vault UI 이메일-first 화면이 SSO Identifier 가림 | 직원이 가입 절차 못 찾음 | /vault/#/sso 직링크 안내 |
| client_secret 평문 메일 전송 | 영구 보관 노출 | view-once URL (axe secret send) |
| 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]) |
상세 결정 근거: auth.mdx · D-ops-24 · D-ops-26 · D-ops-27 · D-ops-29 · D-ops-32.
정기 점검
| 주기 | 작업 |
|---|---|
| 매일 | 자동 backup + integrity check (운영자 측) |
| 매주 | 운영자 → 귀사 IT health report |
| 매월 | secret 만료 30일 전 알림 (운영자 → 귀사) |
| 분기 | Restore drill (운영자 측 자동) |
| 연 1회 | 보안 감사 협의 |
종료/이전 시
귀사가 AXE Labs 서비스 종료 결정 시:
- 데이터 export (frame DB dump + Vaultwarden export + Blueprint Workspaces)
- cold storage SSD 1개 사본 귀사에 인도
- macmini 의 AXE Labs 컨테이너 제거
- Microsoft Entra ID 의 3개 app 비활성화 (귀사 측)
- axelabs.ai zone 의
{customer}record 제거 (운영자 측) - customers.yaml 의 entry 제거 (운영자 측)
데이터 보관 정책: export 후 자체 보관. AXE Labs 측 백업은 종료 +90일 후 폐기.