Skip to Content

Hive

한 줄 소개: 통합 HR MCP 서버. PostgreSQL schema-per-entity 격리, bitemporal employment_records, ltree 조직 트리, JSONB 커스텀 필드, append-only audit log. Blueprint 에이전트와 직원/매니저가 MCP 도구로 접근.

기술 스택

항목
언어Python 3.12+
프레임워크FastMCP (stdio + HTTP/SSE)
DBPostgreSQL 16 (with ltree, pgcrypto)
ORMSQLAlchemy 2.0 Core + psycopg v3
MigrationAlembic dual-env (shared + entity)
인증PyJWT (HS256 hive JWT + RS256 Microsoft JWKS)
LLMAnthropic Claude (OKR 추천, 평가 초안, 이직 리스크)
HTTP serverStarlette + uvicorn

포트

포트용도
3800PostgreSQL 16 (hive-postgres)
3810MCP HTTP blue (active)
3811MCP HTTP green (passive)
3812axe-hive-proxy (Caddy, blue/green selector)

URL 컨벤션

{customer}.axelabs.ai/hive ← HTTP root (path-based mount) {customer}.axelabs.ai/hive/mcp ← MCP endpoint (canonical resource URI) {customer}.axelabs.ai/hive/health/ready ← readiness {customer}.axelabs.ai/hive/.well-known/oauth-protected-resource

axe.axelabs.ai/hive/mcp 가 현재 axe customer 의 endpoint. realchoice 는 onboarding 시 활성.

5 가지 도메인

영역상태Phase
조직 관리 (org tree, employees, positions, employment_records)live1
휴가 관리 (근로기준법 §60 연차 자동)live1
KPI / OKR미구현2
성과 평가 (360도, AI 보조)미구현2
급여 · 인센티브 (스톡옵션 포함)미구현3

MCP Tools (Phase 1, 19개)

조회

  • employee_get — 단일 직원 (id / email / employee_no)
  • employee_search — 이름·이메일·번호 부분일치 + status/org 필터
  • org_get_chart — ltree 서브트리 (or 전체) + as_of_date 시점 query
  • org_get_team — 조직 + 멤버 (line memberships, include_sub_orgs 옵션)
  • position_list — 직무/직급 목록
  • leave_get_balance — 직원 휴가 잔여 (근로기준법 자동 적용)
  • leave_get_calendar — 팀 휴가 캘린더 (overlap 감지)
  • leave_get_request — 단일 요청 detail
  • leave_policy_list — 휴가 정책 목록
  • leave_usage_report — 기간별 사용 집계

변경

  • leave_request — 휴가 신청 (충돌 + 잔여 자동 검증)
  • leave_approve — 승인/반려 (다단계 결재 지원)
  • leave_cancel — 취소 (start_date 전)
  • leave_policy_create — 정책 등록

관리자

  • employee_create — 신규 입사 + 초기 발령 (optional). payroll_start_date 옵셔널 — None 이면 hire_date fallback (D-hive-27).
  • employee_update — patch (self vs HR 필드 분리). payroll_start_date HR 필드.
  • compensation_plan_list(plan_kind?) — entity 의 active compensation_plan 목록 조회 (read scope, D-hive-30 M 트랙). compensation_award_create 호출 전 plan_code 확보용.
  • compensation_award_create(employee_no, plan_code, annual_amount_krw, effective_from, effective_until?, award_code?) — 직원에게 compensation_award 부여 (D-hive-17 후행 leg, D-hive-30). employee/plan 존재 + active overlap 검증. overlap 시 COMPENSATION_AWARD_CONFLICT raise. payroll_compute_period 가 active salary award 없으면 skip → 본 도구가 페이슬립 산정의 선행.
  • org_create — 조직 신설 (ltree path 자동)
  • org_move_employee — 인사 발령 (membership + bitemporal record)
  • position_create — 직무/직급 신설

Schema discovery — GET /hive/schemas

Blueprint 의 artifact + PARA 지식 레이어 (D-bp-artifact-1) 가 fact 의 typed schema 권위를 MCP 에 위임. hive 는 frame 패턴 (Frame Schema discovery) 1:1 mirror — 자기가 생산하는 HR/payroll fact kind 를 /schemas 엔드포인트로 노출.

curl -H "Authorization: Bearer $HIVE_MCP_TOKEN" \ https://axe.axelabs.ai/hive/schemas

15 schemas 현재 노출 (employee / employee_onboarding / org_chart / position / leave_balance / leave_calendar / leave_request / leave_policy / leave_usage_report / payroll_period / payslip / payroll_remittance / payroll_send_log / payroll_dispatch_log / notify_template). 추가 schema 는 src/hive/mcp/schemas.py (SOT) 에 등재.

  • 인증: required (frame 동일)
  • versioning: schema_id 의 @{version} suffix
  • 소스 권위: src/hive/mcp/schemas.py — 외부 schema registry 없음
  • envelope 형식: frame 과 동일 ({ version, service, schemas }) — Blueprint 의 B-bp-artifact-schema-discovery 가 multi-MCP 통합 fetch

데이터 모델

Shared schema

테이블역할
entity등록 entity 메타 (axec, axev, realchoice)
custom_field_definitions회사별 커스텀 필드 정의 (D-hive-6)
idempotency_record멱등 키 (24h TTL)
event_outboxpg_notify 이벤트 outbox (D-hive-7)

Per-entity schema

테이블역할
organizations조직 (ltree path, GIST index)
positions직무·직급·직책 매트릭스
employees임직원 마스터 (national_id/home_address/bank_account_no = PII 암호화). hire_date = 위임/근로계약 발효일 (frame.executive_officer.appointed_date 정합), payroll_start_date = 보수 개시일 (NULL → hire_date fallback, D-hive-27).
organization_memberships매트릭스 다중 소속 (line/dotted/project)
employment_recordsbitemporal 발령 이력 (valid_from/to + recorded/superseded_at)
leave_policies휴가 정책 (basis + rules JSONB)
leave_requests신청 (approval_chain JSONB, 다단계 결재)
leave_balances잔여 캐시 (granted/used/pending/remaining)
custom_field_violationscustom field 검증 실패 audit
audit_log쓰기 추적 (trigger 자동)
llm_callAnthropic API 호출 audit

권한 모델 — Scope

read < write < approve < admin
  • read — get/list/search
  • write — leave.request (본인), employee.update (자기 일부)
  • approve — leave.approve, employee.update(hr_fields=True), 성과 review.submit
  • admin — employee.create, org 변경, policy 변경, payroll.run

권한은 entity 별로 부여 (예: {"axec": ["read","write","approve"], "axev": ["read"]}).

OIDC 인증 (Microsoft Entra) 기본 = ["read", "write", "approve"]. admin 은 Blueprint EntityRole (/api/internal/entity-roles) 가 해당 (email, entity) 에 대해 role='admin' 또는 'owner' 반환할 때 자동 부여 (D-bp-entity-17, 2026-05-22). auth_oidc.py 가 sign-in 마다 Blueprint 내부 API 조회 (HMAC bearer BLUEPRINT_INTERNAL_API_KEY, 5분 cache, fail-open — Blueprint 장애 시 default scope 유지하여 lockout 회피). HS256 service token 으로 admin 직접 발급은 break-glass / dev 용도로만.

한국 근로기준법 §60 연차

연차 leave_policy 생성 시 rules=None 이면 statutory 적용:

rules: first_year_monthly: { grant_per_month: 1, max_first_year: 11 } after_first_year: { base: 15, bonus_every_n_years: 2, max: 25 } expiry: { kind: "1_year_from_grant" } carry_over: { allowed: false, payout_on_expiry: true }

회사별 변형은 동일 schema 에 JSONB override 만 변경 (코드 변경 X).

OAuth-RP 흐름

frame 과 동일. customers.yaml sso.apps.hive_mcp 에서 client_id / application_id_uri 읽어 Microsoft Entra ID id_token RS256 검증.

Claude Code → POST /hive/mcp (no auth) hive → 401 + WWW-Authenticate: Bearer resource_metadata="..." client → GET /hive/.well-known/oauth-protected-resource hive → {resource: "https://axe.axelabs.ai/hive/mcp", authorization_servers: [Microsoft]} client → Microsoft OAuth flow (PKCE) Microsoft → access_token (aud = Application ID URI) client → POST /hive/mcp Authorization: Bearer <access_token> hive → JWKS RS256 verify + aud + iss + email extract → entity 매핑 hive → MCP session 생성 → tool 호출

Custom Connector 등록 (신규 사용자)

운영자: axe secret send AZURE_HIVE_MCP_CLIENT_SECRET --service hive --to <local-part> → URL 발급 → /api/admin/broadcast-dm 자동 Teams DM. 전체 흐름은 /architecture/secrets § 사람에게 전달. 수신자 화면 절차는 /onboard/claude-frame-setup (5분 가이드, MCP URL 만 https://axe.axelabs.ai/hive/mcp 로 치환).

CLI

# DB hive migrate # shared + 모든 entity hive register-entity --id axec --legal-name "에이엑스이 코퍼레이션" hive list-entities # 토큰 발급 hive mcp-token --sub [email protected] --customer axe \ --entity axec:read,write,approve,admin \ --entity axev:read,write,approve \ --ttl 2592000 # 정책 시드 hive seed-policies --entity axec # 연차/병가/경조사 자동 등록 # 서버 hive mcp-serve # stdio hive mcp-serve-http # HTTP (uvicorn :3810) hive mcp-health --port 3810

운영 노트

  • Blue/green deploy 지원 (3810/3811 동시 실행, docker network alias swap via axe-hive-proxy)
  • Append-only: audit_log trigger 가 모든 mutable table 의 변경 자동 기록
  • PII: pgp_sym_encrypt + entity 별 passphrase (HIVE_PII_PASSPHRASE_<ENTITY>)
  • 이벤트: pg_notify(hive_events, …) — frame 이 LISTEN 하여 payroll.finalized → 인건비 분개
  • 환경변수 = env_file 단일 출처 (D-ops-18, 2026-05-21): docker-compose.ymlenvironment: 블록은 literal config 만 (host/port/log_level 등). 비밀 vars (HIVE_DB_PASSWORD, HIVE_JWT_SECRET, AZURE_HIVE_MCP_CLIENT_SECRET, HIVE_PII_PASSPHRASE_*, HIVE_MAILER_*, AZURE_TENANT_ID) 는 모두 env_file .env.local 에서 직접 dump → axe secret pull hive 가 vault 의 manifest 값으로 .env.local atomic write

Actor model — actor_kind 통합 (D-hive-14)

employees.actor_kind 컬럼으로 human / AI agent / service account / external contractor 를 단일 테이블에 통합. HR 자동화 모듈은 WHERE actor_kind = 'human' 으로 도메인 적용 분기.

actor_kind적용
human휴가, 4대보험, 근기법 §60, salary/equity, PII 컬럼
ai_agentapi_budget / compute_subscription, 성과 평가 (uptime 등), OKR, audit 통합
service_account자동화 system 행위자 (audit only)
contractor_external외부 계약직 (commission/project-fee, PII 없음)

audit_log / org chart / access control 은 모든 actor_kind 공통.

인원명단 SOT (D-hive-15)

Hive = 인원명단 superset SOT. 100명 회사가 되면 Blueprint/Frame 사용자는 부분집합 (소수 정예). 다중 entity 표현 (person × entity × employment_kind 3차원) 은 Hive 만 가능.

Microsoft Entra ID ──(AAD anchor)──▶ Hive ──(hive.employee.* events)──▶ Blueprint (identity SOT) (employment SOT) (platform-access SOT)
  • Hire 결정 → hive.employee.hired → Blueprint LISTEN → User row create + AAD link
  • Term 결정 → hive.employee.terminated → Blueprint LISTEN → soft-delete + AAD disable
  • 역방향 없음 (Blueprint User 가 만들어졌다고 Hive employee 자동 등록 X — silent leak 방지)

AI agent dual-origin (D-hive-16)

AI agent 의 lifecycle 은 인간과 비대칭. Blueprint Agent 배포가 시작점, Hive 가 고용관계 매핑 확정.

Blueprint (Agent 배포) → blueprint.agent.created → Hive 운영자 prompt (어느 entity 비용 부담?) → hive employee-create --actor-kind ai_agent --blueprint-agent-id <id> --entity <axec|axev> → hive.employee.hired (actor_kind=ai_agent) → Blueprint LISTEN → user_entity_map + cost_center 확정

SOT 책임 분리:

  • AI agent employment 관계 (entity, cost_center, position) — Hive
  • AI agent runtime config (model, prompt, tools, secrets) — Blueprint
  • Cross-link: employees.attributes.blueprint_agent_id (soft FK)

모델 업그레이드 (4.6 → 4.7) = 같은 employee_id 유지 + employment_records.record_kind = 'position_change' 로 bitemporal 기록.

보상 — 5-table 유연 모델 (D-hive-17)

7차원 다양성 (종류·시점·산정·통화·세무·성과연동·거버넌스) 을 컴포지션 가능한 5개 테이블로:

compensation_plans 회사 정책 (BASE_SALARY_2026, ESOP_2025, AI_COMPUTE_2026) │ 1:N compensation_awards 개인 약정 (한진우 salary 6.5M, ai@ api_budget $500/mo) │ 1:N compensation_events 실제 이벤트 (월별 지급, vesting tick, exercise, api charge) │ frame_journal_id back-write (pg_notify hive.payroll.event → frame 자동 분개) payroll_periods 월별/주별 사이클 payslips employee × period 명세서 (line_items JSONB)

plan_kind (15+1): salary / bonus / commission / allowance / overtime / equity / severance / benefit / reimbursement / profit_sharing / retention / relocation / api_budget / compute_subscription / other

amount_rule_kind: fixed / formula (expr 평가) / tiered (구간별 비율) / pool (이익 풀 분배) / discretionary

유연성 핵심:

  • Plan ↔ Award 분리 (정책 변경 시 plan 만, award 는 frozen snapshot)
  • JSONB rules (모든 산정 방식 schema 변경 없이 확장)
  • vesting_schedule JSONB (4-year cliff / monthly / milestone / acceleration 모두)
  • performance_link (Phase 2 OKR/KPI 와 lazy coupling)
  • frame_journal_id (cross-service trigger via pg_notify)
  • calculation_inputs JSONB (재현 가능성)
  • requires_resolution + resolution_id (임원 보상 frame.internal_resolution 강제 link)

Phase 1 = schema 만 (5 테이블 비어 있음). Phase 3 = MCP tools (compensation.award.create, payroll.run_period 등) + frame LISTEN handler.

frame 와의 차이점

항목framehive
도메인회계 (KSME)HR (조직/휴가/성과/보상)
격리 단위schema-per-entity (axec, axev)schema-per-entity (axec, axev) — 동일
시간 모델fiscal_period + auditbitemporal employment_records + audit
Scoperead/write/close/reopen/adminread/write/approve/admin
Append-onlyjournal_line, raw_transaction (trigger)audit_log, llm_call (trigger). leave_requests 는 state flow
Custom fieldsaccount_template (KSME standard)custom_field_definitions (JSONB 자유 확장)
Actor 모델(single, operator/user)actor_kind 4종 통합 (human/ai_agent/service_account/contractor_external)
인원 SOT(없음 — frame 은 executive_officer 만)인원명단 superset SOT
보상 모델journal_line 분개만compensation_plans/awards/events + payslips (Phase 3)

Frame 연동 — Canonical ownership (D-hive-13)

한 사실은 단 하나의 서비스가 canonical owner. Hive 는 운영 독립성을 위해 frame 의 entity 메타 일부만 캐시.

사실CanonicalHive 보유
법인명·사업자번호·회계연도Frameshared.entity (canonical column)
대표자·사업장 주소·업종·영문명Frameshared.entity.attributes.frame.* JSONB (cache)
법인등기상 임원·주주Frame미보유. 직원 겹칠 시 employees.attributes.frame_links 명시적 link
자본구조 (자본금·주식수)Frame참조 안 함 (HR 도메인 외)
직원·조직·휴가·평가·급여Hive(Frame 이 인건비 분개 시 Hive MCP 호출)

Bootstrap:

hive register-entity --id axec --legal-name "액스코퍼레이션 주식회사" hive sync-entity-meta --entity axec --from-frame # 대표자/주소/업종 cache

재동기 시점: 등기 변경, 대표 교체 등 frame 측 변동 시 운영자가 위 명령 1회 실행. shared.entity.frame_synced_at = 마지막 watermark. 자동화 (frame → hive 역방향 pg_notify) 는 Phase 2 검토.

금지: MCP tool path 에서 frame Postgres 직접 read (D-hive-12). Frame 의 officer roster 를 직원으로 자동 import (다른 도메인).

외부 노출 (2026-05-21 라이브)

claude.ai → Cloudflare edge (axe.axelabs.ai) → axelabs-tunnel (d8efecdd-... cloudflared container) └─ ingress: path ^/hive(/.*)?$ → host.docker.internal:3812 → axe-hive-proxy (Caddy on :3812) └─ reverse_proxy hive-mcp:3810 (docker network alias) → hive-mcp-blue (active) / hive-mcp-green (passive) → hive-postgres :3800

자동화:

  • /Users/axe/.axe/hive-proxy/ — compose dir (frame-proxy 패턴 mirror). docker compose up -d 1회 부팅 후 영구.
  • /Users/axe/.axe/tunnels/axelabs/config.yml^/hive(/.*)?$ 규칙 1줄 추가됨. cloudflared restart 1회 (~5초 다운타임).
  • DNS 추가 불필요 — axe.axelabs.ai 가 이미 apex (path-based routing D3).
  • Blue/green swap = axe deploy hive 가 hive-mcp docker alias 이동 + caddy reload 실행. cloudflared 무관 (다운타임 0).

검증된 외부 endpoints:

  • https://axe.axelabs.ai/hive/health → 200
  • https://axe.axelabs.ai/hive/health/ready → DB ping 200
  • https://axe.axelabs.ai/hive/.well-known/oauth-protected-resource → RFC 9728 metadata
  • https://axe.axelabs.ai/hive/mcp → 401 + WWW-Authenticate (PKCE challenge)
  • https://axe.axelabs.ai/hive/metrics → Prometheus exposition

Self-service onboarding (D-hive-18)

PII (주민번호·주소·계좌·전화) 는 직원 본인이 incremental 입력. 개인정보보호법 정보주체 본인 입력 원칙 + 운영자 부담 분산.

3개 MCP tools:

Tool기능
employee_onboarding_status본인의 부족 PII 필드 list. next_step 안내. ai_agent 는 status="n/a"
employee_complete_onboardingpartial PII 입력. 형식 검증 + PIIString Fernet 자동 + onboarding_log audit
employee_verify_document통장 사본 / 신분증 PDF·이미지 → Anthropic vision → declared_fields 비교 → 일치 시 자동 저장. 예금주/소유자명 = employees.name 일치 critical 검증 (다른 사람 문서 차단)

필수 필드 매트릭스 (actor_kind × employment_type):

조합필수
human × full_time / part_timenational_id, home_address, bank_account_no, phone
human × advisor / contractor / internnational_id, bank_account_no
human × other (겸직 무급)phone (본업 entity 가 canonical)
ai_agent / service_account모두 N/A

shared.entity.attributes.onboarding_requirements 로 entity 별 override.

Self 인증: token.sub == employees.email 시 self 허용, 다른 사람 → admin scope 필수.

문서 폐기 정책: blob 메모리 즉시 폐기, audit_log 에 content_sha256 + actor + extracted (마스킹) 만 잔존. PDF 영구 저장 X.

계좌번호 cross-service 정책 (D-hive-19)

직원 계좌번호 전체는 Hive only canonical. Frame 미보유.

위치상태
employees.bank_account_no DBFernet ciphertext (enc::v1::...)
payroll.export_remittance 실행 중 메모리평문 < 1초
MCP response in transitTLS 안 base64
운영자 머신 다운로드 XLSX평문 — 24h 폐기 권고
audit_logcontent_sha256
Frame container미보유

Cross-service event (hive.payroll.finalized) 페이로드 표준:

{ "employees_paid": [ { "employee_id": 2, "employee_name": "한진우", "net_amount_krw": 3500000, "bank_last4": "9012", /* 매칭 hint, 전체 X */ "bank_name": "국민은행" } ] }

Frame 가 회사 통장 raw_transaction 의 출금 row 와 last4 + 금액 + 일자 매칭 → 자동 인건비 분개.

Payroll (D-hive-17/19/20) — Phase 3 live

5 도구 + 한국 세율 lifecycle + Frame 자동 분개:

ToolScope기능
payroll_compute_period(period_label="2026-06")adminFull re-run, UPSERT-with-gates (D-hive-29) — 전 active 직원 대상. 직원별 case ABCDE: A 없음→INSERT, B pending+send_log=0→UPDATE (산정 fields 덮어쓰기·운영 메타 보존), C pending+발송됨→skip, D paid→skip, E reversed→skip. summary 에 action/skipped_reason 포함. 임원=고용·산재 미적용. AI agent skipped. 사후 정정 (skip 케이스) 은 compensation_event_adjustment_create
compensation_event_adjustment_create(employee_id, period_label, line_kind, delta_krw, reason)admin페이슬립 사후 정정 (D-hive-17/29 + 룰 H)case C (pending + 발송됨) 만 허용. case A→PAYSLIP_NOT_FOUND / B→PAYSLIP_NOT_DISPATCHED (compute_period 안내) / D→PAYSLIP_ALREADY_PAID (운영자 수동) / E→PAYSLIP_REVERSED raise. 정정 시: payslip line append + 합계 보정 + compensation_events audit row + hive.payroll.event (subtype=adjustment) 발행. delta_krw 부호 = line.amount_krw 부호 (공제 460원 증가 = -460). 직원 메일 ≠ DB → 운영자 재발송 책임
payroll_get_my_payslip(period_label)read (self)본인 명세서 (line_items 표시)
payroll_get_payslip(period_label, employee_id)admin다른 직원 조회
payroll_export_remittance(period_id)admin일괄 송금 XLSX (KB 폼). 평문 계좌 24h 폐기 권고
payroll_mark_paid(period_id, payment_reference)adminstatus=paid + hive.payroll.finalized event 발행

한국 세율 lifecycle (D-hive-20)

shared.tax_rate_schedule — effective_from/until + JSONB rates + source_url. 매년 1월 운영자:

hive.tax.update_rates( category="income_tax", effective_from="2027-01-01", rates={"brackets": [...]}, source_url="https://www.nts.go.kr/...", )

→ 기존 active row effective_until 자동 마감 + 신규 INSERT. payroll 호출 시 period_end 기준 시점별 lookup → 과거 재계산 정합성.

산식 정확성 3-layer:

  • L1 — tests/payroll/golden_kr_YEAR.json (국세청 간이세액표 fixture, 자동 CI)
  • L2 — 호수회계법인 정기 cross-check → 차이 시 adjustment event (append-only)
  • L3 — 직원 self payslip 본인 검수 → dispute → 재계산

Frame LISTEN handler (Tier 3)

frame 측 daemon frame listen-hive-payroll:

  • hive-postgres LISTEN hive_events (host.docker.internal:3800)
  • hive.payroll.finalized 받으면 자동 분개 Dr 5101 / Cr 1101 per employee
  • idempotency = SHA256(period + employee + payment_reference)
  • backfill on startup (event_outbox unconsumed)
  • ack via consumed_by JSONB

산정 예시 (axev 2026-05, payroll v2 — 셀별 정확 일치 검증 완료)

직원직급지급합계과세표준공제합계실수령
강태훈대표 (임원)4,166,6673,966,667559,3903,607,277
한진우수석4,166,6673,966,667595,0903,571,577
강수훈파트너 (4/1 퇴사)24,660 (건보 환급)

산식 (D-hive-21): 식대 200K 비과세 → 과세 3,966,667 / 4대보험 보수월액 별도 신고치 (연금 4,186,222, 건보 4,022,567) / 소득세 = 국세청 2026 간이세액표 lookup / 임원 고용·산재 0.

Payroll v2 — 한국 도메인 본질 (D-hive-21)

기존 v1 의 단순 누진세 계산은 한국 표준 아님. v2 는 7가지 본질 반영:

  1. 비과세 분리 — 식대 / 차량보조금 월 200K 한도
  2. 간이세액표 lookupshared.simplified_income_tax_table (월 과세 × 부양가족수 → 세액). 누진세 직접 계산 X
  3. 부양가족·자녀세액공제employees.dependents_count + children_count_under_20
  4. 4대보험 보수월액 분리reported_income_pension_krw / reported_income_health_krw (각자 신고소득)
  5. 정산health_reconciliation_krw / yearend_reconciliation_krw (음수=환급, 양수=추가). payroll_periods.attributes.pending_reconciliations 사전 등록
  6. 퇴사자 처리termination_date 일할 또는 정산-only payslip
  7. 라운딩 — 국민·건강·장기 = 10원 단위 절상, 고용·소득세 = 원 단위 반올림

검증: tests/payroll/golden_axec_2026_04.json — 첨부 급여대장과 모든 셀 정확 일치.

급여명세서 메일 발송 (D-hive-22/23) — Phase 3.6

채널: Microsoft Graph SendMail

M365 SMTP AUTH 의 deprecation (사용자별 app password 필요) 회피. Azure AD App + Mail.Send Application permission + client_credentials → access token → POST /v1.0/users/{from}/sendMail. DKIM 자동 서명.

환경변수용도
AZURE_TENANT_IDM365 tenant GUID
HIVE_MAILER_CLIENT_IDhive-payroll-mailer AppId
HIVE_MAILER_CLIENT_SECRETclient secret (vault)
HIVE_MAILER_FROM발송 mailbox (예: [email protected])
HIVE_MAILER_FROM_NAMEdisplay name

DKIM / DMARC DNS (D-hive-23)

axellc.com Cloudflare zone 에 추가:

selector1._domainkey.axellc.com CNAME selector1-axellc-com._domainkey.<tenant>.n-v1.dkim.mail.microsoft selector2._domainkey.axellc.com CNAME selector2-axellc-com._domainkey.<tenant>.n-v1.dkim.mail.microsoft _dmarc.axellc.com TXT "v=DMARC1; p=none; rua=mailto:[email protected]; aspf=s; adkim=s"

⚠ Microsoft 가 2024년부터 DKIM 도메인을 .onmicrosoft.com 에서 .n-v1.dkim.mail.microsoft 로 변경. 정확한 값은 M365 Defender DKIM 화면에서 확인.

메시지 템플릿 관리 (D-hive-24)

shared.message_templates ◀── AXE 공통 default (fallback 또는 clone source) └ {entity}.message_templates ◀── per-entity customize (override)
  • Jinja2 SandboxedEnvironment + StrictUndefined (변수 누락 = render error 로 즉시 발견)
  • 변수 선언 (variables JSONB) — 운영자가 사용 가능 변수 한눈에 확인
  • partial unique index — 동일 (code, language) 의 active 2개 동시 불가
  • effective_from/until lifecycle — 산식 패턴과 동일 (시점별 적용)

Custom template 강제 (D-hive-25)

send_payslip_email lookup 이 require_entity_override=True — shared default fallback 차단. entity 측 active template 없으면 TemplateNotCustomizedError. 운영자가 한 번은 customize 거쳐야 발송 가능.

해결 흐름 (1회):

notify_template_clone_from_shared(entity_id="axev", code="payslip_email") notify_template_update(entity_id, scope="entity", template_id, body_template="...") notify_template_preview(entity_id, template_id, scope="entity", sample_payslip_id) notify_template_activate(entity_id, scope="entity", template_id)

발송·송금 audit log (D-hive-25)

TableRow 발생 시점핵심 컬럼
{entity}.payslip_dispatch_logexport_remittance 호출 → mark_paid 시 동일 row updateperiod_id / dispatch_kind (‘remittance_export’ or ‘paid_finalized’) / employees_snapshot (bank_last4 only — D-hive-19) / total_amount_krw / file_sha256 / payment_reference / finalized_at
{entity}.payslip_send_logsend_payslip_email 1회 = 1 row (sent/dry_run/failed 모두)payslip_id / to_email / subject / body_sha256 / template_id + template_source / channel (‘graph_api’/‘smtp’/‘dry_run’) / status / error_code / error_message

body_sha256 = render 결과 본문 해시. 동일 메일 재발송 검증·변경 감지·분쟁 시 회신 증거.

MCP Tools 추가 (Phase 3.6)

도구권한용도
notify_template_list(entity_id, scope='both', code?, include_drafts?)read템플릿 목록 (entity / shared / both)
notify_template_get_active(entity_id, code, language='ko')read현재 active 1건 (룩업 우선순위 적용)
notify_template_clone_from_shared(entity_id, code, language?, effective_from?)adminshared active → entity draft 복사
notify_template_create(entity_id, scope, code, subject_template, body_template, ...)admin신규 draft
notify_template_update(entity_id, scope, template_id, subject_template?, body_template?)admindraft 수정 (active 는 immutable)
notify_template_preview(entity_id, template_id+scope OR code, sample_payslip_id?)readrender preview
notify_template_activate(entity_id, scope, template_id)admindraft → active (기존 자동 마감)
payroll_send_payslip(entity_id, payslip_id, dry_run?, attach_pdf?)admin단일 발송 (Graph API)
payroll_send_all_payslips(entity_id, period_id, dry_run?, only_employees?, attach_pdf?)admin일괄 발송
payroll_list_send_log(entity_id, period_id?, employee_id?, status?, limit?)read메일 발송 audit
payroll_list_dispatch_log(entity_id, period_id?, dispatch_kind?, limit?)read송금 의사 snapshot 조회
payroll_preview_payslip_pdf(entity_id, payslip_id)adminPDF base64 미리보기

관련 문서

Last updated on