Skip to Content

Cortex

한 줄 소개: 투자 네트워크를 다루는 1인용 CRM. 별도 앱을 열어 폼을 채우는 대신, 평소 쓰는 AI 에이전트에게 한국어로 시키면 그 에이전트가 Cortex 에 기록하고 찾아 준다. 데이터를 눈으로 훑을 때는 읽기 전용 웹 포털이 따로 있다.

기록은 전부 본인 것만 보인다. 다른 사람은 물론 관리자도 남의 기록을 조회할 경로가 없다. 모든 변경은 지울 수 없는 이력으로 남고, 각 기록에는 어디서 온 정보인지(구글 연락처 · 메일 · 메시지 · 외부 링크) 근거가 붙는다.

사용하기

여기부터 읽으세요. Cortex 에는 “로그인해서 클릭하는” 별도 업무 앱 화면이 없습니다.

접근 방법 3가지 — 뭘 고를까

방법”front end” 가 뭐냐관리자 권한
① axe CLI (권장)당신의 AI 에이전트 (Claude Code · Codex · Cursor · claude.ai) — 또는 그냥 터미널불필요 ✅
② 읽기 전용 웹 포털브라우저 — cortex.axelabs.ai불필요 ✅
③ claude.ai MCP 커넥터claude.ai 커넥터 설정 화면Microsoft 테넌트 관리자 동의 필요 ⚠️

관리자 계정이 아니면 ①을 쓰세요. claude.ai 에서 Custom Connector 추가가 인증 오류로 막히는 것은 정상입니다 — ③은 테넌트 관리자의 사전 동의가 있어야 동작합니다. 커넥터는 안 해도 됩니다. axe CLI 는 본인 Microsoft 로그인만으로 Cortex 를 쓰게 해줍니다.

① axe CLI — 4단계

1. 설치 (로그인 불필요) — 에이전트 에디션 다운로드는 공개입니다. 계정이 없어도 내려받아 axe --version 까지 확인할 수 있습니다.

mkdir -p ~/axe-cli curl -fsSL "https://axe.axelabs.ai/api/cli/download?edition=agent" -o ~/axe-cli/axe chmod +x ~/axe-cli/axe alias axe="~/axe-cli/axe" # 셸 rc 에도 추가 axe --version # 여기까지는 계정 없이 됩니다

2. 로그인 (계정 필요) — 여기서부터는 AXE 워크스페이스 계정이 있어야 합니다.

axe login # 브라우저로 Microsoft 로그인 1회 axe whoami # 출력 scope 에 cortex 가 보이면 준비 끝

어딘가에 적어 두신 옛 명령이 있다면 버리세요. 예전 안내가 쓰던 curl -fsSL <워크스페이스>/cli -o ~/axe-cli/axe 는 더 이상 동작하지 않습니다 — 공개 /cli 바이너리 경로는 폐기돼 404 입니다. curl -fcurl: (56) The requested URL returned error: 404 한 줄을 내고 종료하므로 파일은 아예 만들어지지 않고, 뒤에 이어 붙인 chmod +x 도 실행되지 않습니다. 로그인한 사용자는 워크스페이스 대시보드의 ”⤓ CLI 설치” 카드에서 토큰이 박힌 설치 명령(/cli.sh 설치기)을 복사해 쓰셔도 됩니다 — 그 설치기는 운영자 에디션까지 받을 수 있어 AXE_TOKEN 을 요구합니다.

3. “front end” = 당신의 AI 에이전트. Claude Code · Codex · Cursor · claude.ai 채팅 중 아무거나 열고, 아래 블록을 그대로 붙여넣으면 그 에이전트가 Cortex 를 쓸 수 있습니다:

너는 AXE 의 `axe` CLI 로 Cortex(투자 인맥 CRM)를 쓸 수 있다 — MCP 커넥터 불요. 항상 (1) 도구를 먼저 조회하고 axe cortex tools (2) 그 스키마대로 호출한다 axe cortex call <tool> --args '<json>' 주요 도구: search_person 이름·회사·메모 등 아무 필드나 substring 으로 인물 검색 get_person 한 인물의 상세 + 근거 + 최근 이력 register_person 신규 인물 등록 (Google 연동 시 내 연락처에도 생성) log_interaction 미팅·통화·이벤트 기록 (참석자별 관계도 자동 생성) classify_person 인물에 분류 태그 부착 (예: 담당=나, 구분=LP) connect_google 내 Google 연락처를 Cortex 로 동기화 시작 규칙: - 도구 이름·인자는 외우지 말고 매번 `axe cortex tools` 결과에서 가져와라. - 딜 코멘트·협상 의견·사적 메모처럼 민감하면 visibility=private 로 저장할지 나에게 먼저 물어봐라(기본 shared). - 호출이 401/403 이면 먼저 `axe refresh`, 그래도 안 되면 `axe login` 재실행을 안내해라.

붙여넣은 뒤에는 그냥 한국어로 시키면 됩니다 — 예: “홍길동 연락처 찾아줘”, “방금 ABC캐피탈 김상무랑 한 미팅 기록해줘”, “내 구글 연락처 Cortex 에 연동해줘”.

4. (선택) 터미널에서 직접

axe cortex tools # 도구 목록 (스키마 포함) axe cortex call search_person --args '{"query":"홍길동"}' axe cortex call register_person --args '{"display_name":"김상무","org":"ABC캐피탈","memo":"2026 Q2 소개"}' axe cortex call connect_google --args '{}' # → 반환된 authorize_url 을 브라우저로 열기

② 읽기 전용 웹 포털 — 눈으로 둘러보기

추가·수정 없이 보기만 할 거면 브라우저로 https://cortex.axelabs.ai → Microsoft 로그인.

화면내용
대시보드네트워크 깔때기 통계
인물 · 조직 · 딜목록과 상세
상호작용미팅 · 통화 · 이벤트 이력
관계사람·조직·딜 사이의 연결
AXE Contacts엑셀형 인맥 마스터 뷰 — 이름 · 이메일 · 전화 · 별점 · Needs/Offers · 레이블 · 대화 노트, 필터와 복사, 레이블 드래그 지정

본인 데이터만 보이고 읽기 전용이다. 등록·수정은 ①(CLI/에이전트)로 한다. 예외적으로 별점 · 대화 노트 · 레이블 세 가지 가벼운 보강은 웹에서 바로 쓴다.

우측 패널의 협업 세션

우측 컨텍스트 패널의 “세션” 섹션에 Blueprint 협업 세션service=cortex 로 분류된 세션이 컴팩트 한 줄 목록(상태 도트: 진행 = 채운 초록, 완료 = 빈 원)으로 뜬다 (D-bp-collab-3, @axe/ui 0.38.0 레일 v2). 머리행 [+] 는 그 자리에서 새 세션을 만들고(원장은 Blueprint, 귀속은 로그인한 본인), 프리셋 아이콘/collab/sessions 목록 페이지로 간다. 행을 누르면 Cortex 안의 세션 상세(/collab/sessions/:id)에서 프롬프트와 게재물을 읽는다 — Blueprint 로 이탈하지 않는다. Cortex 에는 법인 컨텍스트가 없어 생성 시 법인을 붙이지 않는다(서비스 귀속만으로 목록을 얻는다).

이 섹션은 연결되어 있을 때만 나타난다. 연동 설정이 없거나 조회가 실패하면 빈 목록 대신 섹션이 통째로 사라진다 — 비어 보이는 목록은 “세션이 없다”로 읽히기 때문이다. 연동에 필요한 설정은 배포 환경 변수 두 개(CORTEX_BLUEPRINT_INTERNAL_URL · CORTEX_BLUEPRINT_INTERNAL_API_KEY)뿐이고, 인증 키는 Blueprint 와 같은 것을 쓴다(새 비밀 없음).

Google 연락처 연동 (1회)

Cortex 는 본인 Google 연락처를 원본으로 삼아 단방향으로 가져온다.

  1. 에이전트에 “내 구글 연락처 연동해줘” (또는 axe cortex call connect_google --args '{}')
  2. 반환된 authorize_url 을 브라우저로 열어 Google 동의
  3. ”✓ 연결 완료” 후 다음 동기화 주기(또는 sync_google_now)부터 자동 유입

민감 라벨(기본값 Personal · HPE)이 붙은 연락처는 애초에 들어오지 않는다. 허용 라벨을 지정하면 그 라벨이 붙은 연락처만 통과시킬 수도 있다. 라벨 목록을 읽지 못하면 그 주기를 통째로 건너뛴다 — 필터가 불확실한 채로 유입시키지 않는다.

데이터가 쌓이는 방식

Cortex 의 모든 기록은 artifact 하나의 형태를 공유한다. 사람 · 조직 · 딜 · 상호작용 · 관계 · 메모 · 분류가 전부 같은 구조의 typed fact 이고, 내용은 자유 형식이라 미리 정의하지 않은 필드도 그대로 저장된다.

artifact 사람 · 조직 · 딜 · 상호작용 · 관계 · 메모 · 속성 └─ 내용 약속된 필드 + 임의의 추가 필드 └─ visibility shared | private citation 근거 — 구글 연락처 · 메일 · 메시지 · 외부 링크 event 누가 · 언제 · 무엇을 했는지 (추가만 가능, 수정·삭제 불가)

관계도 artifact 의 한 종류다. introduced_by(누가 소개했나) · works_at · attended(미팅 참석) · lp_invested_fund 같은 연결 종류를 붙여 두 기록을 잇는다. 미팅을 기록하면 참석자마다 관계가 자동으로 생긴다.

기록을 지우는 대신 보관(archive) 한다. 목록에서는 사라지지만 이력은 남고 언제든 되돌릴 수 있다. 사람은 network(내 인맥)와 pool(원본 연락처) 두 층으로 나뉘며, 기본 목록은 인맥만 보여준다.

공개 범위 — sharedprivate

모든 기록은 이미 본인에게만 보이므로, visibility 는 나와 남을 가르는 장치가 아니라 데이터가 내 영역 밖으로 나가는 것을 막는 게이트다.

등급본인밖으로 (미러 · 내보내기 · 대시보드)Google 로 밀어내기
shared (기본)보임보임신규 인물 등록 시 연락처 생성
private보임절대 안 나감하지 않음

기본값은 shared 다. 딜 코멘트 · 협상 의견 · 사적 메모 · 비공개 미팅처럼 민감할 수 있는 내용은, 쓰기 도구의 설명에 심어 둔 규칙에 따라 에이전트가 저장 전에 “private 으로 할까요?”를 먼저 묻는다. 이름 · 소속 · 전화 같은 일반 정보는 묻지 않고 shared 로 간다. 딜 등록은 반대로 기본값이 private 이다.

“인물은 공유하되 메모만 비공개”로 하려면, 인물은 shared 로 등록하고 민감한 내용은 별도의 분류 항목으로 private 지정한다.

기록 내용을 이력에 남기고 싶지 않을 때는 skip_audit_payload 를 준다. 그러면 그 기록에 표식이 붙어, 이후의 동기화·보관·수정도 내용 스냅샷 없이 “누가·언제·무엇을 했다”만 남긴다. 대신 그 기록은 이력에서 되살릴 수 없다 — 비밀을 택하면 복구 경로를 포기하는 교환이다. 표식은 언제든 해제할 수 있다.

MCP 도구 (30)

https://cortex.axelabs.ai/mcp — JSON-RPC 2.0, Bearer 인증 필수.

도구동작비고
whoami인증된 사용자 확인헬스 체크
list_persons사람 목록 (본인 것만)tier(network|pool|all, 기본 network) · visibility · q 자유검색 · labels AND 교집합 · order · include(계정 케이스 | 최근 접점) · 분류 제외 필터
search_person내용 전체를 substring 으로 검색미리 정의되지 않은 필드도 매칭
get_person한 인물의 상세 + 근거 + 최근 이력 20건인물이 아닌 id 는 명시적 에러. 보관된 것도 id 로 직접 조회 가능
register_person신규 인물 등록Google 연동 사용자는 연락처도 함께 생성. visibility=private 이면 Google 로 밀지 않음
update_person인물 내용 부분 수정 + 변경 전후 이력Google 로 밀지 않음(단방향). 공개 범위 재지정도 여기서
demote_to_pool인맥 → 원본 연락처로 강등등급을 되돌리는 유일한 경로. 삭제가 아니라 재분류
classify_person인물에 분류 태그 부착키 자유 형식. (대상, 키) 멱등. 민감 분류는 private
log_interaction미팅 · 통화 · 이벤트 기록참석자마다 관계 자동 생성, 상호작용의 공개 범위를 상속
list_interactions상호작용 목록 + 인물 필터참석자는 내용과 관계 양쪽에서 해소
register_organization조직 등록멱등 — 같은 이름이면 기존 것을 돌려준다. 별칭 배열 지원
update_organization조직 내용 부분 수정등록이 기존 내용을 덮지 않으므로 정정은 이 도구로. 공개 범위는 생략 시 불변
list_organizations조직 목록이름·별칭 검색, 정렬, 소속 인원 수 · 딜 수 · 계정 케이스 포함 옵션
get_organization조직 상세 + 최근 이력 20건보관된 것도 반환
register_deal딜 등록기본 private. 단계(발굴|스크리닝|실사|심의|진행|종결|중단), 대상 조직, 담당자 연결
list_deals딜 목록단계 · 대상 조직 · 공개 범위 필터
get_deal딜 상세 + 최근 이력 20건보관된 것도 반환
register_relationship두 기록을 잇는 관계 생성멱등 — 같은 (출발, 도착, 종류). 소개 · 재직 · 참석 · 자문 등
list_relationships관계 목록인물 · 관계 종류 · 공개 범위 필터
tag_account사람·조직을 특정 건의 거래 계정으로 태깅(대상, 건) 멱등. 유형 권장 어휘: investor | client | advisor | partner | lp | vendor | portfolio | acquirer | other. 사람은 인맥으로 자동 승격
untag_account계정 태그 제거멱등. 등급 강등은 하지 않음
list_account_cases특정 건에 태깅된 사람·조직 조회건 라벨 substring(접두 조회 가능), 대상 종류 필터
list_org_directory조직 전체 인맥 목록노출 항목은 이름 · 조직 · 직함 · 라벨 · 공개 범위 · 담당자까지 — 연락처와 내용은 나오지 않는다
list_org_accounts조직 전체 계정 케이스 보유자동일한 축소 투영
network_stats네트워크 깔때기 통계scope personal(기본) | orgwide(익명 합계)
connect_googleGoogle 연동 시작 — authorize_url 반환브라우저에서 동의
disconnect_google연동 해제멱등
sync_google_now다음 주기를 기다리지 않고 즉시 1회 동기화추가·수정·라벨로 제외된 건수 반환
update_contactCortex 의 식별 필드를 Google 연락처로 되밀기연결된 연락처 근거가 있어야 함. private 은 거부. 메모·분류는 의도적으로 밀지 않음
archive_artifact기록 보관 / 복원삭제 대신. 멱등, 이력 보존

단일 조회 3종(get_person · get_organization · get_deal)의 recent_events 는 같은 모양이다 — {id, op, actor, payload_after, visibility_before, visibility_after, occurred_at} 를 최신 20건. 이력 내용을 억제한 기록은 payload_afternull 로 오는데, 이는 누락이 아니라 억제다. 공개 범위 필드는 그 변경이 없었으면 null 이며, 게이트 상태라 억제 대상이 아니다.

도구 스키마의 인자 이름은 전부 ASCII 다. 한글 키가 하나라도 들어가면 일부 LLM API 가 도구 목록 전체를 거부한다. 값은 한글 자유.

인증

Cortex 는 세 갈래 인증을 받는다. 어느 쪽이든 본인 확인 후 본인 데이터에만 접근한다는 점은 같다.

경로언제
AXE 플랫폼 SSO (기본)axe login · axe cortex — Microsoft 로그인 1회로 받은 플랫폼 토큰으로 전 서비스를 호출한다. 별도 앱 등록이나 커넥터 동의가 필요 없다
Microsoft Entra ID 직접claude.ai 커넥터, 개발용 CLI 토큰
웹 세션 쿠키읽기 전용 포털 — 8시간 슬라이딩

토큰 검증은 서명(RS256) · 발급자 · 대상 · 만료를 모두 확인한다. 인증에 실패하면 RFC 9728 형식으로 어디서 인증을 받아야 하는지 응답에 담아 돌려준다.

테넌트 축 (2026-09-05, 2~5단계 라이브 · D-ops-115): 한 cortex 가 여러 테넌트를 담는다. 요청마다 어느 테넌트인지를 먼저 정하고, 그 뒤의 모든 읽기·쓰기는 그 테넌트 안에서만 성립한다.

DB 가 보증하는 것과 코드가 지는 몫은 다르다. 세션 하나가 세운 테넌트 표식은 그 세션의 모든 질의에 행 수준 보안 정책으로 걸린다 — 즉 한 세션 = 한 테넌트이고, 질의문에 조건을 빠뜨려도 그 세션이 두 테넌트를 함께 읽는 일은 없다. 다만 어느 테넌트인지를 고르는 것은 코드다(표식을 세우는 자리가 코드다). 그래서 남는 위험은 ‘조건 누락’ 이 아니라 ‘엉뚱한 테넌트로 세션을 여는 것’ 이고, 그쪽은 표식을 세우는 자리를 한 곳(db.rs)으로 모으고 대리 쓰기·대리 읽기가 매번 DB 에 두 사람의 소속을 되묻게 해서 막는다.

들어온 경로테넌트를 무엇으로 정하나
AXE 플랫폼 SSO (axe login 토큰 · 웹의 Blueprint 로그인)토큰의 tenant_id 클레임이 필수다. 이미 등록된 사람은 그 클레임과 등록된 테넌트가 같아야 통과하고, 다르면 “다른 테넌트에 등록된 계정” 으로 거절한다. 등록이 없으면 그 클레임이 가리키는 테넌트로 첫 요청에서 편입된다
Microsoft Entra ID 직접 · 웹의 Microsoft 로그인이 토큰들은 AXE 테넌트에 핀돼 있어 테넌트를 말해 주지 않는다. 등록이 있으면 그 등록이 정답이고, 없으면 AXE 가 선언한 도메인의 주소일 때만 편입한다 — 그 밖(게스트·외부 초대 계정)은 거절하고 플랫폼 로그인으로 안내한다

자동 편입은 초대가 아니다. 편입은 토큰이 이미 증명한 테넌트에 그 사람을 매다는 것뿐이고, 테넌트 자체를 만드는 것은 요청 경로에 없다 — 운영자 명령뿐이다.

도메인 선언은 옵트인 하드닝이다. 테넌트가 --domain 으로 도메인을 선언했으면 그 목록 밖의 주소는 두 경로 모두 자동 편입되지 않는다. 선언이 하나도 없으면 플랫폼 SSO 경로는 종전대로 토큰의 클레임을 믿는다 — 그 경로는 클레임이라는 근거가 따로 있기 때문이고, 그래서 기존 테넌트의 동작이 이번 판에서 바뀌지 않는다. Microsoft 직접 경로는 도메인 말고 근거가 없으므로 선언이 비면 아무도 자동 편입되지 않는다. 고객사 계정에는 그 회사의 게스트·외주 주소가 섞여 있고 편입 즉시 조직 목록이 열리므로, 새 테넌트를 만들 때 --domain 을 채우는 것을 기본으로 삼는다.

잘못 편입됐으면 되돌린다: cortex tenant remove-owner --tenant <슬러그> --email <주소> 가 소속을 닫는다(행은 이력으로 남는다). 닫아야 그 계정을 다른 테넌트로 편입할 수 있다 — 한 사람은 활성 테넌트 하나뿐이라는 제약을 DB 가 강제하기 때문이다. 같은 테넌트로 되돌리는 것은 add-owner 가 한다.

테넌트 등록은 명령 두 줄이다. cortex tenant add --slug <슬러그> --display-name <표시명> --domain <도메인> 으로 테넌트를 만들고, 필요하면 cortex tenant add-owner --tenant <슬러그> --email <주소> 로 사람을 미리 매단다(둘 다 멱등). 앞 명령은 스키마 검사를 선행 가드로 걸어 마이그레이션이 아직 안 돈 DB 에는 두 번째 테넌트를 만들지 않는다 — 그 상태로 만들면 새 테넌트의 첫 행이 옛 기본값(AXE)으로 찍히고, 그 순간 그 행의 귀속은 되돌릴 수 없다.

조직 전역 화면도 이제 테넌트 안이다. 전사 통계 · 조직 인맥 목록 · 조직 계정 목록 · LP 보드 넷은 종전에 보안 정책을 우회해서 읽었는데, 이제 “이 테넌트 전체” 를 뜻하는 표식을 세운 뒤 정책을 통과해서 읽는다. 우회 경로 자체가 사라졌다는 뜻이다. 그 표식은 읽기 정책에만 달려 있어 그 세션은 테넌트 전체를 읽기만 한다 — 읽기와 쓰기를 한 정책으로 묶으면 넓힌 읽기 범위가 삭제 범위까지 같이 넓힌다.

아직 인스턴스 전역인 것: LP 모듈 담당자 · 교차 소유자 쓰기 허용목록 · Google 동기화의 라벨 필터 셋은 프로세스 전역 설정으로 남아 있다. 앞의 둘은 테넌트 경계가 한 겹 더 막는다 — LP 보드는 담당자가 요청자와 같은 테넌트일 때만 켜지고, 교차 소유자 쓰기는 두 사람이 같은 테넌트일 때만 성립한다. 라벨 필터만 전 테넌트 공통이라, 두 테넌트가 같은 라벨명을 서로 다른 뜻으로 쓰면 동기화 범위가 어긋난다(경계가 새는 것이 아니라 범위가 틀리는 것이다).

배포와 롤백: 스키마는 앞으로만 간다 — 되돌리는 서브커맨드가 없다. 그래서 코드 롤백은 직전 한 판까지만 안전하다(이 스키마 위에 이번 판 이전 코드를 올리지 않는다). 그리고 배포 순서상 새 코드가 잠깐 옛 스키마 위에서 뜨므로, 부팅의 스키마 검사는 실패해도 멈추지 않고 경고만 남기고, cortex migrate 안의 같은 검사는 실패로 배포를 세운다 — 세울 자리에서만 세운다.

그 창(새 코드 + 옛 스키마)에서 조직 전역 화면 넷은 오류가 아니라 빈 목록이 된다. 새 코드는 “이 테넌트 전체” 표식으로 읽는데 옛 정책은 그 표식을 모르기 때문이고, 마이그레이션이 실패하면 그 상태가 고착된다. 화면의 “데이터 없음” 이 데이터 없음이 아닐 수 있다는 뜻이라, 부팅 검사가 옛 스키마를 보면 그 뒤 조직 전역 조회마다 경고를 로그에 남긴다. 같은 창에서 도메인 선언 표도 아직 없으므로, Microsoft 직접 경로의 미등록 계정은 “일시적 오류” 가 아니라 “미등록 계정” 으로 거절된다 — 구조적 거절을 일시적 실패로 위장하지 않는다.

claude.ai 커넥터로 붙일 때

  1. Connector URL 에 MCP 주소를 넣는다.
  2. claude.ai 가 401 응답의 안내를 따라 인증 서버 메타데이터를 조회한다.
  3. 인증 서버는 Microsoft 이고, claude.ai 는 confidential client 로 동작하므로 client id 와 client secret 입력을 요구한다. 두 값은 조직 관리자가 발급해 전달한다.
  4. Microsoft 동의 후 발급된 토큰으로 MCP 를 호출한다.

이 경로는 테넌트 관리자의 사전 동의가 선행되어야 한다. 동의가 없으면 Microsoft 로그인 자체는 성공하는데 콜백에서 실패하고, Cortex 에는 요청이 한 건도 도달하지 않는다. 동의를 받은 뒤에는 커넥터를 한 번 끊었다 다시 연결해야 새 인증이 발급된다.

Google 연동

Cortex 가 요청하는 범위는 셋이다 — 연락처(contacts, 읽기는 동기화에·쓰기는 신규 인물 등록 시 연락처 생성에 쓴다), 그리고 연동된 계정이 어느 Google 계정인지 하나를 확인하기 위한 openid + userinfo.email.

계정 이메일은 Google 의 OIDC userinfo 엔드포인트에서 한 번 읽는다. 연락처 API 를 쓰지 않으므로 연락처 호출 한도와 무관하다. 읽는 시점은 연결 직후의 동기화와 그 뒤의 정기 주기이고, 알아낸 값은 그 답을 받아온 그 연결(grant)이 아직 살아 있고 아직 비어 있을 때만 적힌다. 연결은 승인 시각과 연결마다 새로 찍는 고유 식별자 두 조각으로 구분하므로, 같은 초에 두 번 다시 연결해도 서로 다른 연결로 남는다 — 어느 쪽이 현재인지는 시계가 아니라 저장된 기록이 정한다. 캡처가 도는 사이에 재연결이 끼면 그 쓰기는 아무 행도 건드리지 않고 끝나고(옛 계정 이메일이 새 연결에 적히는 일이 없다) 다음 주기가 새 연결로 다시 읽는다.

읽기에 실패했을 때의 처리는 두 갈래뿐이고, 그 구분이 재시도 정책 전부다.

갈래어떤 답이 여기로 오나그다음
영구 — 다시 연결하기 전까지 끝① 권한 범위(동의 범위)가 모자란다고 스스로 지목한 응답. ② 진짜 userinfo 응답인데 계정 이메일 항목이 통째로 빠진 200 — OIDC 는 내주는 항목이 동의 범위를 따르므로, 이 범위가 없는 연결은 거절이 아니라 “빈자리” 로 답한다(계정에 이메일이 없는 경우도 같다)경고 한 줄을 한 번만 남기고 그 연결로는 더 시도하지 않는다. 재동의 전까지 답이 안 바뀌기 때문이다 — connect_google 로 다시 연결해야 채워진다
일시 — 그 밖 전부네트워크·타임아웃, 5xx, 범위를 지목하지 않은 401/403(만료된 토큰 · 시계 어긋남 · 호출 한도), 그리고 userinfo 응답으로 읽을 수조차 없는 2xx 본문(중간 경로가 끼워 넣은 오류 안내 화면 · 잘린 응답)연결마다 따로 잡은 예산 안에서 다시 시도하고, 예산을 다 쓰면 30분 쉬었다가(실패가 이어지면 2배씩, 최대 하루) 같은 연결로 다시 읽는다. 포기가 아니다

일시 갈래의 재시도는 정기 동기화가 돌 때마다 한 번씩이라(기본 10분 간격) 몇 번의 시도가 한꺼번에 몰리지 않는다. 동기화를 수동으로 부르거나 다시 연결해 곧바로 한 번 돌리는 경로도 같은 예산을 함께 쓰므로, 그렇게 연달아 치면 예산이 그만큼 빨리 소진된다. 한 연결에 대해 캡처는 한 번에 하나만 돈다 — 정기 주기 · 수동 동기화 · 재연결 직후의 즉시 동기화가 겹쳐도 Google 을 겹쳐 부르지 않는다. 값을 알아냈는데 기록이 실패했다면 그것은 성공이 아니다: 예산이 되돌아가지 않고 일시 실패로 처리돼 다음 기회에 다시 시도한다.

이미 연결해 둔 사용자는 이 값이 비어 있다. 옛 동의에 이 범위가 없어서 그런 것이고 오류가 아니다 — 다만 저절로 채워지지도 않는다. 범위가 바뀌면 재동의가 필요하므로 connect_google 로 한 번 다시 연결해야 한다. 그리고 그 동의 화면이 이 범위를 내주려면 관리자가 Google Cloud Console 의 동의 화면(Data Access) 설정에 openiduserinfo.email 을 미리 등록해 두어야 한다.

연동 계정이 계속 비어 있다면 셋 중 하나다.

  1. 옛 연결이라 이 범위가 없다 — 위 표의 영구 갈래다. 기다려도 낫지 않고 재시도도 하지 않는다. connect_google 로 다시 연결(재동의)하는 것이 유일한 답이다.
  2. 관리자가 동의 화면에 범위를 등록하지 않았다 — 이 상태로 다시 연결하면 새 연결에도 범위가 없어 1번으로 되돌아온다. 등록이 먼저고, 등록한 뒤 한 번 더 재연결해야 한다.
  3. 일시적인 통신·서버 사정 — 이건 시간이 답이다. 쉬었다 다시 시도하는 과정을 필요한 만큼 되풀이하므로, 일시적인 오류 때문에 이 값이 영영 비어 있게 되지는 않는다.

다른 계정으로 다시 연결하면 이 값은 비워지고 새 계정 기준으로 다시 읽는다 — 옛 계정 이메일이 새 연결에 남지 않는다. 연동을 해제했거나 연결이 만료된 뒤에 뒤늦게 도착한 답도 마찬가지로 기록하지 않는다 — 이미 끊긴 연동에 계정 이메일이 적혀 남는 일이 없다.

재발급 토큰은 암호화해 사용자별로 저장하며, 연동 해제 시 Google 쪽에서도 취소한다.

스키마와 배포

이번 판에 스키마가 두 자리 늘었다. 둘 다 기존 데이터를 옮기거나 지우지 않는다.

마이그레이션무엇
0010기록 표의 계정 케이스 조회에 붙는 표현식 확장통계 (인덱스가 아니다)느렸던 원인이 스캔 경로가 아니라 행수 추정 붕괴였다. 플래너가 바깥쪽을 1행으로 보고 안쪽 표를 실제 행수만큼 되훑었고, 인덱스를 붙여 봐야 이 경로(행 수준 보안 아래)에서는 조건으로 승격되지 못해 무효였다. 통계를 붙여 추정이 실제와 맞자 같은 조회의 실행 시간이 자릿수 단위로 줄었다
0011연동 토큰 행의 연결 고유 식별자 한 칸연결의 정체를 승인 시각만으로 가르면 같은 시각을 받은 두 재연결이 구분되지 않는다. 그러면 앞 연결의 포기 표시가 새 연결을 잠그거나, 옛 계정 이메일이 새 연결의 기록 조건을 통과한다

마이그레이션은 컨테이너가 뜰 때 자동으로 돌지 않는다. 배포가 새 이미지를 올린 직후, 헬스 판정 이전에 그 이미지의 cortex migrate 를 한 번 실행한다 — 새 코드가 옛 스키마 위에서 뜨는 창을 없애기 위해서다. 저장소 README 와 운영자 설치 문서에 나오는 cortex migrate로컬·개발 경로이고, 배포에서 사람이 손으로 다시 칠 일이 아니다.

돌리는 중에 잃는 것은 없다. 0011 은 이미 있던 행마다 새 식별자를 하나씩 채우므로, 마이그레이션 순간에 돌고 있던 계정 이메일 캡처는 조건이 안 맞아 아무 행도 안 건드리고 끝나고 다음 주기가 새 기준으로 다시 읽는다 — 값이 한 주기 늦게 채워질 뿐 틀린 값이 남지 않는다. 새 환경 변수도, 새 비밀도 없다.

관련 서비스

  • Blueprint — Cortex 의 기록을 본인 범위 그대로 미러해 업무 대시보드와 메모 인용에 활용한다.
  • Frame — 딜 기록이 회계·법인 도메인의 식별자를 참조 필드로 들고 있다.