# 아이서티 오프라인 유지보수 준비

Last reviewed: `2026-09-30`, 연결 버전·오류 구분·USB 해제·터미널 언어·자원 보호 보완.

연결 단절 후 test 재현·범용화 준비는 [ct 재사용 인계](CT_REUSE_HANDOFF.ko.md)를 우선한다.
최신 수동시험은 Web signer 설정·Redis override 문제와 ct 포트 병합 수정 뒤 발급/apply/start/WSS와
camera3 시각화까지 통과했다. field CA·계정은 준비됐고 office를 유지한 수동 재부팅 시험 대기다.
아래의 이전401/400/500·미로그인 상태는 해당 시점의 기록이며 최신 상태와 구분한다.

기존 군중 배포를 보존하면서 현장 긴급 분석·Python APP 패치와 작업 기록을 준비했다.
설치 준비 완료와 새 현장 네트워크·브라우저 수용 완료는 구분한다.
기능 원본은 [tools/icerti_offline](../../tools/icerti_offline/README.ko.md),
검증은 [마감 확인](../profiles/ltb1021/topics/runtime-lifecycle/validation/20260928T180549+0900-validation-offline-prep-closeout.md)에 있다.

## 환경 프로필과 현장 설정 (2026-09-30)

신규 주소·계정 전환에는 아래 경로를 우선한다. 이전 `ct connection`/`ct auth`는
현재 Runtime에 대한 호환 명령이며 선택 프로필을 자동으로 사용하는 명령이 아니다.
설치 확인과 실제 네트워크·Web 수용 시험은 분리한다.

| 짧은 명령 | 동작과 확인 경계 |
| --- | --- |
| `ct` | 언어 선택 가능한 번호 메뉴. sudo 한 번 인증 후 메뉴 내 관리 명령 재사용 |
| `ct profile list` / `ct profile select` | 스인 내부망/폐쇄 현장망 중 선택만 변경. NIC·로그인 자동 변경 없음 |
| `ct profile create` / `edit` / `file` | 프로필 생성·안내식 편집·si로 nano 편집. VSCode와 같은 JSON 원본 |
| `ct profile paths` | 편집 원본·계정·공개 CA·실행 Compose·env·적용 명령의 절대 경로 |
| `ct profile web` / `credentials` | 주 Web 하나 선택 / 해당 Web의 계정 숨김 입력. 로그인은 별도 |
| `ct ca` | inbox·USB 선택/읽기 전용 마운트·직접 경로 탐색. 공개 CA 지문 확인 후 복사 |
| `ct net status` / `apply` / `restore` | 실제 주소 확인 / 물리 콘솔에서 명시적 NIC 적용 / 이전 설정 복구 |
| `ct config prepare` | 현재 NIC 주소·프로필·계정·CA를 별도 준비본에 고정. CSR 생성, 원격 요청 없음 |
| `ct config connect` | 선택 Web에 로그인·인증서 요청1회. 기존 서비스 정지 필요. 불명확한 결과는 자동 재시도 없음 |
| `ct config apply` / `resume` | 검사·백업·정합화·기준 지문 등록. 서비스는 stopped 유지, 기동은 `ct start` |
| `ct emergency` | 지원되는 Compose/env/site 수동 변경의 차이를 보고 프로필에 수용. 이후 prepare/connect/apply |
| `ct config restore` | 이 적용이 바꾼 로컬 설정만 복구. 이후 수동 변경 충돌 시 보존; DB·미디어·원격 세션 복구 아님 |
| `ct config note` / `confirm` / `export` | 비민감 작업 메모 / 사용자 확인 기록 / 검토 후 ZIP 반출 |
| `ct diagnose` | 후보별 TCP 연결만 유한하게 확인. TLS·로그인·WSS 성공을 대신하지 않음 |
| `ct config version status` / `ct config version 2` / `ct config version auto` | Web·AI ID별 로컬 버전 이력/명시적 지정. 원격 요청 없음 |
| `ct usb status` / `mount` / `unmount` | USB만 선택 마운트/해제. 시스템·데이터 디스크 보호, 강제/지연 해제 없음 |
| `ct lang en` / `ko` / `auto` | ASCII 영어/한국어/콘솔별 자동 선택. OS locale/font 변경 없음 |
| `ct resources` / `ct resources stop PID` | CPU/RSS·실행시간·용도 확인, 사용자 확인 후 제한된 VSCode 프로세스만 종료 |
| `ct vscode status` / `apply` / `restore` | 원격 si의 탐색/분석 설정과 선택적 복구. 프로세스 자동 재시작 없음 |
| `ct net arp status` / `ct net arp set MAC` / `ct net arp clear` | 선택 환경의 Web neighbor만 조회/명시적 임시 보정/동적 복귀 |

- 원본: `/workspace/contextra/setup/profiles/office.json`, `field.json` (si 소유,600).
  복수 Web·미디어·Redis 허용 목록은 이 파일에서 편집한다. `selected_web` 하나만 제어 대상으로 사용한다.
- 비밀: 같은 디렉터리의 `<profile>.private/<web>.credentials.json` (si 전용600).
  공개 CA는 `<web>.ca.crt`. 계정/CA는 환경·Web별 분리하며 이전 계정을 자동 복사하지 않는다.
- 권장 반입: `/workspace/contextra/setup/inbox`. USB 원본은 유지한다. private key는 CA 반입에서 거절한다.
- 직접 편집은 먼저 저장하고 `ct profile paths`에 나온 준비/적용 순서를 따른다.
  Enter는 표시된 현재값을 유지하고 `-`는 허용된 선택 항목만 비운다. 새 프로필은 기존 일반 설정을 복사하되 계정/CA는 복사하지 않는다.
- AI control/WSS·Redis·Web origin 포트는 프로필로 관리한다. SSH 리스너는1023을 유지하며
  NIC 적용에 SSH 포트 변경을 섞지 않는다. 미디어 목록은 참고용이고 Web의 `rtspUrl`은 바꾸지 않는다.
- 스인 DHCP는 현재 주소를 준비 때 읽는다. 폐쇄 현장은 고정 주소/prefix, Gateway/DNS 없이 준비한다.
  프로필 선택만으로 두 망을 혼합하거나 자동 전환하지 않는다. NIC 적용은 `netplan try` 확인을 사용한다.
- networkd wait-online은 단일60초 대기로 제한한다. 시간 초과 후에도 networkd는 계속 동작한다.
  다른 장치·디스크·서비스에 걸리는 시간까지 포함한 전체 부팅60초 보장은 아니다.
- 일반 설정 SHA 불일치는 메뉴·진단·수정 사용을 막지 않는다. image·코드·mount·인증서·계정·디스크 검사는 유지한다.
  수용한 설정은 새 transaction과 기준으로 영속화하며, 긴급 허용을 전역으로 남기거나 재부팅 때 임의 원복하지 않는다.
- 로컬 복구 기록: `/var/lib/contextra-maintenance/field` (root 전용).
  중단 시 `ct config status`가 prepare/apply/network 잔여 상태를 표시한다. 원격 결과가 미확정이면
  먼저 확인하고 `ct config retry`를 명시적으로 선택한다. 재시도는 자동으로 수행하지 않는다.
- 반출: `/workspace/contextra/setup/outbox/field-change-<시각>-<고유값>/`에
  `SUMMARY.ko.md`, `changes.redacted.json`, `verification.json`. 비밀번호/token/private key는 제외한다.
  수동 메모는 사용자가 비밀정보를 쓰지 않고 ZIP 전에 검토한다. 검증 미완료는 사용자 확인 대기로 남긴다.

이번 도구 패치는 실제 IP 전환·로그인·CA 발급·카메라 조작·재부팅을 수행하지 않는다.
이후 사용자 동반 시험은 `ct config status` → `ct profile paths`에서 시작한다.

## 연결 설정 버전과 실패 구분

`connectionConfigVersion`은 요청 횟수나 DB 행 번호가 아니다. 선택 Web에 등록된 같은 AI ID의
접속정보 개정 번호다. 같은 번호/같은 내용은 허용, 같은 번호/다른 내용은 충돌, 더 작은 번호는
오래된 설정으로 거절되고 더 큰 유효 번호는 갱신 후보가 된다. 로그인 계정은 Web에 먼저 있어야 한다.

- 사람이 편집하는 선택 Web 항목은 `webs[].connection_version`이다. 생략하면 로컬 이력과 같은
  Web/AI의 기존 실행 설정으로 자동 제안한다. 파일 맨 위 `version: 1`은 프로필 형식이므로 변경하지 않는다.
- 허용 범위는 정수 `1..9007199254740991`이며 반드시 +1만 가능한 것은 아니다. 임의의 큰 번호로
  자동 반복하지 않는다. 다른 AI 장비가 등록한 Web의 최신 번호는 로컬 이력만으로 알 수 없다.
- `/var/lib/contextra-maintenance/field/connections/`에 Web·serverId별 마지막 준비/성공 이력을
  별도로 보존한다. 준비는 성공이 아니며 로컬 성공도 다른 장비의 이후 원격 변경을 보장하지 않는다.
- 실패 준비본이 있으면 먼저 `ct config status`로 원인을 확인한다. 명확히 거절된 미발급 요청은
  `ct config cancel`로 보존·선택 해제한 뒤 `ct config version 번호` → `ct config prepare`로 새로 준비한다.
  원격 결과 미확정·적용 중에는 무조건 취소/초기화하지 않는다. 준비 파일의 SHA를 손으로 고치지 않는다.
- 새 준비본에는 실제 송신 번호가 표시된다. 이후 사용자 명시 `ct config connect` 한 번으로 진행한다.
- 버전 저장 명령은 저장 후 파일을 다시 읽어 실제값을 출력한다. 발급 실패의 허용된 details.reason은
  결과 파일과 안내에 보존한다. Web CA 서명 설정 누락을 버전 증가·새 CA 생성으로 우회하지 않는다.
  서비스·카메라는 자동 Start하지 않는다. 성공한 동일 설정은 재부팅/재로그인 때 번호를 올리지 않는다.
- 오류는 LOGIN/CERTIFICATE 단계, HTTP 상태, 허용된 Web 오류 code/field로 표시한다. 비밀정보와
  응답 원문은 출력하지 않는다. 시도 수는 해당 준비본의 명시적 helper 실행 수이며 이전 미계측은 별도 표시한다.
- 버전 충돌400과 계정 인증401, 인증서 API의 token401, TCP/TLS 실패는 별개다. 401을 버전 증가로
  해결하지 않는다. 인증서 발급 성공과 WSS/Redis/브라우저 수용도 별도로 확인한다.

## USB·언어·자원 보호

- `ct usb mount` 또는 `ct ca`의 USB 메뉴는 실제 USB filesystem만 열거하고 명시 확인 후
  `/mnt/contextra-usb/장치명`에 `ro,nosuid,nodev,noexec`로 마운트한다. fstab/포맷을 변경하지 않는다.
- `ct usb unmount`는 해당 USB와 허용된 마운트 경로를 재확인한 뒤 일반 `umount`만 한다.
  `/backup`, `/training`, 시스템·데이터 mount 및 하위 mount가 있는 대상은 거절한다.
  사용 중이면 닫을 파일/터미널을 확인하고 재시도한다. `-f`/`-l`이나 재귀 해제는 없다.
- `ct lang auto`는 물리 Linux 콘솔 ASCII 영어, SSH 가상 터미널 한국어를 사용한다.
  `/var/lib/contextra-maintenance/field/language.json`에 명시 선택을 보존한다.
  영어 모드에서 한글 경로는 Unicode escape와 번호로 식별하며 실제 파일명은 바꾸지 않는다.
- VSCode 원격 설정은 `/home/si/.vscode-server/data/Machine/settings.json`이다. `/` 탐색과 파일
  접근권한은 유지하되 npm 자동 감지·검색 symlink 추적을 끄고 가상 filesystem/대용량 영역을 제외한다.
- Python/Pylance는 `light`, 열린 파일 진단, indexing Off, 자동 경로·환경 탐색 제한을 사용한다.
  편집/디버깅 확장은 제거하지 않는다. 전체 workspace 분석과 자동 import 색인은 제한된다.
  사용자 workspace 설정이 원격 기본값을 덮어쓸 수 있으므로 무조건 무부하를 보장하지 않는다.
- `ct vscode restore`는 이 도구가 바꾼 설정만 복구하며 사용자 후속 변경과 충돌하면 보존한다.
  원본 백업은 root 전용이며 unrelated 설정의 비밀정보를 반출하지 않는다.
- `ct resources stop PID`는 si의 VSCode 파일 탐색/extensionHost만 명시 확인 후 PID identity를
  검증해 SIGTERM한다. extensionHost 전에 파일 저장·확장 작업 중지를 확인한다. 전체 VSCode/SSH/
  터미널/Runtime을 종료하거나 메모리 캐시를 강제로 비우지 않는다. 자동 SIGKILL 승격도 없다.
- ct가 실행한 관리 subprocess는 timeout/취소 때 전용 process group을 회수한다.
  Docker daemon에 이미 접수된 작업이나 임의 외부 프로세스 전체 정리를 의미하지 않는다.

## 재부팅과 종료 경계

- ARP의 `PERMANENT`는 현재 kernel에서의 상태이지 재부팅 영속 설정이 아니다. `ct net arp`는
  현재 선택 환경만 다루고 MAC을 추측하지 않는다. 별도 확인한 MAC으로 명시 보정하며 회사망 예외를
  폐쇄 현장에 복사하지 않는다. 실제 IP 전환·재부팅은 사용자 동반 시험으로 남긴다.
- Gateway는 활성 WSS 연결과 graceful shutdown 대기가 종료 지연 후보다. 종료137을 관측한 사실과
  직접 원인은 구분한다. ct stop은 다른 회수를 마친 뒤 강제 종료를 별도 경고하며 graceful PASS로 숨기지 않는다.
  실제 종료 동작·WSS 수용은 아직 재시험하지 않았고 timeout을 늘려 통과시키지 않았다.

## 준비 상태

| 항목 | 준비와 확인 |
| --- | --- |
| `ct stop/start` | 정상 Stop/reap, mount/transaction/source guard, 사용자 실제 `ct start` 성공 |
| `ct apply` | 현재 image 위 Python APP만 offline build; snapshot/hash/CAS, 자동 Start 없음 |
| `ct work` | begin/note/end/diff/export; 실제 si 기록 smoke 및 변경·추가·삭제 표적 테스트 |
| `ct access` | /workspace si 읽기·쓰기 ACL 재적용; 인증 필수 예외·기존 실효 권한·소유자 보존 |
| `ct auth` | AI 연결용 ID/비밀번호 변경·로그인1회·WSS 확인·중단 적용 재개/취소. 실패/미확정은 자동 반복하지 않음 |
| `ct connection` | plan/prepare/enroll/apply와 공개 CA 검사; 변환·경계 테스트 완료, 새 주소 실연동은 현장 확인 |
| local LLM/agent | 기존 Qwen GGUF, 한국어 CLI 및 agent smoke, 승인형 명령 실행; 기본 Off |
| 관측 | Prometheus/Grafana/Node/GPU 지표 실제 확인; 수동 실행, 기본 Off |
| 임시 GUI | Xfce/IBus·CLI 실행 경로 준비, 기본 Off; 물리 화면은 현장 확인 |
| Windows 연결 | 새 주소를 인자로 받는 SSH 터널·OpenCode 설정·실행기 준비; 실제 PC 확인은 별도 |

## si 권한

| 경로 | 일반 si 권한과 용도 |
| --- | --- |
| `/workspace/contextra/source/crowd-0f172d94/app` | 소스124파일 읽기·쓰기; 실제 파일 생성·읽기·정리 확인 |
| `/workspace/contextra/source` | 작업본 생성·편집, si ACL 상속 |
| `/workspace/contextra/setup/inbox` | 새 공개 CA·검토할 입력 자료 읽기·쓰기 |
| `/workspace/contextra/setup/outbox` | 반출할 검토 자료 읽기·쓰기 |
| `/workspace/contextra/setup/notes` | 현장 메모 읽기·쓰기 |
| `/workspace`, `/workspace/contextra/runtime`, `/workspace/contextra/data` | 후속 승인으로 목록 조회·읽기·쓰기 개방. 데이터 bind view에도 반영 |
| `/workspace/contextra/setup/guides` | 후속 승인으로 읽기·쓰기 가능 |
| 인증 session/TLS 디렉터리, private env/key | 기동 시 권한 검사 때문에 예외 유지. 관리에는 기존 sudo 사용 |

사용자가 후속으로 /workspace 전체 쓰기 개방을 승인했고 기동 필수 보호 영역의 예외에 동의했다.
`chmod777`·chown 대신 si named ACL과 directory default ACL을 사용한다. 기존 ACL은 root 전용
백업에 보존하며 다른 ACL 계정의 숨겨진 권한을 확대하지 않는다. 명시적600/700으로 새로 만든
일반 파일은 필요할 때 `ct access`로 재적용한다. 자동 백그라운드 작업은 추가하지 않는다.
sudo는 기존 방식으로 입력하며 NOPASSWD는 추가하지 않았다. SSH 키는 보존한다.

## 사용 순서

1. `/workspace/contextra/setup/contextra.code-workspace`, source 또는 `/`를 열 수 있다. 루트를 열 때는 위 원격 탐색 보호 설정을 유지한다.
2. 분석·수정 전에 `ct stop`으로 정상 회수한다. 서비스 가동 중 LLM/GUI를 동시에 시작하지 않는다.
3. `ct work begin /workspace/contextra/source/crowd-0f172d94`로 기준점을 남긴다.
4. `ct-llm chat`, 또는 `ct-llm serve`와 `ct-agent`를 사용한다. 변경 이유·명령·검증은 `ct work note`로 남긴다.
5. `ct work end` 뒤 diff를 검토하고 `ct apply /workspace/contextra/source/crowd-0f172d94`를 명시적으로 실행한다.
6. LLM 종료·GPU 반환 뒤 `ct start`로 서비스만 기동하고 모델·Web 연결을 확인한다.
7. 기록을 검토한 뒤 `ct work export 기록이름 --reviewed`로 다음 USB 패치 검토에 전달한다.

Runtime은 si의 편집 폴더를 직접 bind하지 않는다. `ct apply`가 root 소유 snapshot을 만든 뒤
기존 SDK/native/model을 보존한 작은 APP image를 적용한다. SDK·모델·의존성 변경에는 쓰지 않는다.
카메라 자동 Start와 자동 rollback은 하지 않는다. 설치·작업 안내 전체는
[운영 명령](../../tools/icerti_offline/maintenance/OFFLINE_OPERATIONS.ko.md),
[주소/CA 전환](../../tools/icerti_offline/maintenance/NETWORK_TRANSITION.ko.md),
[Windows 준비](../../tools/icerti_offline/windows/WINDOWS_PREP.ko.md)를 따른다.

## 반입 및 남은 확인

- USB 최종 묶음: `contextra-offline-tools-20260928-r03.tar`, 1,593,210,880bytes,229파일.
- SHA256: `ba893f0358c5a032b2bbcfc0f646f0aad5bbb3c2845b8242deb40a4360840f86`.
- 원본 위치와 제외 항목은 [USB 안내](../../tools/icerti_offline/USB_README.ko.md)와 검증 기록을 따른다.
- 큰 실행 바이너리·image·DEB·GGUF·DB·원본은 Git에 넣지 않는다. USB도 전체 서버 복구본은 아니다.
- 새 현장 IP/CA/Manager·Event 주소, Windows 실제 연결, 물리 GUI는 아직 현장 값·장비로 확인해야 한다.
- 기존 계정 요청은401이었다. 사용자 제공 새 계정의 요청은 정상 AI 세션 미확보로 적용하지 않았다.
  이후 사용자가 Web PC 전원 Off를 알려 재시도를 중단했고, 기존 파일·Sidecar를 복구했다.
  새 계정 값의 성공은 미검증이며 Web을 켠 뒤 `ct auth update`로 확인한다. 실제 값은 문서에 기록하지 않는다.
- 부팅 기본은 `multi-user.target`으로 정합화했다. 실제 서버 재부팅 없이 persistent mount/service와 기동 guard를 확인했다.
- 사용자 승인으로 Event·학습 미디어와 전용 원장을 초기화했다. 카메라·모델·인증·보관 설정 및 Web DB는 보존했다.

기존 설치 완료 서버에서 `install_*.py`를 일괄 재실행하지 않는다. 사용·권한은 확인되어 있으며,
이번 Git 보관은 현장 Runtime이나 이미 검증한 USB 묶음을 다시 변경하는 작업이 아니다.
