3. Workspace(?)
workspace = 회사 (또는 사업부) 의 격리·거버넌스 단위개념 정리 — 왜 4 단계로 나누는가
한 회사가 여러 client 의 자산을 보관할 때, 모든 자산을 한 통에 섞으면 누가 무엇을 가지고 있는지 추적 불가능. 그래서 다음처럼 계층을 나눈다:
| 단계 | 역할 | 비유 |
|---|---|---|
| Workspace | 회사 전체의 격리 공간 | "한 회사 (또는 사업부) 의 전체 자산 관리 영역". 다른 회사의 자산과 완전 분리 |
| Vault Account | workspace 안의 자산 그룹 | "한 client / 한 팀 / 한 용도 단위로 묶은 금고". workspace 안에 여러 개 만듦 |
| Asset Wallet | 한 chain 의 자산 보관 | "한 vault account 안의 chain 별 지갑" (BTC 지갑 · ETH 지갑 · SOL 지갑 등) |
| Address | 실제 chain 주소 | "한 asset wallet 의 실제 입출금 주소". chain 종류에 따라 1 개 또는 여러 개 (5. Wallet 페이지 참조) |
1. Workspace — 최상위 격리 공간
Fireblocks 직접 인용: "Every workspace requires one (and only one) Owner to set up the Vault."
→ 의미: Vault 를 운영하려면 workspace 에 정확히 1 명의 Owner 가 존재해야 한다는 필요조건. Owner 만 vault setup 작업을 할 수 있다는 뜻이 아님 — 실제 vault account 생성은 Owner / Admin / NSA / Signer / Approver / Editor 모두 가능 (권한표 "Assets and addresses" 절).
1.1 핵심 규칙
- 1 workspace = 1 Owner 강제 (DB UNIQUE constraint)
- workspace type 2 종: hot / cold (아래 표 참조)
- workspace-level 거버넌스 작업 (Policy 변경 · Admin Quorum 변경 · AML 설정 · Freeze 등) 은 모두 다수결 승인 흐름 (Q / Q+O)
1.2 Workspace type 2 종 — 무엇을 의미하는가
| type | 의미 | 사용 예 |
|---|---|---|
| hot | 일상적인 자산 운영 workspace. 인터넷 연결 · 정상 거버넌스 흐름 작동 | 대부분의 입출금 / 거래소 연동 / 매일 운영. 보통 회사에서 가장 많이 사용 |
| cold | 장기 보관용 workspace. 별도 격리 / 운영 빈도 매우 낮음 | 대규모 자산의 보관용 — 잘 안 꺼냄. 거버넌스 흐름이 더 엄격 |
1.3 workspaces 테이블
CREATE TABLE workspaces (
id BINARY(16) PRIMARY KEY,
type ENUM('hot', 'cold') NOT NULL,
owner_user_id BINARY(16) NOT NULL UNIQUE,
name VARCHAR(128) NOT NULL,
aml_default ENUM('fail-on-unknown', 'pass-on-unknown') NOT NULL,
created_by BINARY(16) NOT NULL,
created_at DATETIME(6) NOT NULL,
modified_by BINARY(16) NOT NULL,
modified_at DATETIME(6) NOT NULL,
frozen_at DATETIME(6),
frozen_by BINARY(16),
KEY (frozen_at)
);
1.4 필드별 설명
| 컬럼 | 자료형 | 역할 |
|---|---|---|
id | BINARY(16) PK | workspace 의 고유 식별자. UUID 를 16-byte binary 로 저장 (string 보다 50% 작고 빠름) |
type | ENUM | 위 1.2 표의 2 종 (hot / cold) 중 하나 |
owner_user_id | BINARY(16) UNIQUE | workspace 의 유일한 Owner 의 user id. UNIQUE 제약으로 "한 workspace 의 Owner 는 1 명" 을 DB 레벨에서 강제 |
name | VARCHAR(128) | 사람이 읽는 이름표 (예: "Acme Corp Production", "Trading Desk") |
aml_default | ENUM | AML 정책의 기본값 — 모르는 주소에 대한 처리: · fail-on-unknown: 처음 보는 destination 은 차단 (안전 우선) · pass-on-unknown: 처음 보는 destination 도 통과 (외부 분석에 의존) |
created_by | BINARY(16) | workspace 를 만든 user id. set-once — 이후 변경 금지 |
created_at | DATETIME(6) | workspace 생성 시각 (마이크로초 정밀). set-once |
modified_by | BINARY(16) | 마지막 변경자의 user id. 변경 발생 시마다 갱신. 빠른 조회용 — 자세한 이력은 1.5 change log |
modified_at | DATETIME(6) | 마지막 변경 시각. 변경 발생 시 자동 갱신. 무엇이 변경되었는지는 1.5 change log 에서 확인 |
frozen_at | DATETIME(6) nullable | Freeze 발동 시각. NULL = 정상 운영 중. 값 있음 = 모든 작업 정지 상태 |
frozen_by | BINARY(16) nullable | 누가 freeze 했는지의 user id. Owner / Admin / NSA / Security Admin 4 role 만 가능 |
1.5 workspace_change_events — 모든 변경의 append-only 이력
workspace 의 모든 field 변경마다 1 row 추가. UPDATE / DELETE 금지 (audit-grade evidence).
CREATE TABLE workspace_change_events (
id BINARY(16) PRIMARY KEY,
workspace_id BINARY(16) NOT NULL,
event_type ENUM('created', 'renamed', 'type-changed', 'owner-transferred', 'aml-default-changed') NOT NULL,
field_name VARCHAR(64) NOT NULL,
old_value JSON,
new_value JSON,
actor_user_id BINARY(16) NOT NULL,
approval_request_id BINARY(16),
audit_event_id BINARY(16),
occurred_at DATETIME(6) NOT NULL,
KEY (workspace_id, occurred_at),
KEY (event_type)
);
| 컬럼 | 자료형 | 역할 |
|---|---|---|
id | BINARY(16) PK | 이벤트 고유 식별자 |
workspace_id | BINARY(16) | 어느 workspace 의 변경인지 |
event_type | ENUM (5 값) | created (최초 생성), renamed (name 변경), type-changed (hot↔cold migration), owner-transferred (Transfer Owner), aml-default-changed (AML 정책 변경) |
field_name | VARCHAR(64) | 변경된 컬럼명 (예: name, owner_user_id). 한 event 가 여러 컬럼 변경 시 여러 row |
old_value · new_value | JSON nullable | 변경 전/후 값. created event 시 old=NULL |
actor_user_id | BINARY(16) | 변경을 실행한 사람의 user id |
approval_request_id | BINARY(16) nullable | Q+O 승인 흐름을 거친 변경의 경우 (aml-default-changed 등) 해당 approval_request 의 id. 즉시 변경 시 NULL |
audit_event_id | BINARY(16) nullable | auditdb.audit_events 의 해당 event id (cross-DB binding). outbox pattern 으로 비동기 영속화 — auditdb 의 hash chain 에도 포함 |
occurred_at | DATETIME(6) | 변경 발생 시각 |
1.6 workspace_freeze_events — Freeze 이력 테이블
CREATE TABLE workspace_freeze_events (
id BINARY(16) PRIMARY KEY,
workspace_id BINARY(16) NOT NULL,
event_type ENUM('freeze', 'unfreeze') NOT NULL,
actor_user_id BINARY(16) NOT NULL,
support_ticket_id VARCHAR(64),
occurred_at DATETIME(6) NOT NULL
);
| 컬럼 | 자료형 | 역할 |
|---|---|---|
id | BINARY(16) PK | 이벤트 고유 식별자 |
workspace_id | BINARY(16) | 어느 workspace 에 발생한 이벤트인지 |
event_type | ENUM | freeze (정지 발동) 또는 unfreeze (정지 해제) |
actor_user_id | BINARY(16) | 이 이벤트를 실행한 사람의 user id |
support_ticket_id | VARCHAR(64) nullable | unfreeze 는 Fireblocks Support 경유 필수 — 그 ticket 번호. freeze 시엔 NULL |
occurred_at | DATETIME(6) | 이벤트 발생 시각 |
이 테이블은 append-only (UPDATE / DELETE 금지) — freeze 이력은 audit evidence 라서 영구 보존.
1.7 왜 change_events 와 freeze_events 를 분리하는가
두 테이블을 합쳐서 하나로 만들 수도 있다 — `event_type` ENUM 에 'freeze' / 'unfreeze' 만 추가하면 가능. 그럼에도 본 reference 는 분리 유지를 권장하는 이유:
| 근거 | 설명 |
|---|---|
| ① 의미적 도메인 분리 | change_events 는 workspace metadata 의 진화 (rename, owner transfer, AML 정책 변경). freeze_events 는 emergency 운영 상태 전환. 같은 도메인의 이벤트가 아님. |
| ② 전용 필드의 schema 명확성 | freeze 만 갖는 컬럼 (support_ticket_id — unfreeze 시 Fireblocks Support 경유 증빙) 이 한 테이블에 강제됨. 통합 시 JSON 으로 흡수해야 해서 schema 명세성이 떨어짐. |
| ③ Hot path query 분리 | "지금 frozen 인가?" 는 매우 빈번 (모든 작업 시작 시 체크). 이 query 가 metadata 변경 이력 (수년치 누적) 까지 스캔하지 않게 함. 작은 freeze_events 테이블만 보면 끝. |
| ④ Index 효율 | 각 테이블의 분포와 query 패턴이 달라서 index 설계도 다름. 통합하면 어느 쪽도 최적 아닌 index 가 됨. |
| ⑤ 권한·운영 분리 가능성 | future scaling 시 freeze 만 별도 incident DB 로 이전, change_events 는 walletdb 유지 같은 옵션 열려 있음. 통합되어 있으면 분리 마이그레이션 부담. |
두 테이블 모두 audit_event_id 로 auditdb.audit_events 와 cross-DB binding 됨. walletdb 안에서는 의미별로 분리하되, auditdb 의 hash chain 에서는 통합되어 workspace 전체 audit trail 이 한 곳에 영속화됨 — best of both worlds.
다음 상황에선 1 테이블로 통합 시작 후 점진 분리:
- 작은 institution — 운영 부담 최소화 우선
- 이벤트 빈도가 매우 낮음 — 테이블 분리의 효율 이점 없음
- 모든 이벤트의 통합 timeline 이 주 use case (workspace audit dashboard 한 query)
1.8 Workspace Freeze 모델 — 풀어 쓰기
- 모든 user 의 role 이 일시적으로 Viewer 로 강제 변경 (Owner 포함). 즉 누구도 자금 이동·설정 변경 불가
- 차단되는 작업: 외부 송금 · 새 주소 whitelist · 새 거래소 연결 · P2P 네트워크 연결
- ★ Incoming transfer (입금) 는 계속 수신 — 외부에서 우리 주소로 들어오는 코인은 막을 수 없음 (chain 의 본질)
- Freeze 발동: Owner / Admin / NSA / Security Admin 4 role 가능
- Unfreeze: Owner only via Fireblocks Support (Console 으로 직접 못 푸는 안전장치)
참고글
- Fireblocks — User roles · workspace 의 9 role 권한표
- Fireblocks — Freeze your workspace · 비상 freeze 흐름 + Unfreeze 절차
- 4. Vault · workspace 안의 자산 보관 단위 (vault account)
- 2. DB 분할 · workspaces 가 walletdb 에 속하는 이유
- 9. Admin Quorum · Approval Group · workspace-level 거버넌스 (Q / Q+O / AG)
- 20. Audit Log · workspace_change_events / freeze_events 의 cross-DB hash chain 영속화
- 24. Storage Discipline · append-only / set-once 규율