# Baron SSO 전체 시스템 백업 및 복구 설계

## 목적

Baron SSO의 전체 시스템 백업/복구는 CSV export/import의 확장판이 아니라, 서비스 저장소 전체를 일관된 시점으로 보존하고 복원하는 재해 복구 기능이다.

핵심 목표는 다음과 같다.

- 사용자, 조직, 권한, RP, WORKS 연동 참조에 쓰이는 UUID를 그대로 보존한다.
- Kratos identity subject와 Baron local user ID가 어긋나지 않게 복구한다.
- Hydra/Keto/Oathkeeper 기반 인증/인가 상태를 서비스 가능한 수준으로 복원한다.
- 복구 후 WORKS externalKey 기반 비교/동기화가 기존 연동과 이어지도록 한다.
- 백업 산출물의 무결성, 보안, 복구 가능성을 검증 가능한 절차로 만든다.

## 배경과 결론

사용자 CSV는 `user_id`를 포함해 내보낼 수 있지만, 실제 사용자 계정의 주체 ID는 Kratos identity ID다. Kratos Admin API의 identity 생성 요청은 `id` 필드를 받지 않으므로, CSV import만으로 기존 사용자 UUID를 보장할 수 없다.

따라서 사용자 UUID 보존이 필요한 복구는 반드시 Kratos DB까지 포함한 full backup/restore로 처리해야 한다. CSV import는 운영 편의 기능으로 유지하되, 재해 복구나 WORKS 연동 보존 목적의 원장 복구 수단으로 간주하지 않는다.

## 백업 대상

### 필수 저장소

| 대상 | 저장소 | 포함 이유 | 복구 필수 여부 |
| --- | --- | --- | --- |
| Baron 애플리케이션 DB | `baron_postgres` | users, tenants, user_login_ids, user_groups, api_keys, outbox, WORKS mapping, RP metadata 등 | 필수 |
| Kratos DB | `ory_postgres`의 `ory_kratos` | identity UUID, credentials, verifiable/recovery addresses, sessions | 필수 |
| Hydra DB | `ory_postgres`의 `ory_hydra` | OAuth2 clients, consent, token/session 관련 상태 | 필수 |
| Keto DB | `ory_postgres`의 `ory_keto` | ReBAC relation tuple 원장 | 필수 |
| Baron ClickHouse | `baron_clickhouse` | 감사 로그, 운영 추적 데이터 | 운영 정책상 필수 |
| Ory ClickHouse | `ory_clickhouse` | Ory/Oathkeeper/Vector 계열 로그 | 운영 정책상 필수 |
| 설정/비밀값 | `.env`, generated Ory config, WORKS private key, gateway/Oathkeeper config | 동일 환경 재기동과 외부 연동에 필요 | 필수 |

### 선택 저장소

| 대상 | 처리 원칙 |
| --- | --- |
| Redis | 로그인 pending state, cache, short code 등 휘발성 데이터다. full restore에서는 원칙적으로 제외하고 재시작 시 비운다. 무중단 이전 시나리오에서만 snapshot을 검토한다. |
| 프론트 빌드 산출물 | 소스/이미지 태그로 재생성한다. 별도 보관은 배포 재현성을 위한 선택 항목이다. |
| 로컬 개발 산출물 | reports, coverage, test-results 등은 백업 대상에서 제외한다. |

## 백업 산출물 형식

백업 단위는 압축된 디렉터리 또는 object storage prefix로 관리한다.

```text
baron-sso-backup-YYYYMMDD-HHMMSSZ/
  manifest.json
  checksums.sha256
  postgres/
    baron.dump
    ory_kratos.dump
    ory_hydra.dump
    ory_keto.dump
    globals.sql
  clickhouse/
    baron_clickhouse/
    ory_clickhouse/
  config/
    env.redacted
    env.encrypted
    ory/
    gateway/
  reports/
    row-counts.json
    restore-readiness.json
```

`manifest.json`에는 최소한 다음 정보를 기록한다.

- backup format version
- 생성 시각, git commit, 이미지 태그
- 서비스별 DB schema/migration version
- 각 dump 파일의 checksum, 크기, 생성 명령 버전
- 백업 모드: `offline`, `maintenance`, `online-best-effort`
- 암호화 방식과 key id
- 복구 대상 환경 제한: `same-env-only`, `staging-rehearsal`, `cross-env`

## Dump 대상별 복구 flow 및 영향도

`make dump`의 서비스 필터는 저장소별 복구 단위를 명확히 나누기 위한 운영 인터페이스다. 전체 재해 복구는 `DUMP_SERVICES=all`을 기본으로 하되, staging rehearsal이나 부분 장애 분석에서는 개별 대상을 분리해서 검증할 수 있다.

### 대상별 요약

| 서비스 필터 | 주요 dump 산출물 | 포함 데이터 | 복구 중요도 | 복구 영향도 |
| --- | --- | --- | --- | --- |
| `postgres` | `postgres/baron.dump` | Baron users, tenants, membership, user_login_ids, user_groups, RP metadata, API keys, WORKS mapping/outbox, Keto outbox, consent read model 등 | 필수 | Ory에 저장되지 않거나 조회가 불가능한 Baron control plane 데이터와 처리 상태를 담는다. 누락되면 사용자/테넌트/RP/WORKS 참조가 끊기고 Ory DB만 복구해도 서비스 의미가 깨진다. |
| `ory-postgres` | `postgres/globals.sql`, `postgres/ory_kratos.dump`, `postgres/ory_hydra.dump`, `postgres/ory_keto.dump` | Kratos identity/credential/session, Hydra client/consent/token state, Keto relation tuple | 필수 | 인증 주체, OAuth2/OIDC 상태, ReBAC 권한 원장이다. Baron DB와 시점이 다르면 로그인/인가/consent 불일치가 발생한다. |
| `clickhouse` | `clickhouse/baron_clickhouse/schema/*.sql`, `clickhouse/baron_clickhouse/data/*.native` | Baron audit_logs, RP usage event/aggregate 등 | 운영 정책상 필수 | 인증 자체를 막지는 않지만 감사 추적, 사용량 집계, 사고 분석 이력이 손실된다. |
| `ory-clickhouse` | `clickhouse/ory_clickhouse/schema/*.sql`, `clickhouse/ory_clickhouse/data/*.native` | Oathkeeper/Ory/Vector 접근 로그 | 운영 정책상 필수 | Ory edge 접근 로그와 장애 분석 근거가 손실된다. 인증 원장은 Postgres에 있으므로 직접 로그인 기능 영향은 제한적이다. |
| `config` | `config/env.redacted`, `config/generated-ory.*`, `config/gateway.*`, `config/compose/*` | 환경 변수 redacted snapshot, generated Ory config, gateway/Oathkeeper 설정, compose 파일 | 필수 | DB만 복구해도 secret/config가 맞지 않으면 Ory Stack, WORKS 연동, callback URL, gateway routing이 정상 기동하지 못한다. |

### `postgres`: Baron 애플리케이션 DB

Dump flow:

1. `baron_postgres` 컨테이너 실행 상태를 확인한다.
2. `${DB_NAME:-baron_sso}`를 `pg_dump -Fc`로 `postgres/baron.dump`에 저장한다.
3. `pg_stat_user_tables` 기준 row count를 `reports/baron-postgres-row-counts.txt`에 기록한다.
4. `checksums.sha256`에 dump와 report checksum을 기록한다.

Restore flow:

1. restore 전용 빈 Baron Postgres DB를 준비한다.
2. `make restore ... RESTORE_SERVICES=postgres CONFIRM_RESTORE=baron-sso`로 `pg_restore --clean --if-exists`를 실행한다.
3. 복구 후 backend migration 자동 실행은 금지하고, dump 시점의 schema version과 현재 binary가 호환되는지 먼저 확인한다.
4. Kratos identity와 Baron `users.id`의 참조 검증을 수행한다.
5. WORKS relay, Keto outbox relay는 post-restore 검증 전까지 재개하지 않는다.

영향도:

- 사용자, 테넌트, 소속, RP metadata, WORKS mapping의 기준 원장이므로 full restore에서 가장 먼저 Baron/Ory 시점 정합성을 확인해야 한다.
- Baron DB만 과거 시점으로 되돌리면 Kratos identity, Hydra consent, Keto tuple과 불일치할 수 있다.
- WORKS mapping/outbox 상태가 과거로 돌아가면 외부 WORKS와 중복 upsert/delete 후보가 생길 수 있으므로 comparison dry-run이 필수다.

검증 포인트:

- `users.id`와 Kratos `identities.id` 일치
- `tenants.parent_id`, membership, user group 참조 무결성
- RP metadata와 Hydra client 연결성
- WORKS mapping의 BaronResourceID 참조 유효성

### `ory-postgres`: Kratos/Hydra/Keto DB

Dump flow:

1. `ory_postgres` 컨테이너 실행 상태를 확인한다.
2. `pg_dumpall --globals-only`로 role/권한 정보를 `postgres/globals.sql`에 저장한다.
3. `${KRATOS_DB:-ory_kratos}`, `${HYDRA_DB:-ory_hydra}`, `${KETO_DB:-ory_keto}`를 각각 `pg_dump -Fc`로 저장한다.
4. 각 DB의 row count report를 `reports/ory_*-row-counts.txt`에 기록한다.

Restore flow:

1. restore 전용 빈 Ory Postgres DB를 준비한다.
2. 필요 시 `globals.sql`을 먼저 적용한다.
3. Kratos, Hydra, Keto DB를 각각 복구한다.
4. migration은 자동 실행하지 않고, Ory binary version과 dump schema version을 확인한다.
5. Ory Stack을 backend보다 먼저 기동해 admin/public endpoint health를 확인한다.

영향도:

- Kratos DB는 사용자 subject와 credential 원장이므로 누락되면 비밀번호, recovery/verifiable address, identity UUID 보존이 불가능하다.
- Hydra DB는 client, consent, OAuth2 token/session 상태를 담으므로 누락되면 기존 RP 로그인/consent 상태가 재생성되거나 만료될 수 있다.
- Keto DB는 ReBAC tuple 원장이므로 누락되면 사용자/테넌트/RP 권한 판단이 실패한다.
- Ory DB만 복구하고 Baron DB가 맞지 않으면 identity는 있으나 Baron user가 없거나, Baron user는 있으나 identity가 없는 상태가 된다.

검증 포인트:

- Kratos identity 수와 Baron users 수 비교
- Hydra client와 Baron RP metadata 비교
- Keto tuple subject/object가 복구된 사용자/테넌트/RP를 참조하는지 확인
- 대표 사용자 password login, 대표 RP OIDC login/consent smoke

### `clickhouse`: Baron ClickHouse

Dump flow:

1. `baron_clickhouse` 컨테이너 실행 상태를 확인한다.
2. `system.tables`에서 Baron ClickHouse table 목록과 engine을 조회한다.
3. 일반 table은 `SHOW CREATE TABLE`과 `FORMAT Native` 데이터를 함께 저장한다.
4. view 계열 engine은 restore insert 위험을 피하기 위해 schema만 저장한다.
5. table별 row count를 `reports/baron_clickhouse-row-counts.txt`에 기록한다.

Restore flow:

1. restore 전용 ClickHouse DB를 준비한다.
2. database를 생성한 뒤 table schema를 먼저 적용한다.
3. 일반 table의 `.native` 데이터를 insert한다.
4. materialized view나 view는 schema만 복구하고, 대상 table 데이터가 들어간 뒤 재계산/재생성 정책을 별도로 확인한다.
5. row count와 주요 기간의 min/max timestamp를 비교한다.

영향도:

- Baron audit log와 RP usage 집계가 손실되면 보안 감사, 운영 추적, 사용량 화면의 신뢰도가 떨어진다.
- 인증 기능 자체의 필수 원장은 아니지만, 사고 대응과 운영 증적 측면에서는 필수 백업 대상이다.
- aggregate/materialized view는 원본 event table과 복구 순서가 맞지 않으면 중복 집계나 누락 집계가 생길 수 있다.

검증 포인트:

- `audit_logs`, `rp_usage_events`, aggregate table row count 비교
- 주요 기간별 min/max timestamp 비교
- AdminFront/DevFront의 사용량 조회 smoke

### `ory-clickhouse`: Ory ClickHouse

Dump flow:

1. `ory_clickhouse` 컨테이너 실행 상태를 확인한다.
2. Ory/Oathkeeper/Vector 계열 table schema와 일반 table data를 저장한다.
3. table별 row count를 `reports/ory_clickhouse-row-counts.txt`에 기록한다.

Restore flow:

1. restore 전용 Ory ClickHouse DB를 준비한다.
2. schema를 적용한 뒤 일반 table data를 insert한다.
3. Vector/Oathkeeper 기동 후 신규 로그가 같은 table로 유입되는지 확인한다.

영향도:

- Ory edge 접근 로그와 gateway 관측 자료가 손실된다.
- 인증/인가 원장은 Postgres에 있으므로 서비스 기동 자체 영향은 제한적이다.
- 보안 사고 분석, OIDC redirect 장애 분석, rate/anomaly 분석에는 영향이 크다.

검증 포인트:

- Oathkeeper access log row count 비교
- Oathkeeper 경유 요청 후 신규 로그 유입 확인
- Vector pipeline 재기동 후 error log 확인

### `config`: 설정 snapshot

Dump flow:

1. `.env`가 있으면 secret 성격 key를 `REDACTED`로 치환해 `config/env.redacted`를 만든다.
2. `config/.generated/ory`, `gateway`, 주요 compose 파일을 tar snapshot 또는 file copy로 보존한다.
3. 실제 secret 원문은 1차 로컬 구현의 `env.redacted`에 포함하지 않는다. 운영 백업에서는 별도 암호화 산출물로 보관해야 한다.

Restore flow:

1. `make restore ... RESTORE_SERVICES=config` 실행 시 운영 파일을 직접 덮어쓰지 않는다.
2. snapshot은 `config-restored/`에 풀어 운영자가 diff로 검토한다.
3. secret 원문은 승인된 secret manager 또는 암호화 백업에서 별도 복원한다.
4. `make validate-auth-config`, `make verify-auth-config`로 callback/redirect/gateway mapping을 검증한다.
5. Ory Stack과 gateway를 기동한 뒤 대표 callback과 OIDC flow를 확인한다.

영향도:

- DB가 정상이어도 Hydra system secret, Kratos config, Oathkeeper rule, WORKS private key, callback URL이 맞지 않으면 서비스가 정상 동작하지 않는다.
- `env.redacted`만으로는 운영 복구가 완성되지 않는다. 운영 복구용 secret은 별도 암호화 저장소가 필요하다.
- config를 운영 파일에 바로 덮어쓰면 현재 환경의 도메인, callback, gateway rule을 깨뜨릴 수 있으므로 수동 검토가 기본이다.

검증 포인트:

- `make validate-auth-config`
- `make verify-auth-config`
- gateway/Oathkeeper route smoke
- WORKS credential dry-run 또는 외부 호출 차단 상태 검증

### 제외 대상의 복구 영향

| 제외 대상 | 제외 이유 | 복구 후 영향 | 운영 대응 |
| --- | --- | --- | --- |
| Redis | cache, pending login, short code 등 휘발성 상태 | 진행 중인 login/link/short code flow가 만료되거나 재시작된다. | 사용자에게 재시도 안내, 서비스 기동 후 cache 재수렴 확인 |
| 프론트 빌드 산출물 | 소스와 이미지 태그로 재생성 가능 | 기존 정적 파일을 그대로 보존하지 않는다. | 동일 commit/image tag로 재배포 |
| 로컬 reports/coverage/test-results | 운영 원장이 아님 | 운영 서비스 영향 없음 | CI artifact나 별도 관측 저장소 기준으로 확인 |

### 전체 복구 순서와 의존성

Full restore는 다음 순서를 기본으로 한다.

1. restore 전용 빈 환경과 volume을 준비한다.
2. `config` snapshot을 `config-restored/`에 풀고 운영자가 secret/config 차이를 검토한다.
3. `ory-postgres`를 복구한다.
4. `postgres`를 복구한다.
5. `ory-clickhouse`와 `clickhouse`를 복구한다.
6. `make dump-verify`와 `make restore-verify`로 산출물 무결성을 확인한다.
7. `make validate-auth-config`, `make verify-auth-config`로 Ory/gateway 설정을 확인한다.
8. Ory Stack을 먼저 기동하고 Kratos/Hydra/Keto health를 확인한다.
9. backend와 app을 기동한다.
10. super admin login, 일반 사용자 login, 대표 RP OIDC login/consent를 확인한다.
11. WORKS comparison dry-run을 수행한다.
12. 대량 delete/upsert 위험이 없을 때 relay worker와 외부 동기화를 재개한다.

부분 복구는 원칙적으로 장애 분석 또는 rehearsal에서만 사용한다. 운영에서 특정 저장소만 과거 시점으로 되돌리면 cross-store 정합성이 깨질 수 있으므로, 실제 운영 restore는 같은 backup directory에서 나온 동일 시점의 산출물을 함께 적용하는 것을 기본으로 한다.

Restore 입력과 report:

- `make dump`, `make restore`, `make upload-cloud`는 기본적으로 Debian Trixie slim 기반 `baron-sso-backup-tools:local` 컨테이너에서 실행한다.
- 호스트 요구사항은 Docker와 Docker socket 접근 권한으로 제한한다. `zstd`, `jq`, `curl`, `openssl`, `postgresql-client`, `docker-cli`는 backup-tools image에 포함한다.
- `make restore BACKUP=<backup-dir>`는 압축 해제된 백업 디렉터리를 입력으로 사용한다.
- `make restore DUMP_FILE=<backup>.tar.zst`는 archive를 임시 디렉터리에 풀어 복구한다.
- `BACKUP`과 `DUMP_FILE`은 동시에 지정하지 않는다.
- restore report 기본 위치는 `BACKUP` 입력일 때 `<backup-dir>/reports/restore-report.json`, `DUMP_FILE` 입력일 때 `reports/restore/<archive-name>-restore-report.json`이다.
- restore report JSON이 생성되면 같은 경로에 `.md` 확장자의 Markdown 요약도 함께 생성한다.
- `RESTORE_REPORT=<path>`로 report 경로를 명시할 수 있다.
- restore report는 checksum 검증, 서비스별 복구 대상, 복구 후 row count 비교, config snapshot copy 위치를 기록한다.
- 외부 접속을 막은 상태에서 복구하려면 app/Ory serve/gateway를 먼저 중지하고 Postgres/ClickHouse/Redis만 기동해 restore를 수행한 뒤 검증 통과 후 Ory/backend/frontend를 순차 기동한다.

### 외부 분산 저장: WORKS Drive 업로드

로컬 dump가 끝난 뒤 같은 백업 디렉터리를 외부 저장소에 분산 보관하기 위해 `scripts/backup/upload_cloud.sh`를 사용한다. 현재 1차 대상은 WORKS Drive다.

Upload flow:

1. `make dump BACKUP=...` 또는 `make dump-upload-cloud BACKUP=...`로 백업 산출물을 만든다.
2. `dump.sh`가 `reports/backup-report.md`를 생성한다. report에는 사용자 수, 테넌트 수, RP 수, Hydra client 수, WORKS 관련 row count, 서비스별 수행 시간이 Markdown 표로 기록된다.
3. `upload_cloud.sh`가 백업 디렉터리를 `baron-sso-backup-*.tar.zst`로 압축한다.
4. Drive API용 access token을 확인한다.
   - 우선순위: `WORKS_DRIVE_ACCESS_TOKEN`, `WORKS_DRIVE_ACCESS_TOKEN_FILE`, `WORKS_DRIVE_ACCESS_TOKEN_CMD`
   - fallback 1: `WORKS_DRIVE_OAUTH_REFRESH_TOKEN`으로 Drive 앱 access token 갱신
   - fallback 2: `WORKS_DRIVE_OAUTH_*` 서비스 계정 JWT 토큰 발급
5. WORKS Drive upload URL 생성 API를 호출한다.
6. 발급받은 upload URL에 multipart `Filedata`로 `.tar.zst` archive를 업로드한다.
7. `WORKS_DRIVE_UPLOAD_REPORTS=true`이면 대상 폴더 아래 `WORKS_DRIVE_REPORT_FOLDER_NAME` 하위 폴더를 찾거나 생성한 뒤 `reports/*.md`만 업로드한다.
   - 업로드 파일명은 `backup-report-YYYYMMDD-HHMMSSZ.md`처럼 업로드 시각을 붙인다.
   - `reports/cloud-upload.json`은 로컬 실행 기록이며 Drive 업로드 대상에서 제외한다.
8. 업로드 결과를 `reports/cloud-upload.json`에 기록한다.

대상 drive 선택:

| `WORKS_DRIVE_TARGET` | 필요 변수 | 업로드 URL 생성 API |
| --- | --- | --- |
| `sharedrive` | `WORKS_DRIVE_SHARED_DRIVE_ID`, 선택 `WORKS_DRIVE_PARENT_FILE_ID` | `/v1.0/sharedrives/{sharedriveId}/files[/<folderFileId>]` |
| `mydrive` | 선택 `WORKS_DRIVE_USER_ID`, 선택 `WORKS_DRIVE_PARENT_FILE_ID` | `/v1.0/users/{userId}/drive/files[/<folderFileId>]` |
| `group` | `WORKS_DRIVE_GROUP_ID`, 선택 `WORKS_DRIVE_PARENT_FILE_ID` | `/v1.0/groups/{groupId}/folder/files[/<folderFileId>]` |
| `sharedfolder` | `WORKS_DRIVE_USER_ID`, `WORKS_DRIVE_SHARED_FOLDER_ID`, 선택 `WORKS_DRIVE_PARENT_FILE_ID` | `/v1.0/users/{userId}/drive/sharedfolders/{sharedFolderId}/files[/<folderFileId>]` |

운영 주의:

- 업로드 archive는 `.tar.zst`로 고정한다. `zstd`가 없으면 실패해야 한다.
- Drive API는 `file` scope가 필요하다.
- `WORKS_DRIVE_PARENT_FILE_ID`는 폴더 이름이나 경로가 아니라 WORKS Drive API가 반환하는 폴더 `fileId`여야 한다.
- 계정 동기화용 `WORKS_ADMIN_OAUTH_*`와 백업 업로드용 `WORKS_DRIVE_OAUTH_*`는 용도가 다른 앱/키로 분리한다.
- 운영 기본 경로는 Drive용 access token을 명시 주입하거나 `WORKS_DRIVE_OAUTH_REFRESH_TOKEN`으로 갱신하는 방식이다.
- 서비스 계정 JWT fallback은 Drive 업로드 앱 정책에서 Drive scope 위임이 허용된 경우에만 성공한다.
- 파일 크기가 WORKS Drive 단일 파일 제한에 걸릴 수 있으면 `WORKS_DRIVE_MAX_SINGLE_FILE_BYTES` 또는 `WORKS_DRIVE_FORCE_SPLIT=true`로 split part 업로드를 사용한다.
- Markdown report 업로드 기본 폴더명은 `reports`이며 `WORKS_DRIVE_REPORT_FOLDER_NAME`으로 바꿀 수 있다.
- 외부 업로드 성공은 복구 가능성을 보장하지 않는다. 업로드 후에도 `make dump-verify BACKUP=...`와 restore rehearsal을 별도로 수행해야 한다.

## 백업 모드

### 1. Offline backup

가장 안전한 모드다. 모든 writer를 중지한 뒤 dump한다.

순서:

1. gateway 또는 Oathkeeper에서 maintenance mode 활성화
2. backend, relay worker, vector 등 write producer 중지
3. Kratos/Hydra/Keto public/admin 요청 차단 또는 컨테이너 중지
4. Baron Postgres dump
5. Ory Postgres의 Kratos/Hydra/Keto DB dump
6. ClickHouse backup
7. 설정/비밀값 백업
8. checksum과 row count 생성
9. 서비스 재개

이 모드는 사용자 UUID, WORKS mapping, Keto relation, OAuth consent 상태의 일관성이 가장 좋다.

### 2. Maintenance backup

짧은 점검 모드에서 writer만 막고 read는 제한적으로 허용한다. 운영 기본 모드로 권장한다.

필수 조건:

- 사용자 생성/삭제/수정 차단
- 테넌트/조직 변경 차단
- WORKS outbox relay 중지
- Keto outbox relay 중지
- OAuth client 변경 차단

### 3. Online best-effort backup

무중단 스냅샷이다. 저장소가 여러 개라 cross-store 일관성을 보장할 수 없다. 감사 로그나 분석용 백업에는 가능하지만, 재해 복구용 원본으로는 사용하지 않는다.

## Postgres 백업 전략

Postgres는 논리 dump를 기본으로 한다.

- `pg_dump -Fc` 형식 사용
- DB별 dump 파일 분리
- `pg_dumpall --globals-only`로 role/extension/권한 정보 별도 보관
- 백업 전후 row count와 핵심 UUID sample 기록

대상 DB:

- Baron DB: `DB_NAME`
- Kratos DB: `ory_kratos`
- Hydra DB: `ory_hydra`
- Keto DB: `ory_keto`

복구는 빈 DB에 `pg_restore --clean --if-exists`로 수행한다. 기존 운영 DB에 덮어쓰는 in-place restore는 금지한다.

## ClickHouse 백업 전략

ClickHouse는 감사 로그 성격이 강하므로 정책을 분리한다.

- 재해 복구: ClickHouse native backup 또는 volume snapshot 사용
- 장기 보관: 파티션 단위 export 또는 object storage backup
- 복구 검증: 날짜 파티션별 row count와 min/max timestamp 비교

ClickHouse 백업 실패가 인증 기능 복구를 막지는 않지만, 감사 로그 보존 정책상 별도 실패로 취급한다.

## Redis 처리 원칙

Redis는 기본적으로 백업하지 않는다.

복구 후 영향:

- 로그인 pending flow 만료
- short code/link login flow 재시작 필요
- headless JWKS cache 재생성
- 세션 cache miss 발생 가능

Kratos/Hydra 자체 session/token 원장은 Postgres 쪽에 있으므로 Redis가 비어 있어도 서비스는 재수렴해야 한다.

## 설정과 비밀값

DB dump만으로는 복구가 불완전하다. 다음 항목은 암호화해서 함께 보관한다.

- `.env` 또는 배포 환경 변수
- Ory generated config
- Hydra system secret, cookie secret
- Kratos courier/config secret
- Keto config
- Oathkeeper rules/config
- WORKS Admin OAuth client private key
- API gateway 설정
- object storage backup key id

`env.redacted`는 검토용이고, 실제 복구에는 `env.encrypted`만 사용한다.

## 복구 절차

### Full restore 기본 절차

1. 대상 환경을 새로 준비한다.
2. 모든 애플리케이션 서비스를 중지한다.
3. 기존 DB가 있으면 별도 보관 후 빈 DB를 만든다.
4. Postgres globals를 복구한다.
5. Baron DB를 복구한다.
6. Kratos/Hydra/Keto DB를 복구한다.
7. ClickHouse를 복구한다.
8. 설정/비밀값을 복호화해 배치한다.
9. migration은 자동 실행하지 않고 현재 dump의 schema version을 확인한다.
10. Ory Stack을 기동한다.
11. backend를 기동한다.
12. relay worker는 아직 켜지 않는다.
13. post-restore verification을 수행한다.
14. WORKS comparison dry-run을 수행한다.
15. 문제가 없을 때 relay worker와 외부 동기화를 재개한다.

### Post-restore verification

필수 검증:

- Kratos identity 수와 Baron users 수 비교
- Baron `users.id`가 Kratos `identities.id`에 존재하는지 확인
- tenant parent tree 참조 무결성 확인
- `user_login_ids.user_id`, `user_login_ids.tenant_id` 참조 무결성 확인
- Keto relation subject/object가 복구된 사용자/테넌트를 참조하는지 확인
- Hydra client와 Baron RP metadata 참조 확인
- WORKS mapping의 BaronResourceID가 복구된 사용자/테넌트를 참조하는지 확인
- super admin 로그인 확인
- 일반 사용자 로그인 확인
- 대표 RP OIDC login/consent 확인
- WORKS comparison dry-run에서 externalKey 기준 대량 삭제/생성 후보가 없는지 확인

## WORKS 연동 복구 정책

WORKS 자체 데이터는 Baron 백업으로 복구하지 않는다. Baron이 보존해야 하는 것은 WORKS와 연결되는 참조 키다.

필수 보존:

- 사용자 UUID
- 테넌트 UUID
- WORKS resource mapping
- WORKS outbox 처리 상태
- WORKS domain mapping/config

복구 직후 정책:

- relay worker 자동 실행 금지
- comparison dry-run 먼저 실행
- externalKey 기준으로 Baron/WORKS가 매칭되는지 확인
- 대량 delete/upsert가 감지되면 동기화 중단
- 확인 후 필요한 사용자/조직만 재동기화

outbox는 복구 모드에 따라 처리한다.

| 복구 모드 | outbox 정책 |
| --- | --- |
| 같은 운영 환경 재해 복구 | outbox 상태 보존, 단 relay 재개 전 dry-run 필수 |
| staging rehearsal | outbox relay 비활성화, 외부 WORKS 호출 금지 |
| cross-env migration | outbox는 보존하되 실행하지 않고 별도 remap 정책 필요 |

## CSV export/import의 위치

CSV는 다음 목적으로만 사용한다.

- 운영자가 사용자/조직을 일괄 등록하거나 보정
- 일부 필드를 검토하기 위한 추출
- dry-run 입력 보조 자료

CSV는 다음 목적에 사용하지 않는다.

- Kratos identity UUID 보존 복구
- 비밀번호/credential 복구
- OAuth consent/token/session 복구
- Keto relation 원장 복구
- WORKS mapping 원장 복구

## 구현 계획

### Phase 1: 백업/복구 스크립트와 문서화

Estimate Time: 3~5d

- `scripts/backup/full-backup.sh`
- `scripts/backup/full-restore.sh`
- `scripts/backup/verify-backup.sh`
- `scripts/backup/verify-restore.sh`
- `manifest.json` 생성기
- checksum 생성
- row count report 생성

### Phase 2: staging restore rehearsal

Estimate Time: 3~5d

- 백업 파일로 격리된 staging stack 복구
- Ory/Baron/Postgres/ClickHouse 복구 검증
- 로그인/OIDC/관리자 화면 smoke test
- WORKS 외부 호출 차단 상태에서 comparison dry-run

### Phase 3: 운영 자동화

Estimate Time: 5~8d

- 정기 백업 스케줄링
- 암호화 및 object storage 업로드
- retention 정책
- 실패 알림
- restore rehearsal 주기화

### Phase 4: 관리 UI

Estimate Time: 5~8d

- backup 목록 조회
- backup 생성 요청
- restore readiness report 조회
- staging rehearsal 결과 조회
- 운영 restore는 UI에서 직접 실행하지 않고 승인 절차와 runbook으로 제한

## 테스트 전략

### 단위 테스트

- manifest 생성 검증
- checksum 검증
- row count report 비교
- restore readiness parser 검증

### 통합 테스트

- fixture DB 생성
- full backup 실행
- 빈 DB로 restore
- 핵심 테이블 row count 비교
- 사용자/테넌트 UUID 동일성 비교
- Kratos identity와 Baron user ID 일치 검증

### E2E 테스트

- super admin 로그인
- 일반 사용자 로그인
- AdminFront 사용자 목록 조회
- UserFront 로그인
- 대표 RP OIDC 로그인
- WORKS comparison dry-run

### 실패 테스트

- 누락된 dump 파일
- checksum 불일치
- schema version 불일치
- 일부 DB만 복구된 상태
- Kratos identity는 있는데 Baron user가 없는 상태
- Baron user는 있는데 Kratos identity가 없는 상태
- WORKS mapping이 복구되지 않은 상태

## 운영 정책

- 백업은 암호화하지 않은 상태로 저장하지 않는다.
- 운영 restore는 빈 환경 또는 새 볼륨에만 수행한다.
- restore 전 현재 운영 DB는 별도 snapshot으로 보존한다.
- restore 후 WORKS relay는 수동 승인 전까지 비활성화한다.
- 월 1회 이상 staging restore rehearsal을 수행한다.
- schema migration 직전 수동 backup을 강제한다.

## 남은 결정 사항

- RPO/RTO 목표값
- 백업 저장 위치와 암호화 key 관리 방식
- ClickHouse 장기 보관 기간
- WORKS outbox replay 정책의 운영 기본값
- 운영 restore 승인자와 절차
- restore rehearsal 자동 실행 주기
