제5장

제5장: 교환 프로토콜과 감사 추적

생물안전 레코드가 한 기관을 떠나 다른 기관에 도착할 때 의미와 출처, 이력을 잃지 않게 하는 방법을 다룬다.

제2장과 제3장은 한 기관 안에 있는 레코드를 다뤘다. 연구시설 등록, 교육 이수 기록, 여러 상태를 거쳐 가는 사고 보고서가 그것이다. 제4장은 이 레코드들 앞에 서비스 인터페이스를 세웠다. 이 장은 레코드가 기관의 경계를 넘는 순간을 다룬다. 한 대학이 발급한 교육 이수 증명을 협력 병원에 제시하는 경우가 있다. 국가 기관이 분기별 사고 통계를 요구하는 경우, 점검자가 시설 인증 이력을 읽기만 하고 고쳐서는 안 되는 경우도 있다. 등록된 물질이 한 등록 시설에서 다른 등록 시설로 옮겨질 때는 양쪽 모두가 누가 넘겼고 누가 받았으며 어떤 승인에 근거했는지를 나중에 입증할 수 있어야 한다.

이런 경계 통과에는 모두 같은 세 가지 질문이 따른다. 첫째, 수신자는 레코드가 정말 송신자에게서 왔고 도중에 바뀌지 않았다고 믿을 수 있는가. 둘째, 같은 메시지를 두 번 받거나 오래된 메시지를 받아도 자기 상태가 망가지지 않는가. 셋째, 몇 달이나 몇 년 뒤에도 무슨 일이 어떤 순서로 일어났고 누가 했는지를 정확히 재구성할 수 있는가. 첫째 질문에는 메시지 봉투(envelope)가, 둘째 질문에는 멱등성·재전송 방지 규칙이, 셋째 질문에는 추가만 가능한(append-only) 감사 추적이 답한다. 감사가 시작되면 이 셋은 결국 데이터로 제출되므로, 이 장은 셋 모두를 데이터 구조로 다룬다.

규범 사항과 제안 사항의 구분. WIA-BIO-017의 PHASE-3 프로토콜 문서는 보안 기준선(전송 구간 TLS 1.3, 저장 데이터 AES-256, RBAC, 다중 인증, 포괄적 감사 로깅, OAuth 2.0과 API 키)을 제시한다. 부속서에는 스키마 진화, 시간 동기화, 상호운용 프로파일, 배치 토폴로지에 관한 비규범적 참고 사항이 있다. 그러나 메시지 봉투, AuditEvent 스키마, 보관 연속성 레코드는 정의하지 않는다. 이 장의 구조는 SDK 타입과 PHASE 부속서에 맞춰 이 책이 제안하는 설계이며, 인용이 아니라 제안인 부분은 그렇다고 밝힌다.

5.1 기관 간 교환 시나리오

봉투를 설계하기 전에, 누가 왜 생물안전 레코드를 주고받는지부터 정리해 둘 필요가 있다. 실무에서 기관이 받는 요청은 거의 모두 네 가지 시나리오 중 하나에 해당한다. 네 시나리오는 방향, 필요한 데이터의 양, 그리고 수신자가 받은 데이터를 보관해도 되는지에서 서로 다르다.

5.1.1 공동연구 시설 공유

두 기관이 공동 과제를 수행하고, A 기관 연구자가 B 기관이 운영하는 등록 시설에서 일하게 되었다고 하자. A 기관의 기관생물안전위원회(IBC)는 B 기관 시설이 등록되어 있는지, 인증이 유효한지, 승인된 과제에 기록된 등급과 시설의 허가 등급이 일치하는지에 대한 근거가 필요하다. B 기관의 평면도나 실별 공학적 점검표, 직원 명단은 필요하지 않다. 최소 페이로드는 제2장 BiosaftyFacility 레코드의 일부 투영이다. 즉 facilityId, name, bslLevel, certification 블록(certified, certificationDate, expirationDate, certifyingAuthority, nextInspectionDate), 그리고 해당 과제를 승인한 IBC 승인 참조로 충분하다. 건물과 실 정보는 기관 밖으로 내보내지 않는다. 제6장에서 설명하듯 시설의 정확한 위치 정보는 그 자체로 민감 정보다.

5.1.2 교육 이수 증명의 이동성

연구자가 기관을 옮기거나 두 기관에 동시에 소속되는 경우가 있다. 교환 체계가 없으면 받는 기관은 교육을 다시 시키거나, 검증할 방법이 없는 스캔본 수료증을 그대로 받아들이게 된다. 교환 체계가 있으면 받는 기관이 발급 기관에 특정 TrainingRecord의 확인을 요청한다. 확인 대상은 trainingType, trainingDate, expirationDate, competencyAssessment 통과 여부, certificateNumber이다. 받는 기관이 그 교육을 인정할지, 자체 현장 교육을 추가로 요구할지는 그 기관 생물안전관리책임자가 정할 정책 사항이다. 교환은 검증된 사실을 전달할 뿐이고 결정은 기관이 내린다. 이 시나리오의 전체 흐름은 5.2.4절의 순서도에서 다룬다.

5.1.3 국가 기관에 대한 사고 통계 집계 제출

일부 기관과 네트워크는 정기 요약을 요구한다. 보고된 사고와 아차사고의 건수, 유형과 심각도, 기한 내 종결된 시정 조치의 수 등이다. 이때 교환 단위는 IncidentReport가 아니라 여러 보고서에서 계산한 집계값이다. 인원 식별자, 자유 서술, 근본원인 서술은 기관 밖으로 나가지 않고, 건수만 수신처가 지정한 단위로 나간다. 건수가 너무 작아 특정 사건이나 인물을 가리킬 수 있는 칸은 송신 측이 숨기거나 더 큰 범주로 묶어야 한다. 억제 기준은 이 표준이 정하지 않으며 수신처의 보고 지침을 따른다. 개별 보고 대상 사고는 이 통계 경로가 아니라 제3장에서 설명한 통보 인계(NotificationRecord)를 따른다.

5.1.4 점검자의 읽기 전용 접근

점검이나 인정 평가 방문 중에 외부 점검자는 인증 이력, 승인된 인원의 교육 상태, 미결 시정 조치 같은 레코드를 원래 형태로 열람해야 한다. 올바른 방식은 내보내기(export)가 아니라, 기간과 범위가 제한된 읽기 전용 권한을 부여하는 것이다. 점검자의 열람 행위 자체도 감사 이벤트가 되므로, 기관은 나중에 무엇이 열람되었는지를 정확히 보여 줄 수 있다. 이 기록은 기관의 책임성을 입증할 때와, 무엇이 공개되었는지를 두고 이견이 생겼을 때 모두 쓸모가 있다.

시나리오방향교환 레코드 유형최소 내용수신자 보관 가능 여부
공동연구 시설 공유운영 기관 → 협력 기관 IBC시설 투영 + 승인 참조ID, 명칭, 등급 라벨, 인증 블록, 승인 ID가능(공동 과제 기간 동안)
교육 이수 증명 이동성발급 기관 → 검증 기관(요청 시)교육 이수 기록 증명유형, 일자, 통과 여부, 수료증 번호가능(인정 결정의 근거로)
사고 통계 집계기관 → 국가 기관·네트워크통계 요약기간·유형·심각도별 건수, CAPA 종결 건수가능(집계값만)
점검자 읽기 전용 접근점검자 ← 기관(조회)원본 레코드, 열람만권한 부여 범위 내사본 없음, 열람 기록 남음

마지막 열은 보기보다 중요하다. 데이터를 보관하는 수신자는 그 데이터의 두 번째 관리 주체가 되고, 보존·접근 통제·유출 대응 의무도 함께 진다. 따라서 시나리오마다 페이로드를 최소로 설계하는 것은 개인정보 보호 조치이면서, 보안과 감사가 필요한 장소의 수를 줄이는 방법이기도 하다.

5.2 메시지 봉투

기관 경계를 넘는 모든 레코드는 봉투에 담겨 이동한다. 봉투는 레코드를 바꾸지 않고, 수신자가 수락 여부를 판단하는 데 필요한 메타데이터만 덧붙인다. 봉투와 페이로드를 분리하면 실무상 이점이 있다. 페이로드 스키마는 제2장의 추가 전용 규칙에 따라 발전하더라도 봉투는 안정적으로 유지되고, 수신자는 본문을 파싱하기 전에 받을 수 없는 메시지를 걸러 낼 수 있다.

5.2.1 봉투 필드

필드형식목적
envelopeId문자열(UUID)이 전송의 고유 식별자. 시간 순서형 UUID(버전 7, RFC 9562)를 쓰면 로그에서 자연스럽게 정렬된다.
senderInstitutionId문자열연합 디렉터리에 등록된 송신 기관 식별자.
recipientInstitutionId문자열의도된 수신 기관. 다른 기관 앞으로 온 메시지는 거부하므로, 가로챈 메시지를 다른 곳으로 돌려보낼 수 없다.
schemaVersion문자열(SemVer)페이로드 스키마 버전. 수신자가 맞는 검증기를 고르는 데 쓴다.
recordType열거형예: facility-projection, training-attestation, incident-statistics, custody-transfer, custody-receipt.
createdAtISO 8601 UTC생성 시점의 송신 측 시각. 신선도 검사에 쓴다(5.5절).
sequence정수송신·수신 기관 쌍의 채널마다 단조 증가하는 카운터.
idempotencyKey문자열업무 작업 단위의 고정 키. 재시도할 때 그대로 재사용한다.
inReplyTo문자열, 선택이 메시지가 응답하는 요청의 envelopeId.
payloadHash문자열정규화한 페이로드의 SHA-256 다이제스트(알고리즘 접두어 포함).
signature객체정규화한 봉투(signature를 뺀 전체 필드)에 대한 분리 서명. 키 식별자와 알고리즘을 함께 적는다.

두 가지 설계 선택은 설명이 필요하다. 첫째, 해시는 페이로드의 정규화된 직렬화 결과에 대해 계산한다. JSON은 같은 데이터를 키 순서, 공백, 숫자 표기가 다른 여러 바이트열로 쓸 수 있으므로, 원시 바이트에 대한 해시는 중간 시스템이 문서를 다시 포맷하기만 해도 깨진다. JSON 정규화 방식(JCS, RFC 8785)은 결정적인 단일 형식을 정의하므로 기본값으로 삼기에 적당하다. 둘째, 서명은 봉투를 대상으로 하고 봉투에는 페이로드 해시가 들어 있다. 따라서 서명 하나가 송신자, 수신자, 순번, 시각, 내용을 한꺼번에 묶는다. 공격자는 서명을 무효로 만들지 않고서는 페이로드를 바꿔치기하거나, 다른 수신자에게 재전송하거나, 순번을 고칠 수 없다.

서명 형식은 배치(deployment)마다 선택할 사항이다. JSON 웹 서명(JWS, RFC 7515)은 웹 스택에서 널리 지원되고, 제7장이 적합성 증거에 쓰는 Sigstore의 DSSE 봉투도 선택지다. 상호운용에 중요한 것은 연합 프로파일이 서명 형식 하나와 허용 알고리즘 목록 하나를 정확히 지정하는 일이다. 또한 키를 유효기간과 함께 연합 디렉터리에 공개해야 검증자가 서명 시점에 그 키가 유효했는지를 판단할 수 있다.

5.2.2 봉투 예시

아래 예시는 교육 이수 증명을 담은 봉투다. 모든 식별자, 이름, 값은 가상이며 해시와 서명 값은 줄여 쓴 자리표시자다.

{
  "envelope": {
    "envelopeId": "0192f3a4-7c1e-7b20-9d3a-5e8f1c2b4a61",
    "senderInstitutionId": "INST-EXAMPLE-UNIV-A",
    "recipientInstitutionId": "INST-EXAMPLE-HOSP-B",
    "schemaVersion": "1.2.0",
    "recordType": "training-attestation",
    "createdAt": "2026-03-10T01:15:42Z",
    "sequence": 4187,
    "idempotencyKey": "verify-TRN-2025-000731-req-88c1",
    "inReplyTo": "0192f3a1-02aa-7f10-8b44-1d9e0c7a3b12",
    "payloadHash": "sha256:9f2c…e41a",
    "signature": {
      "format": "jws-detached",
      "alg": "ES256",
      "kid": "INST-EXAMPLE-UNIV-A#key-2026-01",
      "value": "eyJhbGciOiJFUzI1NiIs…"
    }
  },
  "payload": {
    "subjectRef": "PSN-A-7f3c21",
    "trainingRecordId": "TRN-2025-000731",
    "trainingType": "refresher",
    "trainingDate": "2025-11-04T00:00:00Z",
    "expirationDate": "2026-11-04T00:00:00Z",
    "competencyAssessmentPassed": true,
    "certificateNumber": "CERT-A-2025-1142",
    "attestedBy": "BSO-A-001",
    "attestationStatus": "valid"
  }
}

페이로드에 무엇이 빠져 있는지 눈여겨볼 필요가 있다. 평가자의 점수와 원본 레코드에 있을 수 있는 자유 서술 메모는 들어 있지 않다. 대상자는 주민등록번호 같은 국가 식별자가 아니라, 두 기관이 이 사람에 대해 합의한 가명 참조(subjectRef)로 식별한다. expirationDate는 발급 기관의 자체 정책을 반영한 값이다. 제2장에서 말했듯 보수교육 주기는 기관이 정하는 사항이며, 예시의 1년 간격은 설명을 위한 값일 뿐이다.

5.2.3 멱등성과 재전송 방지

네트워크는 재시도를 한다. 응답을 기다리다 시간이 초과된 송신자는 다시 보내고, 이미 첫 사본을 처리한 수신자는 인수 확인을 두 번 만들거나 사고를 두 번 세서는 안 된다. 재전송 공격은 같은 약점을 의도적으로 노린다. 다음 세 가지 장치를 함께 쓰면 이 틈을 막을 수 있다.

  1. 멱등성 키. 송신자는 업무 작업마다 키 하나를 만들고(예: "TRF-… 인수 확인"), 재시도할 때마다 같은 키를 쓴다. 수신자는 처리한 키와 그때 돌려준 결과를 함께 저장한다. 같은 키가 같은 페이로드 해시로 다시 오면 다시 처리하지 않고 저장해 둔 결과를 돌려준다. 같은 키가 다른 페이로드 해시로 오면 충돌로 보고, 어느 쪽이 맞는지 추측하지 않고 오류로 거부한다.
  2. 순번. 송신·수신 채널마다 단조 증가하는 sequence를 둔다. 수신자는 수락한 최고 순번과 최근 값의 작은 창을 추적한다. 이미 본 값은 중복이고, 창보다 한참 낮은 값은 오래된 메시지로 보고 거부한다. 순번이 비면 메시지가 유실되었다는 신호이므로, 조용히 받아들이지 말고 재동기화를 요청한다.
  3. 신선도 창. 수신자는 createdAt을 자기 시계와 비교해, 연합 프로파일에서 합의한 허용 창 밖의 메시지를 거부한다. 온라인 교환에서는 수 분 정도의 창이 흔한 출발점이다(예시). 망 분리 일괄 교환(5.6절)은 훨씬 넓은 창이 필요하며, 순번과 서명에 더 크게 의존한다.

멱등성 저장소도 보존 기간이 있는 레코드다. 송신자가 재시도할 수 있는 기간보다 길게 키를 보관해야 하며, 실제로는 봉투 로그와 같은 기간 동안 보관하는 편이 가장 단순하다. 멱등성 데이터는 크기가 작다. 이를 일찍 지우면 느린 협력 기관 시스템이 복구되어 대기열을 한꺼번에 쏟아 내는 바로 그 순간에 중복 문제가 되살아난다.

5.2.4 따라가 보기: 교육 이수 증명 검증

아래 순서도는 5.1.2절 시나리오의 처음부터 끝까지를 보여 준다. 발급 기관이 연구자의 개인정보를 제3자에게 제공하게 되므로, 요청을 보내기 전에 받는 기관이 연구자의 동의를 받는다.

연구자            B 병원(검증 기관)            디렉터리           A 대학(발급 기관)
    |                     |                         |                         |
    |-- 수료증 제시 ----->|                         |                         |
    |<-- 동의서 ----------|                         |                         |
    |--- 동의 ----------->|  AuditEvent: consent.recorded                    |
    |                     |-- INST-A 키 조회 ------>|                         |
    |                     |<-- 엔드포인트 + 키 -----|                         |
    |                     |-- 서명된 요청 (seq 912, idemKey K1) ------------->|
    |                     |                         |   서명·수신자·신선도·   |
    |                     |                         |   순번 검증             |
    |                     |                         |   AuditEvent: read      |
    |                     |<-- 서명된 증명 (inReplyTo, seq 4187) ------------|
    |                     |  서명 + payloadHash 검증                          |
    |                     |  AuditEvent: attestation.received                 |
    |                     |  책임자 결정: 인정 / 현장교육 요구                |
    |                     |  AuditEvent: training.accepted                    |
    |<-- 결과 통지 -------|                         |                         |

감사 이벤트는 모두 네 건이 두 기관에 나뉘어 기록된다. 검증 기관에는 동의와 수신이, 발급 기관에는 조회가, 다시 검증 기관에는 인정 결정이 남는다. 어느 기관도 전체 이야기를 혼자 갖고 있지 않지만, 두 기관의 감사 추적을 합치면 전체가 빠짐없이 재구성된다. 각 이벤트가 같은 봉투 식별자를 가리키기 때문이다. 나중에 발급 기관이 해당 교육 이수 기록을 취소하면(예: 역량 평가가 잘못 기록되었음이 드러난 경우), 원래 증명의 envelopeId를 참조하는 서명된 취소 메시지를 보낼 수 있다. 검증 기관은 이를 새 감사 이벤트로 남기고 자신의 결정을 다시 연다.

5.3 감사 추적

감사 추적은 누가, 언제, 어느 레코드에 무엇을 했는지에 대한 기관의 기억이다. 생물안전 행정에서 감사 추적은 모든 점검과 사고 검토에서 나오는 질문에 답한다. 이 시설의 인증은 언제 누가 입력했는가. 사고 이후에 교육 이수 기록이 수정되었는가. 이 시정 조치는 누가 종결했고, 종결은 검토를 거쳤는가. PHASE-3은 "포괄적 감사 로깅"을 요구하지만 그 형태는 정하지 않는다. 아래 AuditEvent는 이 책의 제안이다.

5.3.1 AuditEvent 레코드

필드내용
eventId고유 식별자(제2장의 ID 규칙에 따라 AUD- 접두어).
streamId이 이벤트가 속한 체인. 보통 기관 또는 테넌트마다 하나다.
seq스트림 안의 위치. 빈틈없이 엄격하게 증가한다.
timestamp작업이 확정된 ISO 8601 UTC 시각.
actor가명처리된 사용자·서비스 ID, 행위 당시 역할, 인증 맥락(예: 다중 인증 사용).
action통제 어휘: create, update, transition, read, export, grant, revoke, sign 등.
recordRef영향받은 레코드의 유형, ID, 버전.
beforeHash / afterHash작업 전후 정규화된 레코드의 다이제스트(생성·조회 시에는 해당하는 쪽이 null).
reason짧은 사유 코드와 선택적 참조(승인 ID, 티켓, 봉투 ID).
prevEventHash스트림에서 바로 앞 이벤트의 eventHash.
eventHasheventHash 자신을 뺀 이 이벤트 정규형의 다이제스트.

이벤트는 레코드 내용이 아니라 작업 전후 레코드의 해시를 저장한다. 그래서 감사 추적이 작게 유지되고, 무엇보다 결코 수정하지 않도록 설계된 저장소에 민감한 내용이 들어가지 않는다. 레코드 버전 자체는 이전 버전을 보관하는 레코드 저장소에 있다. 감사 이벤트는 각 시점에 어느 버전이 존재했는지를 증명하고, 권한 있는 검토자가 요청하면 레코드 저장소가 그 내용을 내준다. 적법한 사유로 어떤 버전을 지워야 할 때는 레코드 저장소에서 내용을 삭제한다. 감사 추적에는 그 해시를 가진 버전이 존재했다는 사실과, 누가 왜 그것을 삭제했는지가 계속 남는다.

5.3.2 예시

{
  "eventId": "AUD-2026-0004821",
  "streamId": "INST-EXAMPLE-UNIV-A/main",
  "seq": 4821,
  "timestamp": "2026-03-12T06:40:03.214Z",
  "actor": {
    "id": "USR-A-5d02e9",
    "role": "biosafety-officer",
    "authContext": "mfa"
  },
  "action": "transition",
  "recordRef": {
    "type": "IncidentReport",
    "id": "INC-2026-000118",
    "version": 5
  },
  "beforeHash": "sha256:1b7e…90c3",
  "afterHash": "sha256:c44a…0f7d",
  "reason": {
    "code": "status-change",
    "detail": "under-investigation -> resolved",
    "ref": "CAPA-2026-000061"
  },
  "prevEventHash": "sha256:77d0…a912",
  "eventHash": "sha256:e3f8…5b26"
}

이 이벤트는 제3장에서 정의한 상태 전이 하나를 기록한다. reason.ref는 그 전이를 정당화한 시정 조치 레코드를 가리킨다. 그래서 감사자는 사고 서술을 전혀 읽지 않고도 "사고가 해결됨으로 표시됨"에서 "어떤 CAPA가 그것을 가능하게 했는가", 다시 "그 CAPA를 누가 승인했는가"까지 한 줄기로 따라갈 수 있다.

5.3.3 해시 체인

각 이벤트는 바로 앞 이벤트의 해시를 담는다. 스트림 중간의 이벤트를 고치거나 지우거나 끼워 넣으면 그 이벤트의 해시가 바뀌고, 다음 이벤트에 저장된 prevEventHash와 더 이상 맞지 않게 된다. 이 불일치는 스트림 끝까지 이어진다. 수정을 숨기려면 이후의 모든 이벤트를 다시 써야 하고, 이후 이벤트의 해시 하나라도 데이터베이스 밖에 공유된 적이 있으면 그 시도는 곧바로 드러난다. 검증 로직은 짧다.

import { createHash } from "node:crypto";

// canonicalJson: 배치 환경이 제공하는 RFC 8785(JCS) 구현
declare function canonicalJson(value: unknown): string;

interface AuditEvent {
  eventId: string;
  streamId: string;
  seq: number;
  prevEventHash: string | null;
  eventHash: string;
  [field: string]: unknown;
}

function digest(e: AuditEvent): string {
  const { eventHash, ...body } = e;          // eventHash를 뺀 전부를 해시
  const h = createHash("sha256").update(canonicalJson(body), "utf8");
  return "sha256:" + h.digest("hex");
}

export function verifyStream(events: AuditEvent[]): { ok: boolean; brokenAt?: number } {
  let prev: string | null = null;
  for (let i = 0; i < events.length; i++) {
    const e = events[i];
    if (i > 0 && e.seq !== events[i - 1].seq + 1) return { ok: false, brokenAt: e.seq };
    if (e.prevEventHash !== prev) return { ok: false, brokenAt: e.seq };
    if (digest(e) !== e.eventHash) return { ok: false, brokenAt: e.seq };
    prev = e.eventHash;
  }
  return { ok: true };
}

체인이 증명하는 것은 내부 일관성이지 절대적 진실이 아니다. 스트림 전체를 쥔 데이터베이스 관리자는 내용을 바꾼 채 처음부터 다시 만들 수 있고, 그렇게 만든 체인도 검증을 통과한다. 해시 체인이 강한 통제가 되려면 최신 해시, 즉 "헤드"를 관리자가 통제하지 못하는 곳에 정기적으로 남겨야 한다. 흔히 쓰는 방법은 세 가지다. 첫째, 현재 헤드를 외부로 나가는 봉투의 메타데이터나 협력 기관에 보내는 정기 서명 진술에 넣는다. 둘째, 독립된 시점확인 기관에서 헤드에 대한 신뢰 타임스탬프를 받는다(타임스탬프 프로토콜, RFC 3161). 셋째, 헤드를 외부 원장에 고정(anchoring)한다. README의 WIA-BLOCKCHAIN 연계가 바로 이 방법을 가리킨다.

5.3.4 WIA-BLOCKCHAIN 고정: 이점과 대가

README는 WIA-BLOCKCHAIN 연계를 "규정 준수를 위한 변경 불가능한 감사 추적"으로 소개한다. 꼼꼼히 따져 보면 쓸모 있는 형태는 좁다. 이벤트 자체가 아니라 다이제스트 하나(스트림 헤드, 또는 이벤트 묶음에 대한 머클 트리 루트)를 주기적으로 원장에 기록하는 방식이다. 이렇게 고정하면 실제 이점과 실제 비용이 함께 생긴다.

측면다이제스트만 고정레코드·이벤트를 원장에 기록
변조 탐지강함: 고정 시점 이후의 재작성은 탐지된다강함. 다만 고정 방식보다 더 강하지는 않다
기밀성다이제스트로는 내용을 알 수 없다누가, 언제, 어느 시설이라는 메타데이터가 원장 참여자 전원에게 노출된다
삭제 의무원장 밖 내용을 지우면 다이제스트는 의미를 잃는다적법한 삭제·정정 권리와 충돌한다
운영 의존성낮음: 고정이 늦어지거나 재시도되어도 업무는 멈추지 않는다높음: 레코드 쓰기가 원장 가용성에 묶인다
비용과 복잡도고정 주기마다 트랜잭션 1건이벤트마다 비용이 들고 모든 작성자의 키를 관리해야 한다

결론적으로 원장 고정은 필수 요건이 아니라 선택적 강화 조치다. RFC 3161 타임스탬프나 협력 기관과의 상호 서명으로도 더 적은 구성 요소로 비슷한 수준의 변조 증거를 얻을 수 있다. 고정을 택한 기관은 고정 주기를 프로파일 매니페스트(제7장)에 적어야 한다. 다음 고정 전까지 몰래 다시 쓸 수 있는 이력의 범위가 그 주기로 정해지기 때문이다.

5.3.5 보존 기간

감사 이벤트, 봉투, 멱등성 기록, 레코드 버전을 얼마나 오래 보관해야 하는지는 이 표준이 아니라 관할 법령과 관련 당국·연구비 지원 기관의 규정이 정한다. PHASE-1과 PHASE-3의 구현 참고 사항은 7년 보존 기간을 언급한다. 이 책은 그 숫자를 소프트웨어의 하한으로 다룬다. 즉 구현은 최소한 그 기간 동안 데이터를 보관할 수 있어야 하고, 기간을 레코드 유형별로 설정할 수 있어야 한다. 7년이면 어느 관할에서든 법적으로 충분하다는 뜻은 아니다. PHASE 참고 사항은 그 기간을 인증기관 표준(ISO/IEC 17065)과 연결하지만, 이 책은 그 연결에 기대지 않는다. 기관은 법무·기록관리 부서에서 보존 일정표를 받아 설정을 그에 맞춰야 한다. 감사 추적은 그것이 설명하는 레코드만큼은 보관해야 하므로, 일정표에는 감사 추적도 포함해야 한다.

보존에는 끝도 있다. 보존 기간이 끝나 삭제하는 행위도 감사 대상이다. 무엇이 삭제되었는지(해시로), 어떤 일정표에 따랐는지, 어떤 절차로 삭제했는지를 이벤트로 남겨야, 나중에 레코드가 없는 이유를 설명할 수 있다.

5.4 레코드 수준의 보관 연속성

연구기관은 등록된 물질을 기관 안팎의 등록 시설 사이에서 옮긴다. 관리상의 질문은 단순하지만 엄격하다. 어느 순간이든 어느 등록 시설과 어느 책임자가 책임을 지고 있는가, 그리고 양쪽 서류가 서로 일치하는가. SDK에는 보관 연속성 타입이 없다. 이 책은 송신 측이 만드는 인계 레코드와 수신 측이 만드는 인수 레코드 두 가지를 제안하고, 둘을 공유 인계 ID로 연결한다.

5.4.1 인계 레코드와 인수 레코드

레코드필드의미
인계transferId두 레코드가 공유하는 식별자(TRF- 접두어).
materialRef기관 내부 등록부의 물질 ID. 서술 정보나 특성 데이터는 넣지 않는다(제2장 데이터 최소화 참조).
riskGroup등록부에 기록된 RiskGroup 분류 라벨(1~4). 제2장 materialRefs의 필드와 같으며, 그 밖의 분류 정보는 담지 않는다.
fromFacilityId / toFacilityId양 끝의 등록 시설 ID.
releasedBy보관 책임을 넘기는 책임자의 가명 ID.
approvalRefs[]IBC 승인 ID, 그리고 필요한 경우 관할 당국이 발급한 허가·승인 참조.
transportLabel선택 항목. 운송물에 붙은 규제 라벨을 문자열로 기록한다(5.4.2절).
dispatchedAt보관 책임을 넘긴 UTC 시각.
인수receivedBy보관 책임을 받은 책임자의 가명 ID.
receivedAt보관 책임을 받은 UTC 시각.
outcomeaccepted, accepted-with-discrepancy, refused 중 하나.
discrepancyRef단순 수락이 아닐 때, 제3장 절차로 연 사고·아차사고 레코드의 ID.

두 레코드는 작은 상태 기계를 이룬다. initiated → dispatched → received 또는 refused로 진행하며, 두 기관이 합의한 기간 안에 인수 확인이 오지 않으면 overdue가 자동으로 표시된다. 보관 책임은 결코 "누구의 것도 아닌" 상태가 되지 않는다. 인수가 기록될 때까지는 송신 시설이 계속 책임을 지므로, 기한이 지난 인계는 수신 측뿐 아니라 송신 측 생물안전관리책임자의 대시보드에도 나타난다. 불일치는 인계 레코드를 고쳐서 해결하지 않고 새 레코드로 다룬다. 수신 측이 행정 수준에서 확인한 사실을 기록하고, 그에 따른 사고나 아차사고는 제3장의 워크플로에 들어간다.

{
  "transfer": {
    "transferId": "TRF-2026-000044",
    "materialRef": "MAT-A-REG-01872",
    "riskGroup": 2,
    "fromFacilityId": "FAC-A-0103",
    "toFacilityId": "FAC-B-0210",
    "releasedBy": "PSN-A-19b0aa",
    "approvalRefs": ["IBC-A-2025-117", "IBC-B-2026-009"],
    "transportLabel": "UN 3373",
    "dispatchedAt": "2026-04-02T00:30:00Z",
    "status": "dispatched"
  },
  "receipt": {
    "transferId": "TRF-2026-000044",
    "receivedBy": "PSN-B-4c7e10",
    "receivedAt": "2026-04-02T05:12:00Z",
    "outcome": "accepted",
    "discrepancyRef": null
  }
}

예시의 시설·승인·등록부 ID는 모두 가상이다. riskGroup과 transportLabel 값은 필드 형식을 보여 주는 자리표시자일 뿐이며, 어떤 물질을 어떻게 분류하거나 운송해야 하는지에 대한 지침이 아니다. 그 판단은 자격을 갖춘 담당자가 아래에 언급하는 규정에 따라 내린다.

5.4.2 운송 규정은 라벨로만 다룬다

생물학적 물질의 운송물에는 IATA 위험물 규정(DGR)과 UN 번호 체계(예: UN 2814, UN 3373) 같은 운송 규칙이 적용된다. WIA-BIO-017 본 명세도 국제기구 절에서 이를 언급한다. 관리 레코드에는 어떤 라벨이 적용되었는지를 문자열로 저장해, 보관 연속성 기록을 운송 서류와 대조할 수 있게 한다. 포장, 표시, 내용물에 관한 세부 사항은 레코드에 넣지 않는다. 어떤 규칙이 적용되는지 판단하고 규정에 맞는 운송물을 준비하는 일은, 교육을 받았고 필요한 경우 자격을 갖춘 운송 담당자가 최신 공식 규정에 따라 수행한다. 이 책은 그 규정을 옮겨 싣지 않는다.

5.4.3 기관 간 보관 연속성

두 시설이 서로 다른 기관에 속하면 인계와 인수는 각각 서명된 봉투에 담겨 이동한다. 인수 봉투는 inReplyTo로 인계 봉투를 참조한다. 각 기관은 보낼 때와 받을 때 감사 이벤트를 남긴다. 공유 transferId와 봉투 ID가 있으므로 어느 한쪽이든, 또는 양쪽 모두에 읽기 권한을 가진 점검자든 두 감사 추적을 나란히 맞춰 볼 수 있다. 인수 메시지에 멱등성 키("TRF-2026-000044 인수")를 붙이면, 재시도된 인수 확인이 인수 두 건으로 바뀌는 일이 없다.

5.5 시간과 순서

감사 추적의 품질은 시계의 품질을 넘지 못한다. 감사 추적이 답하는 질문, 예컨대 "인계 당일에 시설 인증이 유효했는가", "교육 이수 기록은 사고 이전에 입력되었는가, 이후에 입력되었는가"는 모두 순서에 관한 질문이다. 그리고 여러 기계에 걸친 순서는 생각보다 다루기 어렵다.

5.5.1 시계 동기화

모든 타임스탬프는 ISO 8601 형식의 UTC로 기록한다. 현지 시각은 화면 표시의 문제일 뿐 저장의 문제가 아니다. PHASE 구현 참고 사항은 대부분의 기한에는 stratum-2 서버를 쓰는 NTPv4(RFC 5905)로 충분하고, 더 정밀한 동기화가 필요한 곳에는 정밀 시간 프로토콜(PTP, IEEE 1588)을 권한다. 생물안전 레코드의 기한은 제3장의 통보 목표처럼 시간이나 일 단위이므로 NTP로 넉넉하다. 더 중요한 것은 감시다. 시계가 어긋난 서버는 경보를 내야 하고, 오차가 허용치를 넘은 동안 기록된 이벤트에는 표시를 남겨, 검토자가 그 타임스탬프의 신뢰도가 낮다는 것을 알 수 있게 해야 한다.

5.5.2 벽시계보다 순번

한 스트림 안의 순서는 timestamp가 아니라 seq로 정한다. 같은 밀리초에 확정된 두 이벤트나, 시계가 조금씩 다른 두 애플리케이션 서버에서 기록된 이벤트라도, 데이터베이스가 하나의 트랜잭션 안에서 순번을 부여하므로 순서가 모호하지 않다. 타임스탬프는 무슨 일이 언제 일어났는지를 보여 주는 증거로서 여전히 중요하지만, 같은 스트림의 두 이벤트 중 무엇이 먼저인지를 가리는 데는 쓰지 않는다.

기관 사이에는 공유 순번이 없다. 각 기관은 자기 이벤트의 순서를 정하고, 인과 관계는 봉투가 이어 준다. 어떤 인계에 inReplyTo로 응답한 인수는 시계가 뭐라고 하든 그 인계보다 뒤에 일어난 것이다. 수신자가 받은 메시지의 createdAt이 자기 현재 시각보다 허용치 이상 늦다면, 타임스탬프를 "바로잡지" 말아야 한다. 프로파일에 따라 수락하거나 거부하고, 송신 측 createdAt과 자신의 receivedAt을 둘 다 기록한다. 그래야 검토자가 시계 오차를 조용히 지워진 상태가 아니라 있는 그대로 볼 수 있다.

5.5.3 시간 기준 유효성 검사

교환 과정의 판단 중 상당수는 타임스탬프를 유효기간과 비교한다. dispatchedAt 시점에 시설 인증이 유효했는가, 출입 권한을 부여한 날짜에 교육이 만료되지 않았는가 같은 것이다. 이런 검사는 항상 해당 이벤트에 기록된 시각과, 그 시각에 효력이 있던 레코드 버전을 기준으로 해야 한다. 그 버전은 감사 추적으로 재구성할 수 있다. "지금"을 기준으로 검사하면 오래된 이벤트를 검토할 때 틀린 결과가 나온다. 그사이 인증이 갱신되었을 수도, 반대로 만료되었을 수도 있기 때문이다.

5.6 연합 토폴로지

PHASE 부속서는 작업반이 시험 벡터 검증에 사용한 네 가지 참조 배치 형태를 설명한다. 비규범적 내용이지만, 교환 아키텍처를 기관 유형에 맞춰 고를 때 유용한 어휘가 된다. 각 배치는 프로파일 매니페스트(제7장)에 토폴로지를 선언하며, 토폴로지를 바꾸면 매니페스트에 새로 서명해야 한다.

토폴로지(PHASE Annex J)형태생물안전 레코드에서의 전형적 적용감사 추적상의 함의
단일 테넌트 엣지조직마다 런타임 하나, 공유 상태 없음. 매니페스트는 조직이 직접 서명자체 생물안전 관리 시스템을 운영하는 단일 대학·병원·기업기관마다 스트림 하나. 내부자 재작성에 대한 주된 방어는 외부 고정이나 협력 기관 상호 서명
멀티 테넌트 게이트웨이공유 런타임 하나가 헤더 기반 격리로 여러 테넌트를 서비스, 공유 신원 제공자 사용공급사가 호스팅하는 서비스, 또는 소규모 기관을 함께 호스팅하는 컨소시엄테넌트마다 스트림 하나. 운영자는 어떤 테넌트도 다른 테넌트의 스트림을 읽거나 쓸 수 없음을 입증해야 하고, 운영자 작업은 영향받는 각 테넌트 스트림에 기록
연합 메시독립 런타임들이 서로 피어링하고 디렉터리 서비스에 게시여러 기관에 걸친 연구 컨소시엄과 공동 과제피어마다 자기 스트림 유지. 봉투와 공유 ID로 연결되고, 디렉터리가 구성원·키 색인에 서명
망 분리 일괄 처리실시간 네트워크 연결 없음. 서명된 증거 패키지를 별도 경로로 운반정책상 실시간 연결이 금지된 환경, 일괄 제출만 받는 수신처배치마다 스트림 헤드와 서명 포함. 신선도 창을 넓히고 순번을 주된 무결성 검사로 사용

실제 기관은 여러 형태를 섞어 쓰는 경우가 많다. 한 대학이 내부적으로는 단일 테넌트 엣지 시스템을 운영하면서, 다기관 컨소시엄에는 연합 메시로 참여하고, 연간 통계는 망 분리 일괄 처리로 당국에 제출할 수 있다. 이 장의 봉투와 감사 이벤트 구조는 일부러 모든 토폴로지에서 똑같게 설계했다. 바뀌는 것은 전송 방식, 키 배포, 신선도 허용치뿐이다. 덕분에 레코드를 다시 쓰거나 감사 체인을 끊지 않고도 형태를 옮겨 갈 수 있다.

5.6.1 토폴로지 선택

대부분의 결정은 세 가지 질문으로 정리된다. 첫째, 런타임은 누가 운영하며, 기관 정책상 그 운영자가 기관 레코드를 보유해도 되는가. 안 된다면 멀티 테넌트 게이트웨이는 제외된다. 둘째, 정기적으로 레코드를 주고받는 협력 기관이 몇 곳인가. 한두 곳이라면 단일 테넌트 시스템 사이의 직접 교환이 메시보다 단순하고, 많다면 디렉터리 서비스를 둘 만한 가치가 있다. 셋째, 실시간 연결을 금지하는 수신처나 환경이 있는가. 있다면 다른 선택과 관계없이 일괄 내보내기를 지원해야 한다.

어떤 형태를 고르든 가장 자주 과소평가되는 부분은 키 관리다. 각 기관은 서명 키를 교체하는 절차, 새 키를 쓰기 전에 디렉터리에 게시하는 절차, 그리고 서명된 메시지와 증거 패키지를 검증할 수 있어야 하는 기간 동안 폐기된 공개 키를 계속 제공하는 절차를 문서로 갖춰야 한다. 그 기간은 적어도 5.3.5절의 보존 기간만큼이다.

장 요약

제6장은 이 레코드와 교환을 보호하는 통제를 다룬다. 위협 모델, 역할 기반 접근 제어, 개인정보 규칙, 그리고 기관이 이미 운영하는 시스템과의 통합이 그 내용이다.

기술적 안전 내용은 공식 생물안전 당국의 자료를 참고할 것. 이 장은 레코드 구조와 데이터 교환만 다룬다. 물질의 분류, 운송·포장 요건, 그 밖의 기술적 생물안전 조치는 관할 국가 당국(한국에서는 질병관리청, 과학기술정보통신부, 그리고 LMO 관련 사항은 국가 바이오안전성정보센터), WHO 실험실 생물안전 매뉴얼(LBM), 공식 운송 규정에서 확인해야 하며, 자격을 갖춘 담당자가 적용해야 한다.