4. Vault Account(?)
workspace 안의 자산 보관 단위 — 다중 role · 변경 이력 · vault 구성 패턴 4 종 (Omnibus / Segregated / Treasury / Withdrawal)Vault Account — workspace 안의 자산 그룹
한 workspace 안에서 client / 팀 / 용도별로 자산을 나누는 단위. vault 의 생성·변경·archive 이력은 1.5 절 의 vault_account_change_events 가 영속화.
1.1 vault_accounts 테이블
CREATE TABLE vault_accounts (
id BINARY(16) PRIMARY KEY,
workspace_id BINARY(16) NOT NULL,
name VARCHAR(128) NOT NULL,
hidden BOOLEAN NOT NULL DEFAULT FALSE,
created_by BINARY(16) NOT NULL, -- 생성 요청한 user
created_at DATETIME(6) NOT NULL,
approved_by_quorum_id BINARY(16) NOT NULL, -- Admin Quorum 승인 (set-once, 생성용)
approved_at DATETIME(6) NOT NULL, -- threshold 충족 시각 (set-once)
modified_by BINARY(16) NOT NULL,
modified_at DATETIME(6) NOT NULL,
archived_at DATETIME(6), -- archive 시각 (set-once when set)
archived_by BINARY(16), -- archive 실행한 user (set-once when set)
KEY (workspace_id)
);
1.2 필드별 설명
| 컬럼 | 자료형 | 역할 |
|---|---|---|
id | BINARY(16) PK | vault account 의 고유 식별자 |
workspace_id | BINARY(16) | 어느 workspace 에 속하는지. FK 검증은 같은 DB 안에서만 |
name | VARCHAR(128) | 사람이 읽는 이름표 (예: "Client Acme - USD vault", "Mint authority") |
hidden | BOOLEAN | UI 에서 숨김 처리 여부. soft-hide — 자료는 그대로, 화면에 안 보일 뿐 |
created_by | BINARY(16) | 이 vault account 를 생성 요청 한 user id (= 요청자). set-once |
created_at | DATETIME(6) | 생성 요청 시각. set-once |
approved_by_quorum_id | BINARY(16) | Admin Quorum 의 id (FK → admin_quorums.id). vault 생성에는 quorum 승인이 필수 — 우리 시스템은 Fireblocks 보다 엄격하게 vault 생성을 approval-gated 로 처리. set-once |
approved_at | DATETIME(6) | threshold (N-of-M) 충족 시각. 이 row 는 승인 완료 후에만 INSERT — pending 상태는 별도 approval_requests 평면에서 관리. set-once |
modified_by | BINARY(16) | 마지막 변경자의 user id. 변경 발생 시마다 갱신. 자세한 이력은 1.5 change log |
modified_at | DATETIME(6) | 마지막 변경 시각. 무엇이 변경되었는지는 1.5 change log |
archived_at | DATETIME(6) nullable | archive (영구 숨김) 시각. NULL = 활성. hide 와 다르게 archive 는 더 강한 soft-delete. set-once when set |
archived_by | BINARY(16) nullable | archive 를 실행한 user id. archive 발생 시에만 set, set-once. admin 단독 동사 (quorum-gated 아님) |
★ role 은 별도 junction table 로 — 하나의 vault 가 다중 role (예: treasury + general) 을 동시 보유 가능하므로 vault_accounts 본체에 단일 ENUM 으로 두지 않음. 다음 1.3 절 참조.
vault 의 lifecycle 동사 중 어디까지를 Admin Quorum 승인으로 막을지의 현재 design:
- quorum-gated:
create— vault 자체의 존재가 자산 책임 경계를 정의하므로 단독 admin 으로 못 만듦 - admin 단독:
rename·hide·unhide·archive— 자산 자체에 영향을 주지 않는 UI / 가시성 변경.modified_by또는archived_by+ change_events 의actor_user_id만 - 참고:
vault_account_roles의 grant/revoke 도 현재 admin 단독 (1.3 절). role 변경이 운영 위험을 키울 수 있다면 별도 재검토 가능
- Approver-level audit trail (N-of-M 의 specific signatures) — 본 schema 의
approved_by_quorum_id는 quorum 자체의 id 만 가리키므로 "그 시점에 누가 sign 했는지" 의 trail 이 없음. Quorum membership 이 시간에 따라 바뀌므로 historical signer trail 은 별도 audit 평면에서 보존 필요. → 20. Cross-DB Audit 의approval_decisions(approver 별mobile_device_sig) +audit_eventshash chain 패턴 - Pending 상태의 approval workflow — vault 생성 요청은 quorum threshold 충족 전까지 pending. 그 상태 (request_id, 누구에게 routed, 누가 sign, 누가 reject, expire 시각) 는 별도
approval_requests평면 (Add user 의 7-day expiry 와 같은 메커니즘) 에서 영속화. → 9. Admin Quorum
1.3 vault_account_roles — vault 의 다중 role 부여
하나의 vault 가 여러 role 동시 보유 가능 (예: treasury + general). 각 role 독립 grant / revoke + audit trail.
CREATE TABLE vault_account_roles (
id BINARY(16) PRIMARY KEY,
vault_account_id BINARY(16) NOT NULL,
role ENUM('treasury', 'client', 'intermediate', 'withdrawal', 'general') NOT NULL,
granted_by BINARY(16) NOT NULL,
granted_at DATETIME(6) NOT NULL,
revoked_by BINARY(16),
revoked_at DATETIME(6),
KEY (vault_account_id, role),
KEY (vault_account_id, revoked_at)
);
| 컬럼 | 자료형 | 역할 |
|---|---|---|
id | BINARY(16) PK | role grant 의 고유 식별자 |
vault_account_id | BINARY(16) | 어느 vault account 의 role 부여인지 |
role | ENUM (5 값) | vault 의 용도 분류 (다음 1.4 절 표 참조) |
granted_by | BINARY(16) | 부여자 user id |
granted_at | DATETIME(6) | 부여 시각. set-once |
revoked_by | BINARY(16) nullable | 회수자 user id. NULL = 현재 active |
revoked_at | DATETIME(6) nullable | 회수 시각. NULL = active. set-once |
- 1 vault → N roles: 같은 vault 가 treasury + general 등 동시 보유 가능
- 독립 grant / revoke: 한 role 만 회수 + 나머지 유지 같은 부분 변경 가능
- 독립 audit trail: 각 role 부여·회수가 자신의 row + timestamp + user 정보 보유
- set-once row: revoke 후 같은 role 을 재부여할 때 새 row INSERT → role 부여 history 누적
- 현재 active role 조회:
SELECT role FROM vault_account_roles WHERE vault_account_id=? AND revoked_at IS NULL
1.4 role ENUM 5 값 — 각 값의 의미
| 값 | 의미 | 전형적 정책 |
|---|---|---|
| treasury | 회사 전체의 메인 자금 vault. 모든 자산의 최상위 저장소 | 가장 엄격 — 큰 금액 송금에 다수 승인 강제 |
| client | 한 개별 client 의 자산 보관 | 해당 client 의 입출금 한도 + KYC 기준 |
| intermediate | 중계용 vault. EVM 처럼 "1 vault = 1 address" 제약 chain 에서 client 별로 unique 입금 주소 제공하려고 만듦 | 입금만 받고 즉시 treasury 로 sweep |
| withdrawal | 외부로 송금하는 전용 vault. 여러 개 두고 round-robin 으로 사용해서 chain 의 sequential bottleneck 회피 | EVM 의 nonce 충돌 / Bitcoin 의 25-tx chain limit(?) 회피 |
| general | 위 분류에 안 맞는 일반 용도 | 기본 정책 |
위 5 종 (token / contract 권한) 은 수탁형 지갑의 본연 기능이 아님:
- 수탁형 지갑 = 고객 자산 보관 + 송금 이 본질
- mint / burn = token issuer (Circle, Tether 등) 의 작업
- pause / deploy / upgrade = protocol operator 의 작업
Fireblocks 가 이를 vault 권한으로 다루는 이유는 plain custodian 이 아니라 digital asset operations platform 이고, 고객 중에 token issuer / protocol team 이 있어서. 순수 custodial wallet 만 구축한다면 위 5 종은 schema 에 포함시키지 않음.
Hybrid (수탁 + token operation) use case 가 필요한 경우: 별도 vault_contract_roles 테이블로 (vault_account_id, chain, contract_address, role) 4-tuple key 의 구조 추가. 본 문서에서는 다루지 않음.
1.5 vault_account_change_events — vault 본체 변경 이력
vault account 의 본체 변경 (rename / hide / unhide / archive) 이력.
CREATE TABLE vault_account_change_events (
id BINARY(16) PRIMARY KEY,
vault_account_id BINARY(16) NOT NULL,
event_type ENUM('created', 'renamed', 'hidden', 'unhidden', 'archived') NOT NULL,
field_name VARCHAR(64) NOT NULL,
old_value JSON,
new_value JSON,
actor_user_id BINARY(16) NOT NULL,
approved_by_quorum_id BINARY(16), -- quorum-gated event (현재 created 만) 에서만 set
audit_event_id BINARY(16),
occurred_at DATETIME(6) NOT NULL,
KEY (vault_account_id, occurred_at),
KEY (event_type)
);
| 컬럼 | 자료형 | 역할 |
|---|---|---|
id | BINARY(16) PK | 이벤트 고유 식별자 |
vault_account_id | BINARY(16) | 어느 vault account 의 변경인지 |
event_type | ENUM (5 값) | created · renamed · hidden · unhidden · archived |
field_name | VARCHAR(64) | 변경된 컬럼명 (예: name, hidden) |
old_value · new_value | JSON nullable | 변경 전/후 값. created event 시 old=NULL |
actor_user_id | BINARY(16) | 변경 실행자의 user id (= 변경을 요청 한 user). quorum-gated event 의 경우 요청자가 quorum approver 자신일 필요는 없음 |
approved_by_quorum_id | BINARY(16) nullable | 이 이벤트가 quorum-gated 일 때만 set — 현재 created 1 종. renamed · hidden · unhidden · archived 는 admin 단독 (NULL) |
audit_event_id | BINARY(16) nullable | auditdb.audit_events 의 cross-DB binding |
occurred_at | DATETIME(6) | 변경 발생 시각 |
- 외부 감사 대응: 분기별 SOC 2(?) 감사 시 "이 workspace 의 owner 가 언제 누구에게 transferred 되었나?" / "AML 정책이 언제 변경되었나?" 같은 질문에 즉시 답할 수 있어야 함
- 법적 책임 추적: 사고 발생 시 "누가 언제 무엇을 바꿨다" 의 cryptographic evidence (auditdb 의 hash chain 으로도 영속화) 필수
- UPDATE 만으로는 부족:
modified_at은 "마지막 변경 시점" 만 알 뿐, 무엇이 어떻게 변했는지는 별도 log 가 있어야 알 수 있음
Vault Structure 4 가지 패턴 — 실제 운영 방식
출처: vault-structure-best-practices.md
| 패턴 | 설명 + 언제 쓰나 |
|---|---|
| Omnibus (통합형) | 중앙 treasury vault 1 개 + client 별 intermediate vault. EVM 처럼 1 vault = 1 address 인 chain 에서 client 별 unique 입금 주소를 만들려고 intermediate 를 둠. 입금되면 즉시 treasury 로 sweep. |
| Segregated (분리형) | per-client / per-team / per-operation 으로 vault 를 완전히 분리. 규제상 client 자산 분리가 필요하거나 회계상 명확한 attribution 이 필요할 때. |
| Treasury vault | 전체 자금의 메인 vault. 가장 엄격한 policy 적용 (다수 승인 + 큰 금액 cap + 외부 주소 whitelist 만). |
| Smart contract per-op vault (hybrid use case) | ※ 순수 수탁형 지갑 본연 기능 아님 — token issuer / protocol operator 가 custody 와 함께 사용하는 hybrid 패턴. contract 의 특권 작업마다 별도 vault 분리해서 권한 노출 최소화. |
| Withdrawal vault round-robin | 외부 송금 vault 를 여러 개 두고 순환 사용. 한 vault 의 tx 가 stuck 되어도 다른 vault 로 진행 가능. EVM 의 nonce 순차성 / Bitcoin 의 25-tx unconfirmed chain limit 같은 chain 의 sequential bottleneck 회피. |
참고글
- Fireblocks — Vault Structure Best Practices · Omnibus / Segregated / Treasury / Withdrawal vault 패턴 출처
- 3. Workspace · vault account 가 속하는 상위 격리 단위
- 5. Wallet & Address · vault account 안의 asset wallet · address 구조
- 10. Policy · TAP · vault 별 정책 (treasury / withdrawal 등)
- 20. Audit Log · vault_account_change_events 의 cross-DB hash chain 영속화
- 24. Storage Discipline · append-only / set-once 규율