<!-- canonical: https://docs.axelabs.ai/ops/runbook/employee-onboarding -->
<!-- source: content/ops/runbook/employee-onboarding.mdx -->

---
title: 신규 직원 등록
description: customer admin 으로부터 신규 직원 추가 요청 받았을 때 운영자 절차.
playbook: true
---

# 신규 직원 등록 (운영자 측)

## AI 요청 프롬프트

```
https://docs.axelabs.ai/ops/runbook/employee-onboarding 따라 신규 직원 등록해줘.

진행:
1. customer admin 요청 정보 확인 (email + 업무 + 필요 entity 권한)
2. 페이지 step 1 — `customers.yaml` 의 `user_entity_map` 에 한 줄 추가 (`"<email>": [<entity>...]`), 매 변경 사용자 확인
3. 페이지 step 2 — customer macmini 측 frame 재기동 (`docker compose restart frame-mcp-blue frame-mcp-green`, ~5초)
4. 함정 — entity 명 typo / scope 별도 분리 미구현 (default `read/write/close` 부여) / customer macmini SSH 진입 실패
5. 페이지 step 3 검증 (`axe health frame`) + 직원에게 onboarding 안내 송부 ([/onboard/ssh-access](/onboard/ssh-access) + [/onboard/vault-setup](/onboard/vault-setup) + [/onboard/claude-connectors](/onboard/claude-connectors) playbook 3종)
```

본인 AI session = Claude Code / Cursor / ChatGPT 데스크탑 / Claude.app / 기타.

페이지 본문 = 사람이 직접 read 도 가능, AI 도 참고. AI 가 본 페이지 fetch 후 위 진행 순서대로 사용자와 step-by-step interactive 풀어나감.

customer admin 으로부터:

```
신규 직원: kim.minsoo@axellc.com
업무: 회계 (axec)
필요 권한: axec read+write
```

같은 요청 받았을 때.

## 1. customers.yaml 수정

```yaml
axe:
  user_entity_map:
    "ai@axellc.com": ["axec", "axev"]
    "cfo@axellc.com": ["axec", "axev"]
    "kim.minsoo@axellc.com": ["axec"]    # ← 추가
```

→ entity 만 매핑. scope 는 기본값 (`read`, `write`, `close`) 모두 부여 — 권한 별도 분리는 별도 작업 (D-ops-?, 향후).

## 2. frame 재기동 (customers.yaml reload)

```bash
ssh axe-macmini "
  cd /Users/axe/frame &&
  set -a && source .env.local && set +a &&
  docker compose restart frame-mcp-blue frame-mcp-green
"
```

소요 ~5초.

## 3. 검증 (옵션)

직원에게 onboarding 안내 보내기 전, 본인이 그 직원 가짜로 시도:

```bash
# Microsoft id_token (테스트용) 발급은 어려우므로, frame integrity 만 확인
axe health frame   # positional target only; multi-customer 환경에선 ssh customer-macmini 측 health 확인
```

`customers.yaml` 의 user_entity_map 변경 → frame 재기동 → 즉시 적용. 별도 검증 불필요.

## 4. 직원 안내 메일

```
제목: [AXE Labs] {회사명} Frame access 설정 안내

안녕하세요 김민수님,

회사 계정으로 회계 시스템 (Frame) 접근 권한이 부여되었습니다.

설정 방법 (5분):
1. claude.ai 에 회사 이메일 (kim.minsoo@axellc.com) 로 로그인
2. https://docs.axelabs.ai/onboard/claude-frame-setup 따라 진행
3. Client ID + Secret 는 별도 안전 채널 (Bitwarden Send) 로 전송 예정

권한:
  - entity axec (에이엑스이 코퍼레이션)
  - 권한: 조회 + 분개 등록 (read + write)

문제 발생 시:
  - 1차: 회사 IT 담당자
  - 2차: ai@axellc.com (운영자)

감사합니다.
```

## 5. Client Secret 전달 — `axe secret send` + Teams DM 자동 발사

Client ID 는 평문으로 같이 보내도 무방 (GUID 일 뿐). Client Secret 은 **반드시 Bitwarden Send 1회용 링크** — `axe secret send` 가 매니페스트 lookup + 1회용 링크 생성을 한 번에. 옵션 의미는 [/architecture/secrets § 사람에게 전달](/architecture/secrets#사람에게-전달--bitwarden-send).

### 5.1 URL 발급 (3개 secret)

```bash
EMP=kim.minsoo   # local-part (라벨용)
EMAIL=kim.minsoo@axellc.com
> /tmp/onboard-$EMP.txt
for env in AZURE_BLUEPRINT_MCP_CLIENT_SECRET \
           AZURE_HIVE_MCP_CLIENT_SECRET \
           AZURE_FRAME_MCP_CLIENT_SECRET; do
  svc=$(echo $env | awk -F_ '{print tolower($2)}')
  url=$(axe secret send $env --service $svc --to $EMP 2>/dev/null)
  echo "$svc|$url" >> /tmp/onboard-$EMP.txt
done
```

### 5.2 Teams DM 자동 발사 ([broadcast-dm](/architecture/secrets#자동-발사--apiadminbroadcast-dm))

```bash
CRON=$(grep -E "^CRON_SECRET=" /Users/axe/blueprint/.env | cut -d= -f2-)
BP=$(grep "^blueprint|" /tmp/onboard-$EMP.txt | cut -d'|' -f2)
HV=$(grep "^hive|"      /tmp/onboard-$EMP.txt | cut -d'|' -f2)
FR=$(grep "^frame|"     /tmp/onboard-$EMP.txt | cut -d'|' -f2)

TEXT="$EMP 님, 회사 시스템 (Blueprint / Hive / Frame) Custom Connector 등록 부탁드립니다.
가이드: https://docs.axelabs.ai/onboard/claude-frame-setup

claude.ai → Settings → Connectors → + Add Custom Connector × 3

Blueprint
  URL: https://axe.axelabs.ai/blueprint/mcp
  Client ID: 482598f7-540c-462c-9dfd-b957651eb804
  Secret: $BP

Hive
  URL: https://axe.axelabs.ai/hive/mcp
  Client ID: b7ead15d-2fea-4864-a5a8-b4b07d1629d4
  Secret: $HV

Frame
  URL: https://axe.axelabs.ai/frame/mcp
  Client ID: 137fc0ef-eb9f-4903-acbc-1a748add349c
  Secret: $FR

Microsoft 회사 계정 ($EMAIL) 로그인 → Connected. 막히면 ai@axellc.com."

curl -s -X POST http://localhost:3100/api/admin/broadcast-dm \
  -H "Authorization: Bearer $CRON" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg e "$EMAIL" --arg t "$TEXT" \
        '{emails:[$e], text:$t, contentType:"text"}')"
# → {"summary":{"sent":1,...}}
```

신규 직원이 Teams 에서 ai@axellc.com 으로부터 1:1 DM 수신 → 3 Send URL 클릭 → claude.ai Custom Connector Secret 칸 paste → `-a 1` 자동 무효. 운영자 손 = unlock 1번 + 위 2-block 실행만.

## 6. Bitwarden Vault 멤버 추가 (선택)

향후 frame-JWT 직접 발급 경로로 회귀할 경우 대비:

1. Vaultwarden organization → frame-jwt-axec collection → 멤버 추가: `kim.minsoo@axellc.com`
2. 권한: read-only (item 조회만)

현재 직접-Microsoft OAuth 경로에선 불필요. 향후 변경 가능성 대비.

## 7. 직원이 첫 호출 후 검증

직원 등록 후 며칠 내 본인이 frame audit_log 확인:

```bash
ssh axe-macmini "docker exec frame-postgres psql -U frame -d frame -c \"
  SELECT actor, op, table_name, ts
  FROM axec.audit_log
  WHERE actor = 'kim.minsoo@axellc.com'
  ORDER BY ts DESC
  LIMIT 5;
\""
```

활동 1건 이상 = 정상 작동 확인.

## 함정

| 함정 | 결과 | 회피 |
|---|---|---|
| `user_entity_map` 에 추가만 하고 컨테이너 재기동 X | 권한 적용 안 됨 | 항상 `docker compose restart` |
| email 대소문자 불일치 | lookup 실패 | customers.yaml 는 lower-case 통일 (frame 코드가 자동 normalize) |
| entity 이름 오타 (`axec` vs `axes`) | EntityNotAuthorizedError | YAML 저장 전 확인 |
| 권한 부여 후 직원에게 알림 안 함 | 직원이 onboarding 절차 모름 | 메일 발송 절차 자동화 검토 |

## 향후 개선

- [ ] `axe employee add --customer axe --email <...> --entity axec` CLI 명령 추가
- [ ] customer admin 이 self-service 로 직원 추가하는 Blueprint UI
- [ ] 권한 부여 audit (누가 누구에게 언제 부여)
