3. Workspace(?)

workspace = 회사 (또는 사업부) 의 격리·거버넌스 단위

개념 정리 — 왜 4 단계로 나누는가

한 회사가 여러 client 의 자산을 보관할 때, 모든 자산을 한 통에 섞으면 누가 무엇을 가지고 있는지 추적 불가능. 그래서 다음처럼 계층을 나눈다:

단계역할비유
Workspace회사 전체의 격리 공간"한 회사 (또는 사업부) 의 전체 자산 관리 영역". 다른 회사의 자산과 완전 분리
Vault Accountworkspace 안의 자산 그룹"한 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 필드별 설명

컬럼자료형역할
idBINARY(16) PKworkspace 의 고유 식별자. UUID 를 16-byte binary 로 저장 (string 보다 50% 작고 빠름)
typeENUM위 1.2 표의 2 종 (hot / cold) 중 하나
owner_user_idBINARY(16) UNIQUEworkspace 의 유일한 Owner 의 user id. UNIQUE 제약으로 "한 workspace 의 Owner 는 1 명" 을 DB 레벨에서 강제
nameVARCHAR(128)사람이 읽는 이름표 (예: "Acme Corp Production", "Trading Desk")
aml_defaultENUMAML 정책의 기본값 — 모르는 주소에 대한 처리:
· fail-on-unknown: 처음 보는 destination 은 차단 (안전 우선)
· pass-on-unknown: 처음 보는 destination 도 통과 (외부 분석에 의존)
created_byBINARY(16)workspace 를 만든 user id. set-once — 이후 변경 금지
created_atDATETIME(6)workspace 생성 시각 (마이크로초 정밀). set-once
modified_byBINARY(16)마지막 변경자의 user id. 변경 발생 시마다 갱신. 빠른 조회용 — 자세한 이력은 1.5 change log
modified_atDATETIME(6)마지막 변경 시각. 변경 발생 시 자동 갱신. 무엇이 변경되었는지는 1.5 change log 에서 확인
frozen_atDATETIME(6) nullableFreeze 발동 시각. NULL = 정상 운영 중. 값 있음 = 모든 작업 정지 상태
frozen_byBINARY(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)
);
컬럼자료형역할
idBINARY(16) PK이벤트 고유 식별자
workspace_idBINARY(16)어느 workspace 의 변경인지
event_typeENUM (5 값)created (최초 생성), renamed (name 변경), type-changed (hot↔cold migration), owner-transferred (Transfer Owner), aml-default-changed (AML 정책 변경)
field_nameVARCHAR(64)변경된 컬럼명 (예: name, owner_user_id). 한 event 가 여러 컬럼 변경 시 여러 row
old_value · new_valueJSON nullable변경 전/후 값. created event 시 old=NULL
actor_user_idBINARY(16)변경을 실행한 사람의 user id
approval_request_idBINARY(16) nullableQ+O 승인 흐름을 거친 변경의 경우 (aml-default-changed 등) 해당 approval_request 의 id. 즉시 변경 시 NULL
audit_event_idBINARY(16) nullableauditdb.audit_events 의 해당 event id (cross-DB binding). outbox pattern 으로 비동기 영속화 — auditdb 의 hash chain 에도 포함
occurred_atDATETIME(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
);
컬럼자료형역할
idBINARY(16) PK이벤트 고유 식별자
workspace_idBINARY(16)어느 workspace 에 발생한 이벤트인지
event_typeENUMfreeze (정지 발동) 또는 unfreeze (정지 해제)
actor_user_idBINARY(16)이 이벤트를 실행한 사람의 user id
support_ticket_idVARCHAR(64) nullableunfreeze 는 Fireblocks Support 경유 필수 — 그 ticket 번호. freeze 시엔 NULL
occurred_atDATETIME(6)이벤트 발생 시각

이 테이블은 append-only (UPDATE / DELETE 금지) — freeze 이력은 audit evidence 라서 영구 보존.

1.7 왜 change_eventsfreeze_events 를 분리하는가

두 테이블을 합쳐서 하나로 만들 수도 있다 — `event_type` ENUM 에 'freeze' / 'unfreeze' 만 추가하면 가능. 그럼에도 본 reference 는 분리 유지를 권장하는 이유:

근거설명
① 의미적 도메인 분리change_events 는 workspace metadata 의 진화 (rename, owner transfer, AML 정책 변경). freeze_eventsemergency 운영 상태 전환. 같은 도메인의 이벤트가 아님.
② 전용 필드의 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 유지 같은 옵션 열려 있음. 통합되어 있으면 분리 마이그레이션 부담.
walletdb 안에서는 분리, auditdb 안에서는 통합

두 테이블 모두 audit_event_idauditdb.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 모델 — 풀어 쓰기

Emergency 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 으로 직접 못 푸는 안전장치)

참고글