# ct 자료 정리와 보관 정책

Last reviewed: `2026-10-07 11:40:00 KST`. Owner: runtime-lifecycle / CTCLR-01~10.
대상은 portable Compose backend의 test와 AI 서버2다. 청계산 설치본에는 적용하지 않았다.
실자료 삭제·초기화·새 APP Runtime 기동은 이번 검증에 포함하지 않았다.

## 처음 확인

```bash
ct
ct storage status
```

AI 서버2의 일반 `si`가 `ct` 메뉴를 열면 sudo 인증을 먼저 받는다. 이미 root인 test와
systemd는 대화형 비밀번호를 요구하지 않는다. `ct <명령>`은 기존 sudo 인증 유효시간을 따른다.
실행 도구는 root 소유로 보호하고, 메뉴 진입 전에 Python이 보호 경로를 읽지 않게 했다.
`ct` 32~36번 메뉴에서 자료 정리·DB/카메라·Redis·보관 정책·백업 관리로 이동한다.

## 명령과 삭제 범위

| 명령 | 관리 대상 | 유지·제외 |
| --- | --- | --- |
| `ct data clean` | 완료된 Event 미디어와 소유권 확인된 학습 묶음 | 작성·전송·재시도·예약 중 자료, 원장 이력 |
| `ct artifact clean` | Event 미디어 | EventID·전송 이력, 보호 작업 |
| `ct training clean` | 완료된 학습 묶음 | 작성 중·소유권 불명 자료, 학습 설정 |
| `ct db reset` | 관리 대상 Event·학습·분석 업무기록 | DB/volume 자체, 인증, 카메라 설정, 타 서비스 |
| `ct camera reset` | AI 로컬 added 카메라 등록·설정 | Web DB, 기본 템플릿, 기존 미디어, 인증 |
| `ct redis clean` | 현재 Contextra namespace의 camera frames/status와 해당 매핑 | 다른 namespace·다른 키; FLUSHALL 없음 |
| `ct data backups` | 완료된 정리 작업의 복구본 조회·선택 삭제 | 미완료 작업 복구본 |

`db reset`의 기존 분석 테이블은 `active_learning_samples`, `detections`, `tracks`, `events`,
`frames`, `system_metrics`다. 실제 schema와 FK 경계를 검사하며 알려지지 않은 관계는 중단한다.
미디어를 남기면서 연결 원장만 지우지 않는다. 연결 미디어 삭제는 용량을 별도로 표시하고
`MEDIA` 확인을 추가로 받는다. 이 명령은 Web DB를 초기화하지 않는다.

## 실행 순서

1. 서버·project·실제 경로와 영향 안내를 확인한다. 분석이 실행 중이면 `STOP` 승인 후
   정상 중지한다. 이후 카메라/분석을 자동 재Start하지 않는다.
2. 기본은 해당 관리 범위 전체다. `Enter=전체 선택`은 삭제 실행이 아니다.
   개별·카메라·날짜 선택과 용량 막대를 제공한다. 방향키/Space/Enter 또는 번호 입력을 쓴다.
3. 최종 건수·용량·보호 제외를 확인한다. 초기화는 기본 백업 성공 후 진행한다.
   이번 요청만 `--no-backup` 또는 메뉴의 무백업 선택을 사용할 수 있다.
4. `DELETE`, `RESET`, `RESET NO BACKUP`을 정확히 입력해야 실행한다. 확인의 Enter는 취소다.
   미디어 자동 백업은 없으며 삭제된 미디어는 복구할 수 없다.

```bash
ct artifact clean --plan
ct training clean --camera 3 --before 2026-10-01 --plan
ct camera reset --camera 3
```

`--plan`은 삭제도 분석 중지도 하지 않는다. 분석이 실행 중이면 중단한다.
목록을 읽기 위해 해당 project의 DB/Redis만 일시 준비할 수 있으며 원래 정지 상태로 돌려놓는다.
`--before`는 KST 날짜 미만, 메뉴의 날짜 선택은 해당 날짜와 일치다. camera/Redis 초기화에는
날짜 필터를 쓰지 않는다. 소스별/테이블별 10,000건을 넘으면 범위를 줄이라는 오류로 중단하며
일부만 선택해 전체 처리한 것처럼 표시하지 않는다.

## 중단과 복구

출력된 `job-...`와 같은 명령의 `--resume job-...`을 사용한다. 대상 hash·server·원장 상태를
다시 검증하고 완료 항목을 중복 차감하지 않는다. 새로운 동시 변경이 있으면 중단한다.
작업 기록은 `ct` 설치 정의의 state 아래 `data-jobs/job-...`에 저장한다.

DB/카메라 복구본은 mode600이며 URI 등 민감정보를 포함할 수 있으므로 외부 공유하지 않는다.
백업 실패·128MiB 초과·root 여유15GiB 미달을 자동 무백업 실행으로 바꾸지 않는다.
완료된 백업만 `ct data backups`에서 삭제할 수 있다. 복구본은 운영자 검토 자료이며
자동 DB/미디어/인증 일괄 rollback 명령은 제공하지 않는다.

카메라 초기화 뒤 다음 Web Start는 기존 기본값 보완·저장 설정 병합과 군중 필수값 검증을
그대로 따른다. Start를 완전 교체 요청으로 바꾸지 않았으며 Web의 설정 원본도 수정하지 않는다.

## 보관 설정

| 서버 | 사람이 편집하는 원본 |
| --- | --- |
| AI 서버2 | `/workspace/contextra/setup/storage.json` |
| test | `/workspace/contextra-runtime-config/ct/setup/storage.json` |

`ct storage edit`과 VSCode는 같은 원본을 사용한다. `version=1`을 유지하고 `artifact`,
`training` 각각의 `max_bytes`, `target_bytes`, `min_free_bytes`, `auto_cleanup`을 편집한다.
숫자는 바이트 정수, auto_cleanup은 JSON boolean이다. 편집 도구의 목표 최소값은128MiB다.
Artifact 자동 정리 활성 시 Runtime은 목표가 상한의95%보다 작은지도 검사한다.

```bash
ct storage edit
ct storage apply
```

edit의 Enter는 현재값 유지다. apply는 분석 정지 상태에서 `APPLY`를 확인한 뒤 적용본
`<실제 Artifact root>/.ct-storage-policy.json`에 저장한다. 적용본·active hash를 직접 고치지 않는다.
다음 승인된 `ct start`가 정책을 읽으며 Web 재로그인은 필요 없다. 하향 변경은 다음 기동 후
정리·저장 제한을 일으킬 수 있다. 자동 정리 Off도 상한·최소 여유 보호를 해제하지 않는다.
상태 화면은 편집값/적용값을 구분하며 실행 중 Runtime 값과 동일하다고 단정하지 않는다.

서버별 기존 정책을 초기 편집 원본으로 보존했다. AI 서버2의 Event400GB/목표360GB,
학습200GB/목표180GB와 최소여유100GB를 test에 복사하지 않았다.
이번에는 `storage apply`를 실행하지 않았으며 기존 env/학습 설정이 계속 기준이다.

### Artifact 순환 보관과 완료 점유 (2026-10-21)

활성 정책은 사용량과 예약 공간에 다음 작업의128MiB 예약 여유를 더한 값이
상한95%에 도달하면 선제 정리를 시작한다. filesystem 최소 여유 부족도 별도 정리 사유다.
목표값까지 오래된 정리 가능 묶음부터 정리하며 최신 자료·작성/전송/재시도 중 자료는 보호한다.
아이서티의400GB/시작380GB/목표360GB·최소여유100GB는10진 기준이다.
test의100GiB/목표90GiB·최소여유50GiB·자동정리Off는 그대로이며, 학습 정책도 변경하지 않았다.

신규 Event 접수에서 파일을 동기 삭제하지 않는다. 기존 관리 thread가 약10초 간격으로
최대64묶음·선택 루프1초·원장 기준256MiB 범위에서 진행한다. 진행 중인 단일 파일 작업과
유한 DB timeout까지 포함한 전체 walltime이 항상1초 이내라는 뜻은 아니다.
후보 조회는 별도 partial index를 사용하며 공용 저장량 row lock을 잡고 검색·파일 삭제하지 않는다.
`retention_accounted_bytes`는 정리한 원장 기록량이며 filesystem의 실제 여유 증가량과 구분한다.
기존 전체 파일 scan은 기동 복구에서 유지하고 주기 scan은 작은 묶음으로 나눈다.
동시 생성·정리로 accounting revision이 바뀌면 scan 결과를 적용하지 않고 다시 확인한다.

완료 결과·예약량·점유 해제는 같은 DB transaction으로 확정한다. DB 확정 실패 시 이미 끝난
renderer/HTTP를 반복하지 않고 완료 결과만 최대8회 재시도한다. 실패 때만 `.completions/`에
작은 private 영수증을 남긴다. 삭제하거나 수정하면 안 된다. 재기동 시 단독 worker 잠금 아래
일반 복구보다 먼저 반영하며, 반영할 수 없으면 불확실한 성공으로 기동하지 않는다.
살아 있는 작업을 나이만 보고 회수하지 않는다. 기존 잔류는 이전 worker의 정상 종료와
단독 잠금이 확인된 기동 복구 경로로 회수한다. Event/run/channel/claim 불일치는 쓰기를 차단한다.

재시도 소진·영수증 오류는 운영자가 원인을 확인할 상태이며 자동 성공 처리나 무한 재시도가 아니다.
삭제 중단으로 사용량을 보수적으로 과대 계상하는 구간은 이후 파일 scan과 원장 상태를 함께 확인한다.
짧은 검증으로 전원 장애 모든 구간의 exactly-once나 장시간 무오류를 보장하지 않는다.
[구현·양 서버 검증과 복구](../profiles/ltb1021/topics/runtime-lifecycle/validation/20261021T171120+0900-validation-artifact-lifecycle.md).

## 설치·패치와 권한

`ct -> field_data/field_storage -> data_host -> 제한된 worker -> 기존 원장/정리 모듈` 순서다.
worker는 GPU 없이 CPU2·메모리768MiB·PID64·read-only root·권한 제거로 실행하며
현재 project의 승인된 데이터 mount만 사용한다. 외부 Event 전송을 요청하지 않는다.
승인된 bind source의 GID는 worker 보조그룹으로 전달한다. capability를 제거한 root도
si 소유770 폴더에는 그룹 접근이 필요하며, 이를 chmod나 privileged로 우회하지 않는다.
실패 시 private job/error.json의 안전한 예외 종류·errno·소스 위치를 확인한다.
Runtime start/stop과 정리 작업을 같은 lock으로 직렬화하고 기존 작성/retention lock도 지킨다.

실행 ct·인증 private key·DB 내부·active 기준은 root 보호를 유지한다. 일반 설정·inbox/outbox·
편집용 소스는 operator가 다룬다. 전체 `/workspace` chown, chmod777, NOPASSWD는 없다.
최종 도구 편집 사본은 각 setup 아래 `source-work/ct-storage-final-20261007/`다.
이는 실행본이 아니며 편집 후 검토된 패치로 설치해야 한다.

`tools/icerti_offline/update_storage.py`는 baseline/file hash를 대조해 도구와 네 APP 파일만
덮는 작은 image를 준비한다. 모델/native/SDK·계정·카메라는 복사하지 않는다.
설치 백업과 영수증은 `<state>/storage-updates/<id>/`에 있으며 후속 supplement 영수증까지
함께 보존해야 한다. 이전 도구 복구는 해당 백업과 현재 지문을 검토한 뒤 별도로 수행한다.

현재 test는 기존5서비스 running, AI 서버2는5서비스 exited다. 새 APP image는 다음 명시적
기동 대상으로만 등록했고 test 실행 중 image는 교체하지 않았다. 새 정책 Runtime 수용·실자료
정리·사용자 선택 UX는 후속 수동시험이다. 청계산은 후속 호환 검토 대상으로만 기록했다.
