docs: 13단계 전환 준비 — 계약 6절 · 디자인 보드 넷 · README #10

Merged
ttipo merged 8 commits from feat/mail-cutover-prep into main 2026-11-02 19:18:08 +09:00

8 Commits

Author SHA1 Message Date
ttipo 6106351351 docs(rules): 시간대도 테스트 입력 — CI 는 UTC, 개발 PC 는 KST
CI (Flutter) / Flutter (analyze · test) (pull_request) Successful in 20m45s
CI / Backend (lint · type-check · test · e2e · build) (pull_request) Successful in 13m40s
CI / Frontend (lint · type-check · test · build) (pull_request) Successful in 13m8s
CI / Infra (compose · Dockerfile · nginx · 운영 스크립트) (pull_request) Successful in 1m57s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 11m20s
CI (Flutter) / Flutter (analyze · test) (push) Successful in 20m14s
CI / Backend (lint · type-check · test · e2e · build) (push) Successful in 13m42s
CI / Frontend (lint · type-check · test · build) (push) Successful in 12m31s
CI / Infra (compose · Dockerfile · nginx · 운영 스크립트) (push) Successful in 1m50s
CI / 에러 코드 SSOT 동기화 검사 (push) Successful in 10m48s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:19:58 +09:00
ttipo a7b9d00931 test(frontend): 앱 비밀번호 · 로그인 기록 테스트의 시각을 지역 시각으로 — CI(UTC)에서도 통과
화면은 시각을 브라우저 시간대로 보이는데, 두 테스트의 고정 시각이 UTC 문자열이라
"오늘 오전 9:12" 같은 기대 글이 KST(개발 PC)에서만 맞았다 — CI(UTC)에서는 "오늘 오전 12:12" 로 세 건 실패.
고정 시각을 지역 시각(new Date(2026, …))으로 만들고, 같은 값을 다시 적던 기대값은 고정값을 가리키게 했다.
UTC · KST · America/Los_Angeles · Pacific/Kiritimati 에서 34건 통과, 전체 2,354건 UTC 통과.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:19:57 +09:00
ttipo 6c8c54d117 docs(rules): 13단계 부품 — 제한된 세션 · 운영 도구 · 백업 · 감시 · 스킬 표 · env
CI (Flutter) / Flutter (analyze · test) (pull_request) Successful in 20m37s
CI / Backend (lint · type-check · test · e2e · build) (pull_request) Successful in 12m57s
CI / Frontend (lint · type-check · test · build) (pull_request) Failing after 7m54s
CI / Infra (compose · Dockerfile · nginx · 운영 스크립트) (pull_request) Successful in 2m5s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 12m3s
- 스킬 표: nestjs(제한된 세션 · 2단계 필수 정책 · 받은편지함 알림 · 운영 도구 · 15초 동기화 · 만료 계정의 엔진 사실), vue3(제한된 세션 화면 · 로그인 틀 단계 · 유예 줄 · 메일 프로그램 연결 · 30일 줄),
  docker(이미지 표 — PostgreSQL · restic · shellcheck, 자동 설정 server, 호스트 systemd 백업 · 감시 절, setup 의 관리자 단단히 · 휴지통 30일, 로그 앵커 규칙, CI infra 잡).
- 규칙: security(제한된 세션 · 운영 도구 비밀 · 관리자 계정 · 알림 메일 링크 없음 · 퇴사 마무리 · 백업 비밀 · restic 의 root 예외), logging(컨테이너 로그 보존 · 실행 기록 · 자동 설정 쿼리),
  env-structure(자동 설정 env · DB_IMAGE · ops.env 예외 · OPS_ALLOW_NO_TTY), error-codes(AUTH_006 ~ AUTH_010), checklist(13단계 운영 준비), release-ops 담당 범위(mail/ · ops/).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 15:37:30 +09:00
ttipo ad6cea79fc docs: 13단계 계약 · README — 구현하며 정한 것
- 계약 6절: 엔진 실측(만료 계정은 대리 접근 메서드도 막힘 · 2단계를 먼저 봄 · 잠긴 계정에도 402), 제한을 푸는 규칙(그 세션만 · 다음 제한으로 · 2단계를 켜면 다른 제한 세션은 끊음),
  운영 도구의 차례(재설정 · 잠금 · 퇴사 마무리)와 결과를 모르는 실패, TTY, 만료 계정의 메일 API 401, 2단계를 켠 잠긴 계정의 로그인 AUTH_004.
- 디자인 README 13단계: 빈 칸 · 넘기지 못함 · 모르는 제한 상태.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 15:37:30 +09:00
ttipo 1881ec7a42 feat(frontend): 13단계 전환 준비 화면 — 새 비밀번호 · 2단계 켜기 단계 · 유예 줄 · 메일 프로그램 연결 · 30일 안내 · 문의 줄
- 제한된 세션: restriction 이 있으면 보호 화면 대신 로그인 틀의 그 단계(새 비밀번호 → 2단계 켜기)로, AUTH_010 은 알림 없이 me 를 다시 받아 옮긴다.
  제한된 동안은 나가기 확인을 묻지 않고(확인에서 이동을 취소하면 메일 화면에 갇혔다 — 리뷰) 실시간 알림을 열지 않는다.
- 로그인 때 넣은 비밀번호는 반응형이 아닌 클로저에 10분 · 세션이 끝나면 지운다. 2단계를 켠 직원의 새 비밀번호 단계는 다음 코드를 받는다.
- 유예 노란 줄(시작일 전날까지), 설정 › 2단계 인증의 필수 안내, 설정 › 메일 프로그램 연결(펼침 줄 · 새 Outlook 은 권하지 않음), 휴지통 · 스팸메일함 30일 줄, 로그인 · 코드 단계의 문의 줄.
검증: lint · type-check · 단위 2,354 · 실사용 시험 89/89.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 15:37:14 +09:00
ttipo 452d9fef1d feat(backend): 13단계 전환 준비 — 제한된 세션 · 2단계 필수 · 운영 도구 · 받은편지함 알림 · 문의 연락처
전 직원 전환 때 관리자가 계정을 다루고(입사 · 비밀번호 재설정 · 2단계 해제 · 잠금 · 퇴사 마무리 · 공용 주소) 회사가 2단계 인증을 필수로 할 수 있게 했다.
- 제한된 세션(AUTH_010): 임시 비밀번호(엔진 만료) · 2단계 필수인 직원은 로그인은 되지만 새 비밀번호 · 2단계 켜기만 지난다. 푸는 요청은 그 세션만 다음 제한으로 바꾸고,
  2단계를 켜면 2단계를 거친 적 없는 다른 세션은 끊는다. 만료는 게이트웨이 대리 접근 세션의 권한(다섯)으로 로그인 · 갱신 때 알아챈다.
- 운영 도구 CLI(dist/cli/ops.js): 바뀔 것 → y/N → 실행 → ops_audit_log. 관리자 비밀번호는 TTY 에서만 · 입력 기록 0, 재설정은 앱 비밀번호 → 세션 → 알림 → 만료 차례(만료 뒤 대리 접근이 막힌다).
- 받은편지함 알림 메일(Email/import — 링크 없음): 재설정 · 2단계 해제 · 위험 파일 판정. 끊은 세션 기억은 15초마다 DB 에서 채운다.
- 2단계를 켠 잠긴 계정도 로그인은 AUTH_004 — 엔진이 잠긴 계정에도 2단계를 먼저 봐, 게이트웨이 자신의 권한으로 잠김과 설정 문제를 가른다(실사용 시험에서 찾음).
검증: lint 0 · 단위 3,138 · e2e 266 · 리뷰 두 번 · 실사용 시험(브라우저 + 운영 도구) 89/89.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 15:37:03 +09:00
ttipo 73babeaa2c feat(docker): 13단계 운영 기반 — 백업 · 감시 · 관리자 단단히 · 휴지통 30일 · 메일 프로그램 자동 설정
전 직원 전환 전에 서버가 스스로 지키고 되살릴 수 있게 했다.
- 관리자 단단히(setup): 관리자 역할에서 impersonate 를 빼고 관리자 허용 IP 를 앱 네트워크로 — 관리자 비밀번호로 바깥 993 · JMAP 에서 직원 메일함에 대리 로그인되던 길을 막았다.
  운영에서 처음 걸 때는 되돌릴 복구 관리자가 있을 때만 건다(관리자 잠김 방지).
- 휴지통 · 스팸 30일 정리를 KST 03:00 으로 명시. imapsync 로 옮긴 메일도 옮긴 때부터 센다(임시 엔진 실측).
- restic 백업(호스트 systemd 03:30 — 메일 볼륨 warm → 엔진 멈춤 9초 안팎 → final, pg_dump, 업로드, env) · 복원(새 서버 차례를 빈 클론에서 실측) · 15분 점검 · 부팅 되살리기, Healthchecks 하트비트.
- 메일 프로그램 자동 설정: frontend nginx 의 autoconfig · autodiscover 고정 응답(엔진 것은 POP3 를 알리고 입력을 되돌려 쓴다).
- 모든 서비스 docker 로그 보존, DB 이미지 판 · digest 고정.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:44:44 +09:00
ttipo f0e7890bf1 docs: 13단계 전환 준비 — 계약 6절 · 디자인 보드 넷 · README
관리자가 준 임시 비밀번호 바꾸기 · 2단계 인증 필수 · 메일 프로그램 연결 안내 · 받은편지함 알림 메일 · 휴지통 30일 안내와 서버 안 운영 도구를 정했다.
엔진 실험(Stalwart 0.16.27): 계정 끄기 칸이 없어 로그인 권한을 빼서 잠그고, 비밀번호가 만료되면 웹메일(대리 접근)도 메일 메서드가 막혀 서버가 새 비밀번호 단계로 보낸다.
사용자 결정(2026-10-31): 새 비밀번호 단계는 다음 코드를 기다려 넣고, 필수 직원의 휴대폰 바꾸기는 관리자 해제, 알림 메일에는 링크를 넣지 않고, 새 Outlook 은 권하지 않는다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 22:47:30 +09:00