This is the full developer documentation for Signox # 자주 묻는 질문 > 자주 묻는 질문 ## 어떤 클라이언트를 사용하나요? [섹션 제목: “어떤 클라이언트를 사용하나요?”](#어떤-클라이언트를-사용하나요) Node·Java·C#·Python의 ActivationClient를 사용하세요. 고객 승인과 자동·수동 응답 전달을 연결해요. 기존 OnlineClient·OfflineClient·SignoxClient는 호환성을 위해 남아 있지만 신규 연동의 시작점은 아니에요. ## 기기를 여러 개 등록할 수 있나요? [섹션 제목: “기기를 여러 개 등록할 수 있나요?”](#기기를-여러-개-등록할-수-있나요) 라이센스 하나에는 활성 등록 하나만 허용돼요. 다른 기기는 이전 절차가 필요하며 기기 수는 정책에서 조절하지 않아요. ## 포맷하면 반납을 증명할 수 있나요? [섹션 제목: “포맷하면 반납을 증명할 수 있나요?”](#포맷하면-반납을-증명할-수-있나요) 아니요. 같은 보호된 하드웨어라면 등록을 복구하고, 복구할 수 없다면 벤더에게 이전 검토를 요청해요. 분리된 백업이 계속 동작할 수 있으므로 원격 초기화를 보호된 반납이라고 설명하지 않아요. ## 장치 종류를 직접 선택하나요? [섹션 제목: “장치 종류를 직접 선택하나요?”](#장치-종류를-직접-선택하나요) SDK가 지원 여부를 감지해요. 현재 보호 기능의 실행 환경과 신뢰 등록 요건은 SDK 가이드에 있어요. USB 토큰 공급자는 아직 구현되지 않았어요. ## SDK 패키지를 찾지 못해요 [섹션 제목: “SDK 패키지를 찾지 못해요”](#sdk-패키지를-찾지-못해요) 0.3.1 개발 SDK와 대응하는 API를 사용하세요. 네 언어 모두 설정된 개발 패키지 저장소가 필요해요. 공개 저장소 출시는 별도예요. [설치 안내](/start/install/) · [검증 안내](/guides/integration-checks/) # 다른 기기를 통해 활성화 > 요청 파일을 고객 포털에 전달하고 원래 기기에서 활성화 응답을 적용해요 인터넷에 연결되지 않은 앱은 요청 파일과 활성화 응답을 다른 PC로 옮겨 인증할 수 있어요. 고객 계정과 라이센스는 브라우저로 활성화할 때와 같고, 요청과 응답을 전달하는 방법만 달라요. 활성화 응답은 승인한 기기에서만 열 수 있도록 보호돼요. 먼저 앱이 실행될 환경에서 기기 보호 기능을 사용할 수 있는지 확인하세요. ## 실행 환경 준비하기 [섹션 제목: “실행 환경 준비하기”](#실행-환경-준비하기) SDK는 OS에 맞는 실행 모듈을 선택해요. 일반 앱 코드에서 장치 공급자 이름이나 키 정보를 직접 조립하지 마세요. | 환경 | 실행 모듈·필요 조건 | 현재 검증 범위 | | ------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | | Linux | Python 3, tpm2-tools, TPM 2.0 리소스 매니저와 기기 접근 권한 | Linux 실물 장치와 시뮬레이터에서 파일 교환·서명 검증을 확인했어요. | | Windows | Windows 10/11, Windows PowerShell 5.1, 준비된 TPM 2.0과 Microsoft Platform Crypto Provider | Windows 러너에서 플랫폼 키 생성·재사용·서명·복호화·잘못된 대상 거부를 확인했어요. | | macOS | macOS 11+, Secure Enclave 지원 기기, 보호 키 핸들을 보존할 개인 상태 디렉터리 | Apple Silicon Mac에서 보호 키 연산을 확인했어요. arm64·x86\_64 바이너리를 빌드하며 Intel 실장비 검증은 별도예요. | Mac SDK 0.3.1 패키지에는 범용 네이티브 실행 파일이 포함되어 일반 실행에 Swift·Xcode가 필요하지 않아요. 상태 디렉터리에 저장하는 것은 비밀키 평문이 아니라 Secure Enclave가 감싼 기기 전용 핸들이에요. 일반적인 이동 가능한 개인키처럼 사용하지 말고 원래 기기에서 보존하세요. 외부 앱 배포에서는 공급자의 앱에 맞게 실행 파일 서명·공증을 적용하고 최종 배포물을 검증하세요. 소프트웨어 RSA/ECDH 테스트가 성공해도 실제 OS의 보호 저장소에서 동작한다고 확인된 것은 아니에요. 보호 기능이 없거나 접근할 수 없는 기기에서는 온라인 활성화와 통신 유예를 사용해요. USB 토큰 모듈은 아직 제공하지 않아요. ### 운영자의 기기 신뢰 등록 [섹션 제목: “운영자의 기기 신뢰 등록”](#운영자의-기기-신뢰-등록) 보호된 응답을 발급하려면 해당 기능을 제공하는 요금제와 기기 키의 신뢰 등록이 필요해요. 운영자는 실제 기기에서 공식 실행 모듈이 보호 저장소를 사용해 생성한 공개키를 별도 신뢰 경로로 대조한 뒤, 운영자 보안 화면의 **기기 보호 키 신뢰 등록**에서 등록하세요. 고객이 보내온 요청의 `provider` 이름만으로 하드웨어 키임을 판단하지 마세요. 제조사 인증서 검증은 자동으로 제공하지 않아요. 앱 사용자에게 장치 종류를 고르게 하는 대신, 공급자가 지원 환경과 신뢰 등록을 준비해야 해요. ## 1. 원래 앱에서 요청 파일을 만드세요 [섹션 제목: “1. 원래 앱에서 요청 파일을 만드세요”](#1-원래-앱에서-요청-파일을-만드세요) 사용하는 언어의 [샘플](/samples/node/)을 설치한 뒤 `activation-request request.signox`를 실행하세요. 샘플이 SDK의 `createRequest()` 또는 언어별 대응 메서드로 공개 요청을 만들고 파일로 저장해요. 요청 파일에는 승인할 제품과 기기의 공개 정보가 들어 있어요. 요청 자체는 이용권이 아니에요. 결과를 받기 위한 비밀과 설치 자격증명은 앱의 개인 상태 디렉터리에 남겨 두세요. 정상 결과는 `request.signox` 파일이 생성되는 거예요. 같은 앱에서 명령을 반복하면 기존 요청을 재사용해요. 요청의 7일 유효기간이 지났다면 샘플의 `activation-renew-request request.signox`로 새 요청을 만드세요. ## 2. 연결된 PC의 고객 포털에 요청을 가져오세요 [섹션 제목: “2. 연결된 PC의 고객 포털에 요청을 가져오세요”](#2-연결된-pc의-고객-포털에-요청을-가져오세요) 고객 포털 `/activate`를 열고 **요청 파일**을 선택하거나 **요청 코드**를 붙여넣으세요. 포털이 요청의 제품을 조회한 뒤 해당 앱의 이름과 로고를 보여줘요. 앱 이름이 맞는지 확인하고 고객 계정으로 로그인하세요. 벤더 대시보드 계정은 고객 계정과 달라요. 소유한 라이센스가 없다면 구매 키를 먼저 등록하세요. ## 3. 라이센스를 선택하고 응답을 받으세요 [섹션 제목: “3. 라이센스를 선택하고 응답을 받으세요”](#3-라이센스를-선택하고-응답을-받으세요) 사용할 라이센스와 기기 이름을 확인하고 **이 기기 활성화**를 누르세요. 확인창에서 승인하면 응답이 발급되고 이 기기에 등록이 예약돼요. * 이미 다른 기기에 등록되어 있으면 **기기 이전 요청**으로 안내해요. 사유를 입력하면 벤더가 해당 목적지 요청을 검토해요. * 신뢰 등록이 필요하다고 나오면 공급자가 실제 기기의 공개키를 대조·등록한 뒤 다시 승인해야 해요. * 만료·정지·폐기된 라이센스는 연결 장애로 간주하지 않아요. 이용권 상태부터 해결해야 해요. **활성화 파일 저장**으로 `activation.signox`를 내려받으세요. 발급됨은 실제 기기에 적용됐다는 의미가 아니에요. ## 4. 원래 기기에 응답을 적용하세요 [섹션 제목: “4. 원래 기기에 응답을 적용하세요”](#4-원래-기기에-응답을-적용하세요) `activation.signox`를 원래 기기로 옮기고 샘플의 `activation-apply activation.signox`를 실행하세요. SDK는 응답 서명·제품·요청·기기 일치를 확인하고, 기기가 보관한 키로 응답을 연 뒤 기간과 기능 권한을 검증해요. 요청과 응답을 한 번씩 옮기면 돼요. 앱 개발자가 중간 challenge/proof 파일을 교환하는 구형 절차는 사용하지 않아요. 정상이라면 `VALID` 또는 `IN_GRACE_PERIOD`와 `valid=true`가 나와요. 실제 기능을 실행할 때는 필요한 기능값도 확인하세요. `ACTIVATION_RESPONSE_INVALID`가 나오면 응답이 해당 요청·제품·기기의 것인지 확인하고 파일을 수정하지 않은 상태로 다시 적용하세요. ## 적용 확인과 기기 이전 이해하기 [섹션 제목: “적용 확인과 기기 이전 이해하기”](#적용-확인과-기기-이전-이해하기) 연결된 앱은 적용 확인을 서버로 보낼 수 있어요. 연결되지 않은 앱의 적용 여부는 포털에서 자동 확인할 수 없으므로 발급 상태로 남을 수 있어요. 같은 보호 키가 남아 있으면 같은 기기의 등록을 복구할 수 있어요. Windows의 키 저장소나 Mac의 기기 전용 보호 키 핸들까지 지워지면 기기 이름이 같더라도 기존 키를 복구했다고 판단하지 않아요. 이 경우에는 새 요청으로 이전 검토를 받아야 해요. 완전 오프라인 기기에는 원격 정지·폐기 신호를 즉시 보낼 수 없어요. 파일 삭제나 포맷도 옛 백업이 중단됐다는 증명이 아니에요. 벤더의 예외 이전 승인은 이 불확실성을 확인하는 절차이며 물리적인 중복 사용이 전혀 없음을 증명하지 않아요. [기기 식별과 복구](/guides/hwid/) · [기능 허용·거부 검증](/guides/integration-checks/) # 고객 포털에서 활성화 > 앱에서 고객 로그인과 라이센스 선택을 연결하고 기기를 승인받아요 고객이 앱에서 라이센스 활성화를 시작하면 해당 앱의 고객 포털로 이동해 기기를 승인할 수 있어요. 앱은 고객 비밀번호를 보관하지 않고, SDK가 승인 결과를 받아 권한을 확인해요. 이 가이드는 브라우저 승인, 앱의 결과 수신과 다른 기기로 이전하는 흐름을 설명해요. 코드를 먼저 실행하려면 [언어별 SDK 가이드](/sdk/node/)를 사용하세요. ## 1. 앱에서 활성화 요청을 만드세요 [섹션 제목: “1. 앱에서 활성화 요청을 만드세요”](#1-앱에서-활성화-요청을-만드세요) `ActivationClient.start()` 또는 언어별 대응 메서드를 호출하세요. SDK는 설치 정보를 담은 요청을 서버에 등록하고 `portalUrl`과 `requestId`를 반환해요. `portalUrl`을 시스템 브라우저로 여세요. 고객 로그인 화면은 요청의 제품을 서버에서 조회해 이름과 로고를 표시해요. 요청 번호는 공개 정보지만 결과 수신 비밀과 설치 자격증명은 앱의 개인 상태 디렉터리에 보관해요. 요청 등록에 실패하면 API 주소와 연결 상태를 확인하세요. 포털 주소를 API 주소에 넣거나 매번 상태 디렉터리를 바꾸면 정상적인 설치를 이어갈 수 없어요. ## 2. 고객이 사용할 라이센스를 선택하게 하세요 [섹션 제목: “2. 고객이 사용할 라이센스를 선택하게 하세요”](#2-고객이-사용할-라이센스를-선택하게-하세요) 고객은 포털에서 자신의 계정으로 로그인해요. 벤더·운영자 계정은 고객 승인에 사용할 수 없어요. 해당 앱에 소유한 라이센스가 없다면 구매 키를 등록한 뒤 다시 선택하세요. 포털에서 라이센스와 기기 이름을 확인하고 **이 기기 활성화**를 누르세요. 확인창에서 승인하면 서버가 이 기기에 등록을 예약하고 활성화 응답을 발급해요. 라이센스당 활성 등록은 한 개예요. 이미 다른 기기가 등록되어 있으면 자동으로 등록을 비우지 않아요. 포털이 이전 요청을 안내하며, 아래의 **다른 기기로 이전하기** 절차를 따릅니다. ## 3. 앱에서 승인 결과를 받아 적용하세요 [섹션 제목: “3. 앱에서 승인 결과를 받아 적용하세요”](#3-앱에서-승인-결과를-받아-적용하세요) 앱은 `poll()` 또는 언어별 대응 메서드를 3초 이상의 간격으로 호출해요. 결과에 따라 다음 행동을 구분하세요. | 상태 | 의미 | 앱의 동작 | | ----------------------- | --------------------------- | -------------------------------------- | | `pending` | 아직 고객 승인이나 벤더 검토가 끝나지 않았어요. | 승인 화면으로 돌아갈 수 있게 안내하고 기다리세요. | | `applied`와 유효한 `result` | 서명된 응답을 받아 기기에서 적용·검증했어요. | 현재 기능 권한을 확인하세요. | | `application_failed` | 응답은 받았지만 기기에서 적용 검증에 실패했어요. | `result.code`에 맞는 오류를 보여주고 기능을 열지 마세요. | 포털의 발급됨과 기기의 적용 확인됨은 별도예요. 연결된 SDK의 적용 확인을 받으면 포털 상태도 바뀌어요. 응답 발급만으로 실제 사용을 시작했다고 판단하지 마세요. ## 4. 실행 중에도 권한을 확인하세요 [섹션 제목: “4. 실행 중에도 권한을 확인하세요”](#4-실행-중에도-권한을-확인하세요) 앱의 기능을 실행하기 직전과 실행 중 적절한 주기에 `validate()`를 호출하세요. `valid=true`와 필요한 기능값을 함께 확인해야 해요. 예를 들어 CSV 내보내기는 `demo_export=true`가 필요해요. 연결 기반 등록은 서버가 서명한 캐시와 통신 유예를 사용할 수 있어요. 통신 장애 유예는 마지막 정상 검증부터 계산하고, 라이센스 만료 유예는 이용권의 만료일부터 계산해요. 첫 연결에 성공한 적이 없는 설치에는 통신 유예를 적용하지 않아요. 정지·폐기·만료·서명 오류는 통신 장애가 아니에요. 명시적 거절을 받은 뒤 네트워크가 끊겨도 이전 허용 결과로 덮지 마세요. ## 같은 기기에서 복구하기 [섹션 제목: “같은 기기에서 복구하기”](#같은-기기에서-복구하기) 상태 디렉터리와 보호 키가 남아 있다면 같은 기기의 요청을 다시 사용하거나 새 요청으로 복구할 수 있어요. Linux의 신뢰 하드웨어 식별, Windows의 플랫폼 키 저장소, Mac의 기기 전용 보호 키 핸들은 보존 방식이 달라요. 자세한 조건은 [기기 식별과 복구](/guides/hwid/)를 확인하세요. 기기 이름이나 새로운 소프트웨어 설치 ID만으로 원래 장비라고 증명하지는 않아요. 키를 복구할 수 없다면 이전 요청이 필요해요. ## 다른 기기로 이전하기 [섹션 제목: “다른 기기로 이전하기”](#다른-기기로-이전하기) 1. 새 기기의 앱에서 활성화 요청을 만들어요. 2. 고객 포털에서 기존 라이센스를 선택하고 이전 사유를 입력해요. 3. 벤더는 제품의 **기기 이전** 탭에서 사유와 기존 등록을 확인해요. 4. 기존 오프라인 이용권·캐시가 남을 수 있음을 확인하고 검토 사유와 함께 승인하거나 거절해요. 5. 승인되면 해당 요청의 새 기기에만 응답을 발급해요. 앱은 polling 또는 파일 가져오기로 적용해요. 승인 대기 중 원래 등록이 바뀌면 오래된 요청을 그대로 승인하지 않아요. 변경된 상황에 맞는 새 요청으로 다시 검토하세요. 요청·결정·발급·적용 확인은 감사기록으로 남아요. 이전 승인은 기존 오프라인 기기의 사용 종료 증명이 아니에요. 완전히 연결되지 않은 기기에 발급된 이용권은 즉시 원격 무효화할 수 없어요. [파일을 옮겨 활성화하기](/guides/activation-offline/) · [실제 기능 허용·거부 검증하기](/guides/integration-checks/) # 기기 식별과 복구 > 기기 식별과 복구 ActivationClient는 개인 상태 디렉터리를 보관해요. CPU·디스크·MAC 값을 개발자가 수집하지 않아도 돼요. SDK가 설치 자격증명을 만들고 지원되는 기기 보호 기능을 내부에서 감지해요. | 상황 | 처리와 보장 범위 | | ------------------ | -------------------------------------------------- | | 재시작·앱 업데이트 | 같은 상태 디렉터리를 사용해요. 새 등록을 만들지 않아요. | | 컨테이너 재생성 | 설치별 개인 볼륨을 연결해요. 공통 이미지에 설치 자격증명을 넣지 마세요. | | 같은 보호 기기의 로컬 상태 유실 | 새 요청을 만들어요. 일치하는 보호된 하드웨어 식별을 통해 복구해요. | | 새 소프트웨어 ID·다른 기기 | 대상 기기가 지정된 이전을 요청하고 벤더 검토를 기다려요. | | 오프라인 파일 삭제·기기 포맷 | 삭제만으로 옛 백업의 사용 종료를 증명하지 못해요. 복구할 수 없다면 이전 검토를 거쳐요. | 현재 파일 기반 시각 기록은 전체 상태 복원을 막지 못해요. 소프트웨어 자격증명은 복사될 수 있고, 복제 가능한 가상 보안 기기는 실물 하드웨어와 같은 보장을 제공하지 않아요. 완전한 변조 방지로 설명하지 마세요. [이전 흐름](/guides/activation-online/) · [수동 응답 적용](/guides/activation-offline/) # 통합 검증 실행 절차 > 통합 검증 실행 절차 설치한 SDK와 실제 샘플 기능으로 검증하세요. 먼저 언어별 샘플을 읽고 실제 발급받은 테스트 정보를 준비하세요. 예제 값을 실제 고객 자격증명으로 사용하지 마세요. [Node](/samples/node/) · [Java](/samples/java/) · [C#](/samples/csharp/) · [Python](/samples/python/) | 검증 항목 | 기대 결과 | | -------------- | -------------------------------------------------------- | | 고객 활성화 | start → 고객 로그인·승인 → poll → 유효한 결과 | | 수동 전달 | request → 포털 가져오기 → activation.signox → 같은 보호 기기에서 apply | | CSV 권한 | valid와 demo\_export가 참일 때만 2행을 생성해요. 기능이 없으면 종료 코드 3이에요. | | 변조 | 다른 제품·요청·서명·기기는 앱 권한을 열 수 없어요. | | 요청 반복 | 같은 요청은 멱등 처리되며 두 번째 활성 등록이 생기지 않아요. | | 기기 교체 | 다른 기기는 그 요청에 연결된 벤더 승인이 필요해요. | | 명시적 거절 후 연결 끊기 | 보호된 클라이언트가 서명된 거절을 보존하고 옛 권한으로 우회하지 않아요. | | 앱 브랜딩 | 로그인·가입·재설정·서로 다른 요청 탭에서 서버의 앱 문맥을 유지해요. | 명령·종료 코드·예상 값·관찰 값·로컬 증거 경로를 기록하세요. 승인 대기·하드웨어 미지원·미실행은 pending/not\_run이며 통과가 아니에요. 라이센스 키·토큰·개인 상태·복호화된 증명을 보고서에 넣지 마세요. 소프트웨어 검증을 모두 통과해도 물리적인 오프라인 회수와 상태 복원 한계는 남아요. [AI 작업 프롬프트](/start/quickstart-ai/) # C# 샘플 > 독립된 C# 프로젝트로 라이센스에 따른 CSV 생성과 거절을 확인해요 설치한 SDK로 실제 CSV 파일 생성을 보호하는 샘플이에요. 브라우저와 파일 전달이 동일한 고객 승인과 등록을 사용해요. [이 언어 샘플 다운로드](/downloads/samples/signox-csharp-sample.zip) ## 설치하고 설정하기 [섹션 제목: “설치하고 설정하기”](#설치하고-설정하기) 소스 압축파일을 푼 디렉터리 안에서 실행하세요. 언어별 가이드에 따라 개발 SDK를 설치해요. 네 SDK 모두 패키지 의존성으로 설치해요. [SDK](/sdk/csharp/) ```bash dotnet restore ``` env.example을 참고해 SIGNOX\_API\_URL·SIGNOX\_PRODUCT\_ID·SIGNOX\_PUBLIC\_KEY\_FILE·SIGNOX\_STATE\_DIR를 설정하세요. 제품 UUID와 공개키는 벤더의 제품 설정에서 받아요. 고객은 포털에서만 라이센스 키를 등록해요. 개인 상태와 계정 자격증명은 커밋하지 마세요. ## 샘플 실행하기 [섹션 제목: “샘플 실행하기”](#샘플-실행하기) env.example의 네 변수를 실행할 셸에 설정하고 SIGNOX\_PUBLIC\_KEY\_FILE 위치에 제품 공개키를 저장하세요. start가 출력한 고객 포털에서 승인한 뒤 poll과 export를 실행하세요. ```bash dotnet run -- activation-start dotnet run -- activation-poll dotnet run -- activation-export output.csv ``` 연결되지 않은 기기에서는 아래 두 파일을 PC 사이에 옮기세요: ```bash dotnet run -- activation-request request.signox # Import request.signox in the customer portal and save activation.signox. dotnet run -- activation-apply activation.signox ``` | Command | 기대 동작 | | ------------------------------------ | ----------------------------------------------------- | | `activation-start` | 앱 브랜드가 유지되는 고객 포털 주소를 출력해요. 브라우저로 여세요. | | `activation-poll` | 승인 전에는 pending, 승인 후에는 응답 적용과 권한 검증 결과를 출력해요. | | `activation-request` | 다른 PC로 전달할 공개 요청 JSON을 출력해요. | | `activation-renew-request` | 로컬 설치 식별을 유지한 채 만료된 요청을 새 요청으로 교체해요. | | `activation-apply activation.signox` | 고객 포털에서 받은 응답의 서명과 기기를 검증하고 적용해요. | | `activation-status` | 현재 권한을 확인해요. valid와 demo\_export가 모두 참이어야 내보내기를 허용해요. | | `activation-export output.csv` | 허용되면 센서 예제 2행을 실제 파일로 써요. 기능이 없으면 종료 코드 3이에요. | | `activation-deactivate` | 연결 기반 등록을 해제해요. 보호된 이용권은 이전 검토가 필요해요. | 종료 코드 0은 명령 실행 성공이며 승인 대기 pending도 포함해요. 반드시 활성화 완료라는 뜻은 아니에요. 종료 코드 2는 검증·활성화 실패예요. 대기·미실행을 검증 통과로 기록하지 마세요. 요청 생성 → 고객 로그인 → 구매 키 등록 → 라이센스 선택 → 승인 → poll/apply 순서로 실행하세요. 별도 상태 디렉터리에서 요청을 만들면 다른 기기 이전 검토로 안내돼야 해요. 명령·종료 코드·예상/실제 결과·증거 경로를 남기세요. 기존 방식의 온라인·오프라인 전용 명령은 호환성 검증용으로 남아 있어요. 신규 연동은 위 activation 명령으로 시작하세요. # Java 샘플 > 독립된 Java 프로젝트로 라이센스에 따른 CSV 생성과 거절을 확인해요 설치한 SDK로 실제 CSV 파일 생성을 보호하는 샘플이에요. 브라우저와 파일 전달이 동일한 고객 승인과 등록을 사용해요. [이 언어 샘플 다운로드](/downloads/samples/signox-java-sample.zip) ## 설치하고 설정하기 [섹션 제목: “설치하고 설정하기”](#설치하고-설정하기) 소스 압축파일을 푼 디렉터리 안에서 실행하세요. 언어별 가이드에 따라 개발 SDK를 설치해요. 네 SDK 모두 패키지 의존성으로 설치해요. [SDK](/sdk/java/) ```bash mvn -U compile ``` env.example을 참고해 SIGNOX\_API\_URL·SIGNOX\_PRODUCT\_ID·SIGNOX\_PUBLIC\_KEY\_FILE·SIGNOX\_STATE\_DIR를 설정하세요. 제품 UUID와 공개키는 벤더의 제품 설정에서 받아요. 고객은 포털에서만 라이센스 키를 등록해요. 개인 상태와 계정 자격증명은 커밋하지 마세요. ## 샘플 실행하기 [섹션 제목: “샘플 실행하기”](#샘플-실행하기) env.example의 네 변수를 실행할 셸에 설정하고 SIGNOX\_PUBLIC\_KEY\_FILE 위치에 제품 공개키를 저장하세요. start가 출력한 고객 포털에서 승인한 뒤 poll과 export를 실행하세요. ```bash mvn exec:java -Dexec.mainClass=kr.signox.samples.Main -Dexec.args="activation-start" mvn exec:java -Dexec.mainClass=kr.signox.samples.Main -Dexec.args="activation-poll" mvn exec:java -Dexec.mainClass=kr.signox.samples.Main -Dexec.args="activation-export output.csv" ``` 연결되지 않은 기기에서는 아래 두 파일을 PC 사이에 옮기세요: ```bash mvn exec:java -Dexec.mainClass=kr.signox.samples.Main -Dexec.args="activation-request request.signox" # Import request.signox in the customer portal and save activation.signox. mvn exec:java -Dexec.mainClass=kr.signox.samples.Main -Dexec.args="activation-apply activation.signox" ``` | Command | 기대 동작 | | ------------------------------------ | ----------------------------------------------------- | | `activation-start` | 앱 브랜드가 유지되는 고객 포털 주소를 출력해요. 브라우저로 여세요. | | `activation-poll` | 승인 전에는 pending, 승인 후에는 응답 적용과 권한 검증 결과를 출력해요. | | `activation-request` | 다른 PC로 전달할 공개 요청 JSON을 출력해요. | | `activation-renew-request` | 로컬 설치 식별을 유지한 채 만료된 요청을 새 요청으로 교체해요. | | `activation-apply activation.signox` | 고객 포털에서 받은 응답의 서명과 기기를 검증하고 적용해요. | | `activation-status` | 현재 권한을 확인해요. valid와 demo\_export가 모두 참이어야 내보내기를 허용해요. | | `activation-export output.csv` | 허용되면 센서 예제 2행을 실제 파일로 써요. 기능이 없으면 종료 코드 3이에요. | | `activation-deactivate` | 연결 기반 등록을 해제해요. 보호된 이용권은 이전 검토가 필요해요. | 종료 코드 0은 명령 실행 성공이며 승인 대기 pending도 포함해요. 반드시 활성화 완료라는 뜻은 아니에요. 종료 코드 2는 검증·활성화 실패예요. 대기·미실행을 검증 통과로 기록하지 마세요. 요청 생성 → 고객 로그인 → 구매 키 등록 → 라이센스 선택 → 승인 → poll/apply 순서로 실행하세요. 별도 상태 디렉터리에서 요청을 만들면 다른 기기 이전 검토로 안내돼야 해요. 명령·종료 코드·예상/실제 결과·증거 경로를 남기세요. 기존 방식의 온라인·오프라인 전용 명령은 호환성 검증용으로 남아 있어요. 신규 연동은 위 activation 명령으로 시작하세요. # Node 샘플 > 독립된 Node 프로젝트로 라이센스에 따른 CSV 생성과 거절을 확인해요 설치한 SDK로 실제 CSV 파일 생성을 보호하는 샘플이에요. 브라우저와 파일 전달이 동일한 고객 승인과 등록을 사용해요. [이 언어 샘플 다운로드](/downloads/samples/signox-node-sample.zip) ## 설치하고 설정하기 [섹션 제목: “설치하고 설정하기”](#설치하고-설정하기) 소스 압축파일을 푼 디렉터리 안에서 실행하세요. 언어별 가이드에 따라 개발 SDK를 설치해요. 네 SDK 모두 패키지 의존성으로 설치해요. [SDK](/sdk/node/) ```bash npm install ``` env.example을 참고해 SIGNOX\_API\_URL·SIGNOX\_PRODUCT\_ID·SIGNOX\_PUBLIC\_KEY\_FILE·SIGNOX\_STATE\_DIR를 설정하세요. 제품 UUID와 공개키는 벤더의 제품 설정에서 받아요. 고객은 포털에서만 라이센스 키를 등록해요. 개인 상태와 계정 자격증명은 커밋하지 마세요. ## 샘플 실행하기 [섹션 제목: “샘플 실행하기”](#샘플-실행하기) env.example의 네 변수를 실행할 셸에 설정하고 SIGNOX\_PUBLIC\_KEY\_FILE 위치에 제품 공개키를 저장하세요. start가 출력한 고객 포털에서 승인한 뒤 poll과 export를 실행하세요. ```bash node main.mjs activation-start node main.mjs activation-poll node main.mjs activation-export output.csv ``` 연결되지 않은 기기에서는 아래 두 파일을 PC 사이에 옮기세요: ```bash node main.mjs activation-request request.signox # Import request.signox in the customer portal and save activation.signox. node main.mjs activation-apply activation.signox ``` | Command | 기대 동작 | | ------------------------------------ | ----------------------------------------------------- | | `activation-start` | 앱 브랜드가 유지되는 고객 포털 주소를 출력해요. 브라우저로 여세요. | | `activation-poll` | 승인 전에는 pending, 승인 후에는 응답 적용과 권한 검증 결과를 출력해요. | | `activation-request` | 다른 PC로 전달할 공개 요청 JSON을 출력해요. | | `activation-renew-request` | 로컬 설치 식별을 유지한 채 만료된 요청을 새 요청으로 교체해요. | | `activation-apply activation.signox` | 고객 포털에서 받은 응답의 서명과 기기를 검증하고 적용해요. | | `activation-status` | 현재 권한을 확인해요. valid와 demo\_export가 모두 참이어야 내보내기를 허용해요. | | `activation-export output.csv` | 허용되면 센서 예제 2행을 실제 파일로 써요. 기능이 없으면 종료 코드 3이에요. | | `activation-deactivate` | 연결 기반 등록을 해제해요. 보호된 이용권은 이전 검토가 필요해요. | 종료 코드 0은 명령 실행 성공이며 승인 대기 pending도 포함해요. 반드시 활성화 완료라는 뜻은 아니에요. 종료 코드 2는 검증·활성화 실패예요. 대기·미실행을 검증 통과로 기록하지 마세요. 요청 생성 → 고객 로그인 → 구매 키 등록 → 라이센스 선택 → 승인 → poll/apply 순서로 실행하세요. 별도 상태 디렉터리에서 요청을 만들면 다른 기기 이전 검토로 안내돼야 해요. 명령·종료 코드·예상/실제 결과·증거 경로를 남기세요. 기존 방식의 온라인·오프라인 전용 명령은 호환성 검증용으로 남아 있어요. 신규 연동은 위 activation 명령으로 시작하세요. # Python 샘플 > 독립된 Python 프로젝트로 라이센스에 따른 CSV 생성과 거절을 확인해요 설치한 SDK로 실제 CSV 파일 생성을 보호하는 샘플이에요. 브라우저와 파일 전달이 동일한 고객 승인과 등록을 사용해요. [이 언어 샘플 다운로드](/downloads/samples/signox-python-sample.zip) ## 설치하고 설정하기 [섹션 제목: “설치하고 설정하기”](#설치하고-설정하기) 소스 압축파일을 푼 디렉터리 안에서 실행하세요. 언어별 가이드에 따라 개발 SDK를 설치해요. 네 SDK 모두 패키지 의존성으로 설치해요. [SDK](/sdk/python/) ```bash python3 -m venv .venv .venv/bin/python -m pip install -r requirements.txt ``` env.example을 참고해 SIGNOX\_API\_URL·SIGNOX\_PRODUCT\_ID·SIGNOX\_PUBLIC\_KEY\_FILE·SIGNOX\_STATE\_DIR를 설정하세요. 제품 UUID와 공개키는 벤더의 제품 설정에서 받아요. 고객은 포털에서만 라이센스 키를 등록해요. 개인 상태와 계정 자격증명은 커밋하지 마세요. ## 샘플 실행하기 [섹션 제목: “샘플 실행하기”](#샘플-실행하기) env.example의 네 변수를 실행할 셸에 설정하고 SIGNOX\_PUBLIC\_KEY\_FILE 위치에 제품 공개키를 저장하세요. start가 출력한 고객 포털에서 승인한 뒤 poll과 export를 실행하세요. ```bash .venv/bin/python main.py activation-start .venv/bin/python main.py activation-poll .venv/bin/python main.py activation-export output.csv ``` 연결되지 않은 기기에서는 아래 두 파일을 PC 사이에 옮기세요: ```bash .venv/bin/python main.py activation-request request.signox # Import request.signox in the customer portal and save activation.signox. .venv/bin/python main.py activation-apply activation.signox ``` | Command | 기대 동작 | | ------------------------------------ | ----------------------------------------------------- | | `activation-start` | 앱 브랜드가 유지되는 고객 포털 주소를 출력해요. 브라우저로 여세요. | | `activation-poll` | 승인 전에는 pending, 승인 후에는 응답 적용과 권한 검증 결과를 출력해요. | | `activation-request` | 다른 PC로 전달할 공개 요청 JSON을 출력해요. | | `activation-renew-request` | 로컬 설치 식별을 유지한 채 만료된 요청을 새 요청으로 교체해요. | | `activation-apply activation.signox` | 고객 포털에서 받은 응답의 서명과 기기를 검증하고 적용해요. | | `activation-status` | 현재 권한을 확인해요. valid와 demo\_export가 모두 참이어야 내보내기를 허용해요. | | `activation-export output.csv` | 허용되면 센서 예제 2행을 실제 파일로 써요. 기능이 없으면 종료 코드 3이에요. | | `activation-deactivate` | 연결 기반 등록을 해제해요. 보호된 이용권은 이전 검토가 필요해요. | 종료 코드 0은 명령 실행 성공이며 승인 대기 pending도 포함해요. 반드시 활성화 완료라는 뜻은 아니에요. 종료 코드 2는 검증·활성화 실패예요. 대기·미실행을 검증 통과로 기록하지 마세요. 요청 생성 → 고객 로그인 → 구매 키 등록 → 라이센스 선택 → 승인 → poll/apply 순서로 실행하세요. 별도 상태 디렉터리에서 요청을 만들면 다른 기기 이전 검토로 안내돼야 해요. 명령·종료 코드·예상/실제 결과·증거 경로를 남기세요. 기존 방식의 온라인·오프라인 전용 명령은 호환성 검증용으로 남아 있어요. 신규 연동은 위 activation 명령으로 시작하세요. # C# SDK > SDK 설치부터 고객 승인과 앱 기능 권한 확인까지 연결해요 `ActivationClient`로 C# 앱에 라이센스 활성화를 연결해요. SDK를 설치하고 고객 포털에서 기기를 승인한 뒤, 앱에서 기능을 사용할 수 있는지 확인하는 과정을 알아보세요. 활성화 요청은 승인할 앱 설치를 식별하는 정보예요. 고객이 해당 앱의 이름과 로고가 표시된 포털에서 소유한 라이센스를 선택하면, 그 요청과 기기에 묶인 서명된 활성화 응답을 받아요. 라이센스 하나에는 활성 기기 하나만 등록돼요. ## 1. SDK를 설치하세요 [섹션 제목: “1. SDK를 설치하세요”](#1-sdk를-설치하세요) ```bash dotnet new console --framework net8.0 dotnet add package Signox.Sdk --version 0.3.1 ``` 개발 환경에 설정된 패키지 저장소를 사용하세요. SDK는 앱의 의존성으로 설치해요. 샘플 ZIP에는 실행 예제 소스가 들어 있으며, SDK 설치를 대신하지 않아요. 실행 환경: SDK는 netstandard2.0, 예제는 .NET 8. 프로젝트 폴더에서 복원된 SDK 버전을 확인하세요. Signox.Sdk의 확인된 버전이 0.3.1이어야 해요. .NET 실행기가 설치되어 있어도 프로젝트의 패키지 복원은 필요해요. ```bash dotnet list package ``` ## 2. 제품과 테스트 라이센스를 준비하세요 [섹션 제목: “2. 제품과 테스트 라이센스를 준비하세요”](#2-제품과-테스트-라이센스를-준비하세요) 벤더 대시보드에서 제품을 선택하고 설정을 여세요. 제품 상세 주소의 UUID를 확인하고 그 제품의 공개키를 내려받으세요. 정책에 demo\_export 불리언 기능을 추가해 켠 뒤 테스트 라이센스를 발급하고, 고객 포털에서 테스트 고객에게 구매 키를 등록하세요. | 입력 | 얻는 위치와 사용 방법 | | ------------------------ | -------------------------------------------- | | `SIGNOX_API_URL` | 테스트할 환경의 API 주소를 사용해요. 포털 주소와는 달라요. | | `SIGNOX_PRODUCT_ID` | 벤더의 제품 상세 주소에 있는 UUID예요. | | `SIGNOX_PUBLIC_KEY_FILE` | 내려받은 제품 공개키 파일의 경로예요. PEM 내용 자체를 변수에 넣지 않아요. | | `.signox-state` | 예제가 만드는 개인 상태 디렉터리예요. 재시작 후에도 같은 경로를 유지하세요. | 예제를 실행할 셸에 위 세 환경변수를 설정하세요. 제품 공개키는 신뢰하는 앱 배포물에 포함해 사용하세요. 고객 비밀번호는 고객 포털에만 입력하고, 라이센스 키는 앱에 하드코딩하지 않아요. ## 3. 활성화 예제를 작성하세요 [섹션 제목: “3. 활성화 예제를 작성하세요”](#3-활성화-예제를-작성하세요) 아래 전체 코드를 `Program.cs` 파일로 저장하세요. 모든 명령은 같은 프로젝트 디렉터리에서 실행하세요. ```csharp using System; using System.IO; using Signox.Sdk; class App { static int Main(string[] args) { string api = Environment.GetEnvironmentVariable("SIGNOX_API_URL"), product = Environment.GetEnvironmentVariable("SIGNOX_PRODUCT_ID"), keyFile = Environment.GetEnvironmentVariable("SIGNOX_PUBLIC_KEY_FILE"); if (api == null || product == null || keyFile == null) throw new ArgumentException("Set SIGNOX_API_URL, SIGNOX_PRODUCT_ID and SIGNOX_PUBLIC_KEY_FILE"); var client = new ActivationClient(product, new SignoxOptions { ProductPublicKey = File.ReadAllText(keyFile), BaseUrl = api }, ".signox-state"); string command = args.Length == 0 ? "start" : args[0]; if (command == "start") Console.WriteLine(client.Start().PortalUrl); else if (command == "request") Console.WriteLine(client.CreateRequest()); else if (command == "poll") { var progress = client.Poll(); Console.WriteLine(progress.Status); if (progress.Result != null && !progress.Result.Valid) return 2; } else { var result = command == "apply" ? client.ApplyResponse(File.ReadAllText(args[1])) : client.Validate(); bool allowed = result.Valid && result.Features.TryGetValue("demo_export", out var value) && value is bool && (bool)value; Console.WriteLine("code=" + result.Code + ", exportAllowed=" + allowed); if (!result.Valid) return 2; } return 0; } } ``` 브라우저 승인과 파일 전달 모두 같은 클라이언트를 사용해요. SDK가 실행 환경의 장치 모듈을 선택하므로 앱에서 장치 공급자 종류를 고르지 않아요. ## 4. 고객 포털에서 기기를 승인하세요 [섹션 제목: “4. 고객 포털에서 기기를 승인하세요”](#4-고객-포털에서-기기를-승인하세요) ```bash dotnet run -- start ``` 출력된 portalUrl을 시스템 브라우저로 여세요. 앱 이름과 기기 이름을 확인한 뒤 고객 계정으로 로그인하고, 소유한 라이센스를 골라 이 기기 활성화를 누르세요. 다른 기기가 등록되어 있다면 사유를 입력해 벤더에게 이전 검토를 요청할 수 있어요. ```bash dotnet run -- poll ``` 승인 전에는 status가 pending이에요. 승인 후 다시 실행하면 응답을 받아 적용해요. 3초보다 짧은 간격으로 반복 호출하지 마세요. pending은 승인 대기이며 라이센스 검증 성공이 아니에요. ## 5. 기능 실행 전에 권한을 확인하세요 [섹션 제목: “5. 기능 실행 전에 권한을 확인하세요”](#5-기능-실행-전에-권한을-확인하세요) ```bash dotnet run -- status ``` 성공하면 VALID 또는 IN\_GRACE\_PERIOD가 나와요. 라이센스가 유효하고 demo\_export가 true일 때만 예제의 exportAllowed가 true가 돼요. 기능이 없는 라이센스는 유효하더라도 CSV를 만들면 안 돼요. 이 판정을 시작 화면뿐 아니라 앱의 실제 내보내기 함수에도 넣으세요. [실제 CSV 파일을 만드는 샘플 실행하기](/samples/csharp/) ## 다른 기기에서 활성화하기 [섹션 제목: “다른 기기에서 활성화하기”](#다른-기기에서-활성화하기) 원래 기기에 연결이 없다면 요청을 내보내고, 연결된 PC의 고객 포털 /activate에서 가져오세요. 승인 후 받은 activation.signox를 원래 기기로 옮겨요. SDK의 applyResponse에는 파일 경로가 아닌 파일 내용을 전달해요. 위 예제는 대신 파일을 읽어 줘요. ```bash dotnet run -- request dotnet run -- apply activation.signox ``` 파일로 전달할 때는 언어별 샘플의 activation-request request.signox 명령을 사용하세요. 특히 Maven의 빌드 로그를 요청 파일로 리다이렉션하지 마세요. 연결 없이 응답을 적용하려면 지원되고 신뢰 등록된 기기 키가 필요해요. 보호 기능이 없으면 응답 적용 시 연결해야 해요. 앱을 배포하기 전에 실행 환경과 기기 요구 사항을 확인하세요. [오프라인 실행 환경 확인하기](/guides/activation-offline/) ## 생성자와 옵션 [섹션 제목: “생성자와 옵션”](#생성자와-옵션) | 이름 | 타입 | 필수 여부·기본값 | 설명 | | -------------------- | --------------- | --------------------------------------------------- | --------------------------------- | | `productId` | `string` | 필수 | ActivationClient 생성자의 제품 UUID | | `options` | `SignoxOptions` | 필수 | ProductPublicKey와 서버 연결 설정 | | `stateDir` | `string` | 필수 | 이 설치에서만 쓰는 영속 디렉터리 | | `BaseUrl` | `string` | [https://api.signox.kr](https://api.signox.twentyoz.dev) | SignoxOptions의 API 주소 | | `TimeoutMs` | `int` | 10000 ms | 네트워크 요청 제한 시간 | | `TsToleranceSeconds` | `int` | 300 초 | 서버 시각과의 허용 차이 | | `OfflineGraceDays` | `int` | 365 일 | 서버 통신 유예를 줄이는 로컬 상한; 0이면 캐시 유예 해제 | 로컬 유예 상한으로 서버가 서명한 기간을 늘릴 수는 없어요. 만료 유예와 통신 장애 유예는 별개이며, 빈 캐시로 최초 활성화를 허용하지 않아요. ### 신뢰하는 실행 모듈로 시험하기 [섹션 제목: “신뢰하는 실행 모듈로 시험하기”](#신뢰하는-실행-모듈로-시험하기) 기본값은 OS에 맞는 모듈을 자동 선택해요. 통제된 기기 시험이나 앱이 직접 제공하는 모듈을 사용할 때는 `new ActivationClient(productId, options, stateDir, executable, args...)`을 지정할 수 있어요. 신뢰하는 앱 배포물이나 시험 환경에서 제공한 실행 파일만 사용하세요. 고객 요청 JSON이나 활성화 응답의 경로를 실행하면 안 돼요. 샘플은 제공된 시험용 래퍼를 SIGNOX\_OFFLINE\_RUNTIME·SIGNOX\_OFFLINE\_EXECUTABLE로 지정할 수 있어요. 이 옵션으로 기기 신뢰와 서명 검증을 우회하지는 않아요. ## 메서드와 반환값 [섹션 제목: “메서드와 반환값”](#메서드와-반환값) | 시그니처 | 반환 타입 | 동작 | | ---------------------------------------------- | -------------------- | ----------------------------------------------------------------- | | `Start()` | `ActivationSession` | 요청을 등록하고 브라우저 주소와 요청 번호를 반환해요. | | `CreateRequest(name="My device", renew=false)` | `string` | 공개 요청 JSON을 반환해요. name은 기기 표시명, renew는 설치 자격증명을 유지한 새 요청 생성 여부예요. | | `Poll()` | `ActivationProgress` | 승인 전에는 pending, 승인 후에는 응답을 적용한 검증 결과를 반환해요. | | `ApplyResponse(content)` | `LicenseResult` | 응답 문자열을 읽고 요청·기기 일치를 검증한 뒤 적용해요. | | `Validate()` | `LicenseResult` | 라이센스 상태와 현재 기능 권한을 확인해요. | | `Deactivate()` | `LicenseResult` | 연결 기반 등록을 해제해요. 보호된 이용권은 이전 검토로 안내해요. | Node는 비동기 메서드예요. Java·C#·Python은 동기 메서드이므로 네트워크·장치 작업을 UI 스레드 밖에서 실행하세요. LicenseResult에는 유효 여부·코드·기능값이 들어 있어요. 요청·상태·가져오기 오류의 예외를 검증 성공으로 바꾸면 안 돼요. ## 실패했을 때 확인하기 [섹션 제목: “실패했을 때 확인하기”](#실패했을-때-확인하기) | 현상·코드 | 원인 | 조치와 재확인 결과 | | ----------------------------- | -------------------------------- | -------------------------------------------------- | | `STATE_INVALID` | 저장한 설치 상태가 없거나 손상됨 | 보존한 상태를 복원하세요. 복구할 수 없으면 새 설치에서 요청을 만들어 이전을 신청하세요. | | `DEVICE_TRUST_REQUIRED` | 검증되지 않은 장비 키에는 포털이 발급할 수 없음 | 운영자가 실제 기기의 키를 별도 신뢰 경로로 대조하고 등록한 뒤 다시 승인하세요. | | `TRANSFER_REQUIRED` | 다른 기기가 등록되어 있거나 보호된 반납을 확인할 수 없음 | 새 기기의 요청으로 이전을 신청하세요. 벤더는 그 요청을 검토해요. | | `ACTIVATION_RESPONSE_INVALID` | 다른 파일·서명·요청·기기 | 원래 기기에서 이 요청에 발급된 응답을 적용하세요. 응답 내용을 수정하지 마세요. | | `REQUEST_EXPIRED` | 요청의 7일 유효기간 경과 | 새 요청을 만들어 고객 승인을 다시 진행하세요. | | `NETWORK_ERROR` | 일시적인 연결·서비스 장애 | 연결을 복구하세요. 기존의 유효한 서명 이용권·캐시만 연결 없이 사용할 수 있어요. | | `REQUEST_ERROR` | 잘못된 주소·입력·접근 권한 | 요청을 수정하세요. 영구 HTTP 오류를 무한 재시도하지 마세요. | 서명된 정지·폐기·만료 거절을 받은 뒤 연결이 끊겨도 거절을 유지해야 해요. 이전 오프라인 이용권으로 덮지 마세요. 계속 연결되지 않은 기기는 즉시 원격 회수 신호를 받을 수 없으며, 포맷이나 파일 삭제만으로 옛 백업이 중단됐다고 증명할 수 없어요. [통합 검증 실행하기](/guides/integration-checks/) · [기기 복구·이전 알아보기](/guides/hwid/) 응답은 받았지만 로컬 검증에 실패하면 polling의 status는 application\_failed이고 result는 무효예요. applied와 유효한 result를 함께 확인해야 적용 성공이에요. 요청 생성과 적용에 같은 실행 모듈 설정을 사용하세요. 브라우저·파일 전달 방식은 기기 보호 유무를 선택하는 옵션이 아니고, 요청 이름을 바꾸어도 공급자가 바뀌지 않아요. 검증된 응답에서 OFFLINE\_NOT\_SUPPORTED가 나오면 필요한 로컬 실행 모듈을 사용할 수 없는 상태이므로 기존 환경을 복원하세요. 무조건 새 신뢰 등록을 요청하는 오류는 아니에요. # Java SDK > SDK 설치부터 고객 승인과 앱 기능 권한 확인까지 연결해요 `ActivationClient`로 Java 앱에 라이센스 활성화를 연결해요. SDK를 설치하고 고객 포털에서 기기를 승인한 뒤, 앱에서 기능을 사용할 수 있는지 확인하는 과정을 알아보세요. 활성화 요청은 승인할 앱 설치를 식별하는 정보예요. 고객이 해당 앱의 이름과 로고가 표시된 포털에서 소유한 라이센스를 선택하면, 그 요청과 기기에 묶인 서명된 활성화 응답을 받아요. 라이센스 하나에는 활성 기기 하나만 등록돼요. ## 1. SDK를 설치하세요 [섹션 제목: “1. SDK를 설치하세요”](#1-sdk를-설치하세요) ```xml 4.0.0 kr.signox.sampleslicensed-export0.3.1-SNAPSHOT 8UTF-8 kr.signoxsignox-sdk0.3.1-SNAPSHOT org.apache.maven.pluginsmaven-compiler-plugin3.13.0 org.apache.maven.pluginsmaven-dependency-plugin3.8.1 org.codehaus.mojoexec-maven-plugin3.6.3 ``` ```bash mvn -U compile ``` 개발 환경에 설정된 패키지 저장소를 사용하세요. SDK는 앱의 의존성으로 설치해요. 샘플 ZIP에는 실행 예제 소스가 들어 있으며, SDK 설치를 대신하지 않아요. 실행 환경: Java 8+, 예제 빌드는 JDK 17+·Maven 3.6.3+. 프로젝트 폴더에서 Maven이 선택한 SDK 버전을 확인하세요. 출력에 kr.signox:signox-sdk:jar:0.3.1-SNAPSHOT이 표시되어야 해요. JDK 설치와 SDK 의존성 설치는 별도예요. ```bash mvn dependency:tree -Dincludes=kr.signox:signox-sdk ``` ## 2. 제품과 테스트 라이센스를 준비하세요 [섹션 제목: “2. 제품과 테스트 라이센스를 준비하세요”](#2-제품과-테스트-라이센스를-준비하세요) 벤더 대시보드에서 제품을 선택하고 설정을 여세요. 제품 상세 주소의 UUID를 확인하고 그 제품의 공개키를 내려받으세요. 정책에 demo\_export 불리언 기능을 추가해 켠 뒤 테스트 라이센스를 발급하고, 고객 포털에서 테스트 고객에게 구매 키를 등록하세요. | 입력 | 얻는 위치와 사용 방법 | | ------------------------ | -------------------------------------------- | | `SIGNOX_API_URL` | 테스트할 환경의 API 주소를 사용해요. 포털 주소와는 달라요. | | `SIGNOX_PRODUCT_ID` | 벤더의 제품 상세 주소에 있는 UUID예요. | | `SIGNOX_PUBLIC_KEY_FILE` | 내려받은 제품 공개키 파일의 경로예요. PEM 내용 자체를 변수에 넣지 않아요. | | `.signox-state` | 예제가 만드는 개인 상태 디렉터리예요. 재시작 후에도 같은 경로를 유지하세요. | 예제를 실행할 셸에 위 세 환경변수를 설정하세요. 제품 공개키는 신뢰하는 앱 배포물에 포함해 사용하세요. 고객 비밀번호는 고객 포털에만 입력하고, 라이센스 키는 앱에 하드코딩하지 않아요. ## 3. 활성화 예제를 작성하세요 [섹션 제목: “3. 활성화 예제를 작성하세요”](#3-활성화-예제를-작성하세요) 아래 전체 코드를 `src/main/java/App.java` 파일로 저장하세요. 모든 명령은 같은 프로젝트 디렉터리에서 실행하세요. ```java import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.*; import kr.signox.sdk.*; public class App { public static void main(String[] args) throws Exception { String api = System.getenv("SIGNOX_API_URL"), product = System.getenv("SIGNOX_PRODUCT_ID"), keyFile = System.getenv("SIGNOX_PUBLIC_KEY_FILE"); if (api == null || product == null || keyFile == null) throw new IllegalArgumentException("Set SIGNOX_API_URL, SIGNOX_PRODUCT_ID and SIGNOX_PUBLIC_KEY_FILE"); String pem = new String(Files.readAllBytes(Paths.get(keyFile)), StandardCharsets.UTF_8); ActivationClient client = new ActivationClient(product, SignoxConfig.builder(pem).baseUrl(api).build(), new File(".signox-state")); String command = args.length == 0 ? "start" : args[0]; if (command.equals("start")) System.out.println(client.start().portalUrl); else if (command.equals("request")) System.out.println(client.createRequest()); else if (command.equals("poll")) { ActivationClient.Progress progress = client.poll(); System.out.println(progress.status); if (progress.result != null && !progress.result.isValid()) System.exit(2); } else { LicenseResult result = command.equals("apply") ? client.applyResponse(new String(Files.readAllBytes(Paths.get(args[1])), StandardCharsets.UTF_8)) : client.validate(); boolean allowed = result.isValid() && result.getFeatureBool("demo_export", false); System.out.println("code=" + result.getCode() + ", exportAllowed=" + allowed); if (!result.isValid()) System.exit(2); } } } ``` 브라우저 승인과 파일 전달 모두 같은 클라이언트를 사용해요. SDK가 실행 환경의 장치 모듈을 선택하므로 앱에서 장치 공급자 종류를 고르지 않아요. ## 4. 고객 포털에서 기기를 승인하세요 [섹션 제목: “4. 고객 포털에서 기기를 승인하세요”](#4-고객-포털에서-기기를-승인하세요) ```bash mvn compile exec:java -Dexec.mainClass=App -Dexec.args="start" ``` 출력된 portalUrl을 시스템 브라우저로 여세요. 앱 이름과 기기 이름을 확인한 뒤 고객 계정으로 로그인하고, 소유한 라이센스를 골라 이 기기 활성화를 누르세요. 다른 기기가 등록되어 있다면 사유를 입력해 벤더에게 이전 검토를 요청할 수 있어요. ```bash mvn compile exec:java -Dexec.mainClass=App -Dexec.args="poll" ``` 승인 전에는 status가 pending이에요. 승인 후 다시 실행하면 응답을 받아 적용해요. 3초보다 짧은 간격으로 반복 호출하지 마세요. pending은 승인 대기이며 라이센스 검증 성공이 아니에요. ## 5. 기능 실행 전에 권한을 확인하세요 [섹션 제목: “5. 기능 실행 전에 권한을 확인하세요”](#5-기능-실행-전에-권한을-확인하세요) ```bash mvn compile exec:java -Dexec.mainClass=App -Dexec.args="status" ``` 성공하면 VALID 또는 IN\_GRACE\_PERIOD가 나와요. 라이센스가 유효하고 demo\_export가 true일 때만 예제의 exportAllowed가 true가 돼요. 기능이 없는 라이센스는 유효하더라도 CSV를 만들면 안 돼요. 이 판정을 시작 화면뿐 아니라 앱의 실제 내보내기 함수에도 넣으세요. [실제 CSV 파일을 만드는 샘플 실행하기](/samples/java/) ## 다른 기기에서 활성화하기 [섹션 제목: “다른 기기에서 활성화하기”](#다른-기기에서-활성화하기) 원래 기기에 연결이 없다면 요청을 내보내고, 연결된 PC의 고객 포털 /activate에서 가져오세요. 승인 후 받은 activation.signox를 원래 기기로 옮겨요. SDK의 applyResponse에는 파일 경로가 아닌 파일 내용을 전달해요. 위 예제는 대신 파일을 읽어 줘요. ```bash mvn compile exec:java -Dexec.mainClass=App -Dexec.args="request" mvn compile exec:java -Dexec.mainClass=App -Dexec.args="apply activation.signox" ``` 파일로 전달할 때는 언어별 샘플의 activation-request request.signox 명령을 사용하세요. 특히 Maven의 빌드 로그를 요청 파일로 리다이렉션하지 마세요. 연결 없이 응답을 적용하려면 지원되고 신뢰 등록된 기기 키가 필요해요. 보호 기능이 없으면 응답 적용 시 연결해야 해요. 앱을 배포하기 전에 실행 환경과 기기 요구 사항을 확인하세요. [오프라인 실행 환경 확인하기](/guides/activation-offline/) ## 생성자와 옵션 [섹션 제목: “생성자와 옵션”](#생성자와-옵션) | 이름 | 타입 | 필수 여부·기본값 | 설명 | | -------------------- | -------------- | --------------------------------------------------- | --------------------------------- | | `productId` | `String` | 필수 | ActivationClient 생성자의 제품 UUID | | `config` | `SignoxConfig` | 필수 | 공개키와 서버 연결 설정 | | `stateDir` | `File` | 필수 | 이 설치에서만 쓰는 영속 디렉터리 | | `baseUrl` | `String` | [https://api.signox.kr](https://api.signox.twentyoz.dev) | SignoxConfig.Builder 옵션 | | `timeoutMs` | `int` | 10000 ms | 연결·읽기 제한 시간 | | `tsToleranceSeconds` | `long` | 300 초 | 서버 시각과의 허용 차이 | | `offlineGraceDays` | `long` | 365 일 | 서버 통신 유예를 줄이는 로컬 상한; 0이면 캐시 유예 해제 | 로컬 유예 상한으로 서버가 서명한 기간을 늘릴 수는 없어요. 만료 유예와 통신 장애 유예는 별개이며, 빈 캐시로 최초 활성화를 허용하지 않아요. ### 신뢰하는 실행 모듈로 시험하기 [섹션 제목: “신뢰하는 실행 모듈로 시험하기”](#신뢰하는-실행-모듈로-시험하기) 기본값은 OS에 맞는 모듈을 자동 선택해요. 통제된 기기 시험이나 앱이 직접 제공하는 모듈을 사용할 때는 `new ActivationClient(productId, config, stateDir, executable, args...)`을 지정할 수 있어요. 신뢰하는 앱 배포물이나 시험 환경에서 제공한 실행 파일만 사용하세요. 고객 요청 JSON이나 활성화 응답의 경로를 실행하면 안 돼요. 샘플은 제공된 시험용 래퍼를 SIGNOX\_OFFLINE\_RUNTIME·SIGNOX\_OFFLINE\_EXECUTABLE로 지정할 수 있어요. 이 옵션으로 기기 신뢰와 서명 검증을 우회하지는 않아요. ## 메서드와 반환값 [섹션 제목: “메서드와 반환값”](#메서드와-반환값) | 시그니처 | 반환 타입 | 동작 | | ---------------------------------------------- | --------------------------- | ----------------------------------------------------------------- | | `start()` | `ActivationClient.Session` | 요청을 등록하고 브라우저 주소와 요청 번호를 반환해요. | | `createRequest() / createRequest(name, renew)` | `String` | 공개 요청 JSON을 반환해요. name은 기기 표시명, renew는 설치 자격증명을 유지한 새 요청 생성 여부예요. | | `poll()` | `ActivationClient.Progress` | 승인 전에는 pending, 승인 후에는 응답을 적용한 검증 결과를 반환해요. | | `applyResponse(content)` | `LicenseResult` | 응답 문자열을 읽고 요청·기기 일치를 검증한 뒤 적용해요. | | `validate()` | `LicenseResult` | 라이센스 상태와 현재 기능 권한을 확인해요. | | `deactivate()` | `LicenseResult` | 연결 기반 등록을 해제해요. 보호된 이용권은 이전 검토로 안내해요. | Node는 비동기 메서드예요. Java·C#·Python은 동기 메서드이므로 네트워크·장치 작업을 UI 스레드 밖에서 실행하세요. LicenseResult에는 유효 여부·코드·기능값이 들어 있어요. 요청·상태·가져오기 오류의 예외를 검증 성공으로 바꾸면 안 돼요. ## 실패했을 때 확인하기 [섹션 제목: “실패했을 때 확인하기”](#실패했을-때-확인하기) | 현상·코드 | 원인 | 조치와 재확인 결과 | | ----------------------------- | -------------------------------- | -------------------------------------------------- | | `STATE_INVALID` | 저장한 설치 상태가 없거나 손상됨 | 보존한 상태를 복원하세요. 복구할 수 없으면 새 설치에서 요청을 만들어 이전을 신청하세요. | | `DEVICE_TRUST_REQUIRED` | 검증되지 않은 장비 키에는 포털이 발급할 수 없음 | 운영자가 실제 기기의 키를 별도 신뢰 경로로 대조하고 등록한 뒤 다시 승인하세요. | | `TRANSFER_REQUIRED` | 다른 기기가 등록되어 있거나 보호된 반납을 확인할 수 없음 | 새 기기의 요청으로 이전을 신청하세요. 벤더는 그 요청을 검토해요. | | `ACTIVATION_RESPONSE_INVALID` | 다른 파일·서명·요청·기기 | 원래 기기에서 이 요청에 발급된 응답을 적용하세요. 응답 내용을 수정하지 마세요. | | `REQUEST_EXPIRED` | 요청의 7일 유효기간 경과 | 새 요청을 만들어 고객 승인을 다시 진행하세요. | | `NETWORK_ERROR` | 일시적인 연결·서비스 장애 | 연결을 복구하세요. 기존의 유효한 서명 이용권·캐시만 연결 없이 사용할 수 있어요. | | `REQUEST_ERROR` | 잘못된 주소·입력·접근 권한 | 요청을 수정하세요. 영구 HTTP 오류를 무한 재시도하지 마세요. | 서명된 정지·폐기·만료 거절을 받은 뒤 연결이 끊겨도 거절을 유지해야 해요. 이전 오프라인 이용권으로 덮지 마세요. 계속 연결되지 않은 기기는 즉시 원격 회수 신호를 받을 수 없으며, 포맷이나 파일 삭제만으로 옛 백업이 중단됐다고 증명할 수 없어요. [통합 검증 실행하기](/guides/integration-checks/) · [기기 복구·이전 알아보기](/guides/hwid/) 응답은 받았지만 로컬 검증에 실패하면 polling의 status는 application\_failed이고 result는 무효예요. applied와 유효한 result를 함께 확인해야 적용 성공이에요. 요청 생성과 적용에 같은 실행 모듈 설정을 사용하세요. 브라우저·파일 전달 방식은 기기 보호 유무를 선택하는 옵션이 아니고, 요청 이름을 바꾸어도 공급자가 바뀌지 않아요. 검증된 응답에서 OFFLINE\_NOT\_SUPPORTED가 나오면 필요한 로컬 실행 모듈을 사용할 수 없는 상태이므로 기존 환경을 복원하세요. 무조건 새 신뢰 등록을 요청하는 오류는 아니에요. # Node SDK > SDK 설치부터 고객 승인과 앱 기능 권한 확인까지 연결해요 `ActivationClient`로 Node 앱에 라이센스 활성화를 연결해요. SDK를 설치하고 고객 포털에서 기기를 승인한 뒤, 앱에서 기능을 사용할 수 있는지 확인하는 과정을 알아보세요. 활성화 요청은 승인할 앱 설치를 식별하는 정보예요. 고객이 해당 앱의 이름과 로고가 표시된 포털에서 소유한 라이센스를 선택하면, 그 요청과 기기에 묶인 서명된 활성화 응답을 받아요. 라이센스 하나에는 활성 기기 하나만 등록돼요. ## 1. SDK를 설치하세요 [섹션 제목: “1. SDK를 설치하세요”](#1-sdk를-설치하세요) ```bash npm install @signox/sdk@0.3.1 ``` 개발 환경에 설정된 패키지 저장소를 사용하세요. SDK는 앱의 의존성으로 설치해요. 샘플 ZIP에는 실행 예제 소스가 들어 있으며, SDK 설치를 대신하지 않아요. 실행 환경: Node 18+, Node 22 권장. 프로젝트 폴더에서 설치된 SDK 버전을 확인하세요. 출력에 @signox/sdk\@0.3.1이 표시되어야 해요. ```bash npm ls @signox/sdk ``` ## 2. 제품과 테스트 라이센스를 준비하세요 [섹션 제목: “2. 제품과 테스트 라이센스를 준비하세요”](#2-제품과-테스트-라이센스를-준비하세요) 벤더 대시보드에서 제품을 선택하고 설정을 여세요. 제품 상세 주소의 UUID를 확인하고 그 제품의 공개키를 내려받으세요. 정책에 demo\_export 불리언 기능을 추가해 켠 뒤 테스트 라이센스를 발급하고, 고객 포털에서 테스트 고객에게 구매 키를 등록하세요. | 입력 | 얻는 위치와 사용 방법 | | ------------------------ | -------------------------------------------- | | `SIGNOX_API_URL` | 테스트할 환경의 API 주소를 사용해요. 포털 주소와는 달라요. | | `SIGNOX_PRODUCT_ID` | 벤더의 제품 상세 주소에 있는 UUID예요. | | `SIGNOX_PUBLIC_KEY_FILE` | 내려받은 제품 공개키 파일의 경로예요. PEM 내용 자체를 변수에 넣지 않아요. | | `.signox-state` | 예제가 만드는 개인 상태 디렉터리예요. 재시작 후에도 같은 경로를 유지하세요. | 예제를 실행할 셸에 위 세 환경변수를 설정하세요. 제품 공개키는 신뢰하는 앱 배포물에 포함해 사용하세요. 고객 비밀번호는 고객 포털에만 입력하고, 라이센스 키는 앱에 하드코딩하지 않아요. ## 3. 활성화 예제를 작성하세요 [섹션 제목: “3. 활성화 예제를 작성하세요”](#3-활성화-예제를-작성하세요) 아래 전체 코드를 `app.mjs` 파일로 저장하세요. 모든 명령은 같은 프로젝트 디렉터리에서 실행하세요. ```js import { readFileSync } from 'node:fs'; import { ActivationClient } from '@signox/sdk'; const { SIGNOX_API_URL, SIGNOX_PRODUCT_ID, SIGNOX_PUBLIC_KEY_FILE } = process.env; if (!SIGNOX_API_URL || !SIGNOX_PRODUCT_ID || !SIGNOX_PUBLIC_KEY_FILE) throw new Error('Set SIGNOX_API_URL, SIGNOX_PRODUCT_ID and SIGNOX_PUBLIC_KEY_FILE'); const client = new ActivationClient({ productId: SIGNOX_PRODUCT_ID, productPublicKey: readFileSync(SIGNOX_PUBLIC_KEY_FILE, 'utf8'), baseUrl: SIGNOX_API_URL, stateDir: './.signox-state' }); const [command = 'start', file] = process.argv.slice(2); if (command === 'start') console.log(await client.start()); else if (command === 'request') console.log(await client.createRequest()); else if (command === 'poll') { const progress = await client.poll(); console.log({ status: progress.status, valid: progress.result?.valid, code: progress.result?.code, exportAllowed: !!progress.result?.valid && progress.result.features.demo_export === true }); if (progress.result && !progress.result.valid) process.exitCode = 2; } else { if (command === 'apply' && !file) throw new Error('Activation response file is required'); const result = command === 'apply' ? await client.applyResponse(readFileSync(file, 'utf8')) : await client.validate(); const exportAllowed = result.valid && result.features.demo_export === true; console.log({ valid: result.valid, code: result.code, exportAllowed }); if (!result.valid) process.exitCode = 2; } ``` 브라우저 승인과 파일 전달 모두 같은 클라이언트를 사용해요. SDK가 실행 환경의 장치 모듈을 선택하므로 앱에서 장치 공급자 종류를 고르지 않아요. ## 4. 고객 포털에서 기기를 승인하세요 [섹션 제목: “4. 고객 포털에서 기기를 승인하세요”](#4-고객-포털에서-기기를-승인하세요) ```bash node app.mjs start ``` 출력된 portalUrl을 시스템 브라우저로 여세요. 앱 이름과 기기 이름을 확인한 뒤 고객 계정으로 로그인하고, 소유한 라이센스를 골라 이 기기 활성화를 누르세요. 다른 기기가 등록되어 있다면 사유를 입력해 벤더에게 이전 검토를 요청할 수 있어요. ```bash node app.mjs poll ``` 승인 전에는 status가 pending이에요. 승인 후 다시 실행하면 응답을 받아 적용해요. 3초보다 짧은 간격으로 반복 호출하지 마세요. pending은 승인 대기이며 라이센스 검증 성공이 아니에요. ## 5. 기능 실행 전에 권한을 확인하세요 [섹션 제목: “5. 기능 실행 전에 권한을 확인하세요”](#5-기능-실행-전에-권한을-확인하세요) ```bash node app.mjs status ``` 성공하면 VALID 또는 IN\_GRACE\_PERIOD가 나와요. 라이센스가 유효하고 demo\_export가 true일 때만 예제의 exportAllowed가 true가 돼요. 기능이 없는 라이센스는 유효하더라도 CSV를 만들면 안 돼요. 이 판정을 시작 화면뿐 아니라 앱의 실제 내보내기 함수에도 넣으세요. [실제 CSV 파일을 만드는 샘플 실행하기](/samples/node/) ## 다른 기기에서 활성화하기 [섹션 제목: “다른 기기에서 활성화하기”](#다른-기기에서-활성화하기) 원래 기기에 연결이 없다면 요청을 내보내고, 연결된 PC의 고객 포털 /activate에서 가져오세요. 승인 후 받은 activation.signox를 원래 기기로 옮겨요. SDK의 applyResponse에는 파일 경로가 아닌 파일 내용을 전달해요. 위 예제는 대신 파일을 읽어 줘요. ```bash node app.mjs request node app.mjs apply activation.signox ``` 파일로 전달할 때는 언어별 샘플의 activation-request request.signox 명령을 사용하세요. 특히 Maven의 빌드 로그를 요청 파일로 리다이렉션하지 마세요. 연결 없이 응답을 적용하려면 지원되고 신뢰 등록된 기기 키가 필요해요. 보호 기능이 없으면 응답 적용 시 연결해야 해요. 앱을 배포하기 전에 실행 환경과 기기 요구 사항을 확인하세요. [오프라인 실행 환경 확인하기](/guides/activation-offline/) ## 생성자와 옵션 [섹션 제목: “생성자와 옵션”](#생성자와-옵션) | 이름 | 타입 | 필수 여부·기본값 | 설명 | | ------------------ | -------- | --------------------------------------------------- | --------------------------------- | | `productId` | `string` | 필수 | 제품 상세 주소에 있는 UUID | | `productPublicKey` | `string` | 필수 | 앱에 포함한 SPKI PEM 공개키 내용 | | `stateDir` | `string` | 필수 | 이 설치에서만 쓰는 영속 디렉터리 | | `baseUrl` | `string` | [https://api.signox.kr](https://api.signox.twentyoz.dev) | 대상 환경의 API 주소 | | `timeout` | `number` | 10000 ms | 네트워크 요청 제한 시간 | | `tsToleranceSec` | `number` | 300 초 | 서버 시각과의 허용 차이 | | `offlineGraceDays` | `number` | 365 일 | 서버 통신 유예를 줄이는 로컬 상한; 0이면 캐시 유예 해제 | 로컬 유예 상한으로 서버가 서명한 기간을 늘릴 수는 없어요. 만료 유예와 통신 장애 유예는 별개이며, 빈 캐시로 최초 활성화를 허용하지 않아요. ### 신뢰하는 실행 모듈로 시험하기 [섹션 제목: “신뢰하는 실행 모듈로 시험하기”](#신뢰하는-실행-모듈로-시험하기) 기본값은 OS에 맞는 모듈을 자동 선택해요. 통제된 기기 시험이나 앱이 직접 제공하는 모듈을 사용할 때는 `offline: { bridgeCommand: string, bridgeArgs?: string[] }`을 지정할 수 있어요. 신뢰하는 앱 배포물이나 시험 환경에서 제공한 실행 파일만 사용하세요. 고객 요청 JSON이나 활성화 응답의 경로를 실행하면 안 돼요. 샘플은 제공된 시험용 래퍼를 SIGNOX\_OFFLINE\_RUNTIME·SIGNOX\_OFFLINE\_EXECUTABLE로 지정할 수 있어요. 이 옵션으로 기기 신뢰와 서명 검증을 우회하지는 않아요. ## 메서드와 반환값 [섹션 제목: “메서드와 반환값”](#메서드와-반환값) | 시그니처 | 반환 타입 | 동작 | | ----------------------------------- | --------------------------------- | ----------------------------------------------------------------- | | `start(name?)` | `Promise<{portalUrl, requestId}>` | 요청을 등록하고 브라우저 주소와 요청 번호를 반환해요. | | `createRequest(name?, renew=false)` | `Promise` | 공개 요청 JSON을 반환해요. name은 기기 표시명, renew는 설치 자격증명을 유지한 새 요청 생성 여부예요. | | `poll()` | `Promise<{status, result?}>` | 승인 전에는 pending, 승인 후에는 응답을 적용한 검증 결과를 반환해요. | | `applyResponse(content)` | `Promise` | 응답 문자열을 읽고 요청·기기 일치를 검증한 뒤 적용해요. | | `validate()` | `Promise` | 라이센스 상태와 현재 기능 권한을 확인해요. | | `deactivate()` | `Promise` | 연결 기반 등록을 해제해요. 보호된 이용권은 이전 검토로 안내해요. | Node는 비동기 메서드예요. Java·C#·Python은 동기 메서드이므로 네트워크·장치 작업을 UI 스레드 밖에서 실행하세요. LicenseResult에는 유효 여부·코드·기능값이 들어 있어요. 요청·상태·가져오기 오류의 예외를 검증 성공으로 바꾸면 안 돼요. ## 실패했을 때 확인하기 [섹션 제목: “실패했을 때 확인하기”](#실패했을-때-확인하기) | 현상·코드 | 원인 | 조치와 재확인 결과 | | ----------------------------- | -------------------------------- | -------------------------------------------------- | | `STATE_INVALID` | 저장한 설치 상태가 없거나 손상됨 | 보존한 상태를 복원하세요. 복구할 수 없으면 새 설치에서 요청을 만들어 이전을 신청하세요. | | `DEVICE_TRUST_REQUIRED` | 검증되지 않은 장비 키에는 포털이 발급할 수 없음 | 운영자가 실제 기기의 키를 별도 신뢰 경로로 대조하고 등록한 뒤 다시 승인하세요. | | `TRANSFER_REQUIRED` | 다른 기기가 등록되어 있거나 보호된 반납을 확인할 수 없음 | 새 기기의 요청으로 이전을 신청하세요. 벤더는 그 요청을 검토해요. | | `ACTIVATION_RESPONSE_INVALID` | 다른 파일·서명·요청·기기 | 원래 기기에서 이 요청에 발급된 응답을 적용하세요. 응답 내용을 수정하지 마세요. | | `REQUEST_EXPIRED` | 요청의 7일 유효기간 경과 | 새 요청을 만들어 고객 승인을 다시 진행하세요. | | `NETWORK_ERROR` | 일시적인 연결·서비스 장애 | 연결을 복구하세요. 기존의 유효한 서명 이용권·캐시만 연결 없이 사용할 수 있어요. | | `REQUEST_ERROR` | 잘못된 주소·입력·접근 권한 | 요청을 수정하세요. 영구 HTTP 오류를 무한 재시도하지 마세요. | 서명된 정지·폐기·만료 거절을 받은 뒤 연결이 끊겨도 거절을 유지해야 해요. 이전 오프라인 이용권으로 덮지 마세요. 계속 연결되지 않은 기기는 즉시 원격 회수 신호를 받을 수 없으며, 포맷이나 파일 삭제만으로 옛 백업이 중단됐다고 증명할 수 없어요. [통합 검증 실행하기](/guides/integration-checks/) · [기기 복구·이전 알아보기](/guides/hwid/) 응답은 받았지만 로컬 검증에 실패하면 polling의 status는 application\_failed이고 result는 무효예요. applied와 유효한 result를 함께 확인해야 적용 성공이에요. 요청 생성과 적용에 같은 실행 모듈 설정을 사용하세요. 브라우저·파일 전달 방식은 기기 보호 유무를 선택하는 옵션이 아니고, 요청 이름을 바꾸어도 공급자가 바뀌지 않아요. 검증된 응답에서 OFFLINE\_NOT\_SUPPORTED가 나오면 필요한 로컬 실행 모듈을 사용할 수 없는 상태이므로 기존 환경을 복원하세요. 무조건 새 신뢰 등록을 요청하는 오류는 아니에요. # Python SDK > SDK 설치부터 고객 승인과 앱 기능 권한 확인까지 연결해요 `ActivationClient`로 Python 앱에 라이센스 활성화를 연결해요. SDK를 설치하고 고객 포털에서 기기를 승인한 뒤, 앱에서 기능을 사용할 수 있는지 확인하는 과정을 알아보세요. 활성화 요청은 승인할 앱 설치를 식별하는 정보예요. 고객이 해당 앱의 이름과 로고가 표시된 포털에서 소유한 라이센스를 선택하면, 그 요청과 기기에 묶인 서명된 활성화 응답을 받아요. 라이센스 하나에는 활성 기기 하나만 등록돼요. ## 1. SDK를 설치하세요 [섹션 제목: “1. SDK를 설치하세요”](#1-sdk를-설치하세요) ```bash python3 -m venv .venv .venv/bin/python -m pip install signox-sdk==0.3.1 ``` 개발 환경에 설정된 패키지 저장소를 사용하세요. SDK는 앱의 의존성으로 설치해요. 샘플 ZIP에는 실행 예제 소스가 들어 있으며, SDK 설치를 대신하지 않아요. 실행 환경: Python 3.9+, cryptography 의존성은 pip가 설치. 방금 만든 가상환경에 설치된 SDK 버전을 확인하세요. 출력의 Version이 0.3.1이어야 해요. 이후 앱도 같은 가상환경의 Python으로 실행하세요. 다른 Python 실행기에는 구버전 SDK가 설치되어 있을 수 있어요. ```bash .venv/bin/python -m pip show signox-sdk ``` ## 2. 제품과 테스트 라이센스를 준비하세요 [섹션 제목: “2. 제품과 테스트 라이센스를 준비하세요”](#2-제품과-테스트-라이센스를-준비하세요) 벤더 대시보드에서 제품을 선택하고 설정을 여세요. 제품 상세 주소의 UUID를 확인하고 그 제품의 공개키를 내려받으세요. 정책에 demo\_export 불리언 기능을 추가해 켠 뒤 테스트 라이센스를 발급하고, 고객 포털에서 테스트 고객에게 구매 키를 등록하세요. | 입력 | 얻는 위치와 사용 방법 | | ------------------------ | -------------------------------------------- | | `SIGNOX_API_URL` | 테스트할 환경의 API 주소를 사용해요. 포털 주소와는 달라요. | | `SIGNOX_PRODUCT_ID` | 벤더의 제품 상세 주소에 있는 UUID예요. | | `SIGNOX_PUBLIC_KEY_FILE` | 내려받은 제품 공개키 파일의 경로예요. PEM 내용 자체를 변수에 넣지 않아요. | | `.signox-state` | 예제가 만드는 개인 상태 디렉터리예요. 재시작 후에도 같은 경로를 유지하세요. | 예제를 실행할 셸에 위 세 환경변수를 설정하세요. 제품 공개키는 신뢰하는 앱 배포물에 포함해 사용하세요. 고객 비밀번호는 고객 포털에만 입력하고, 라이센스 키는 앱에 하드코딩하지 않아요. ## 3. 활성화 예제를 작성하세요 [섹션 제목: “3. 활성화 예제를 작성하세요”](#3-활성화-예제를-작성하세요) 아래 전체 코드를 `app.py` 파일로 저장하세요. 모든 명령은 같은 프로젝트 디렉터리에서 실행하세요. ```python import json import os from pathlib import Path import sys from signox import ActivationClient required = ('SIGNOX_API_URL', 'SIGNOX_PRODUCT_ID', 'SIGNOX_PUBLIC_KEY_FILE') if not all(os.getenv(k) for k in required): raise ValueError('Set SIGNOX_API_URL, SIGNOX_PRODUCT_ID and SIGNOX_PUBLIC_KEY_FILE') client = ActivationClient(os.environ['SIGNOX_PRODUCT_ID'], Path(os.environ['SIGNOX_PUBLIC_KEY_FILE']).read_text(), base_url=os.environ['SIGNOX_API_URL'], state_dir='.signox-state') command = sys.argv[1] if len(sys.argv) > 1 else 'start' if command == 'start': print(json.dumps(client.start())) elif command == 'request': print(client.create_request()) elif command == 'poll': progress = client.poll() print(progress['status']) if progress.get('result') and not progress['result'].valid: sys.exit(2) else: result = client.apply_response(Path(sys.argv[2]).read_text()) if command == 'apply' else client.validate() allowed = result.valid and result.features.get('demo_export') is True print(json.dumps(dict(valid=result.valid, code=str(result.code), exportAllowed=allowed))) sys.exit(0 if result.valid else 2) ``` 브라우저 승인과 파일 전달 모두 같은 클라이언트를 사용해요. SDK가 실행 환경의 장치 모듈을 선택하므로 앱에서 장치 공급자 종류를 고르지 않아요. ## 4. 고객 포털에서 기기를 승인하세요 [섹션 제목: “4. 고객 포털에서 기기를 승인하세요”](#4-고객-포털에서-기기를-승인하세요) ```bash .venv/bin/python app.py start ``` 출력된 portalUrl을 시스템 브라우저로 여세요. 앱 이름과 기기 이름을 확인한 뒤 고객 계정으로 로그인하고, 소유한 라이센스를 골라 이 기기 활성화를 누르세요. 다른 기기가 등록되어 있다면 사유를 입력해 벤더에게 이전 검토를 요청할 수 있어요. ```bash .venv/bin/python app.py poll ``` 승인 전에는 status가 pending이에요. 승인 후 다시 실행하면 응답을 받아 적용해요. 3초보다 짧은 간격으로 반복 호출하지 마세요. pending은 승인 대기이며 라이센스 검증 성공이 아니에요. ## 5. 기능 실행 전에 권한을 확인하세요 [섹션 제목: “5. 기능 실행 전에 권한을 확인하세요”](#5-기능-실행-전에-권한을-확인하세요) ```bash .venv/bin/python app.py status ``` 성공하면 VALID 또는 IN\_GRACE\_PERIOD가 나와요. 라이센스가 유효하고 demo\_export가 true일 때만 예제의 exportAllowed가 true가 돼요. 기능이 없는 라이센스는 유효하더라도 CSV를 만들면 안 돼요. 이 판정을 시작 화면뿐 아니라 앱의 실제 내보내기 함수에도 넣으세요. [실제 CSV 파일을 만드는 샘플 실행하기](/samples/python/) ## 다른 기기에서 활성화하기 [섹션 제목: “다른 기기에서 활성화하기”](#다른-기기에서-활성화하기) 원래 기기에 연결이 없다면 요청을 내보내고, 연결된 PC의 고객 포털 /activate에서 가져오세요. 승인 후 받은 activation.signox를 원래 기기로 옮겨요. SDK의 applyResponse에는 파일 경로가 아닌 파일 내용을 전달해요. 위 예제는 대신 파일을 읽어 줘요. ```bash .venv/bin/python app.py request .venv/bin/python app.py apply activation.signox ``` 파일로 전달할 때는 언어별 샘플의 activation-request request.signox 명령을 사용하세요. 특히 Maven의 빌드 로그를 요청 파일로 리다이렉션하지 마세요. 연결 없이 응답을 적용하려면 지원되고 신뢰 등록된 기기 키가 필요해요. 보호 기능이 없으면 응답 적용 시 연결해야 해요. 앱을 배포하기 전에 실행 환경과 기기 요구 사항을 확인하세요. [오프라인 실행 환경 확인하기](/guides/activation-offline/) ## 생성자와 옵션 [섹션 제목: “생성자와 옵션”](#생성자와-옵션) | 이름 | 타입 | 필수 여부·기본값 | 설명 | | -------------------- | ----- | --------------------------------------------------- | --------------------------------- | | `product_id` | `str` | 필수 | 제품 상세 주소에 있는 UUID | | `product_public_key` | `str` | 필수 | 앱에 포함한 SPKI PEM 공개키 내용 | | `state_dir` | `str` | 필수 | 이 설치에서만 쓰는 영속 디렉터리 | | `base_url` | `str` | [https://api.signox.kr](https://api.signox.twentyoz.dev) | 대상 환경의 API 주소 | | `timeout_ms` | `int` | 10000 ms | 네트워크 요청 제한 시간 | | `ts_tolerance_sec` | `int` | 300 초 | 서버 시각과의 허용 차이 | | `offline_grace_days` | `int` | 365 일 | 서버 통신 유예를 줄이는 로컬 상한; 0이면 캐시 유예 해제 | 로컬 유예 상한으로 서버가 서명한 기간을 늘릴 수는 없어요. 만료 유예와 통신 장애 유예는 별개이며, 빈 캐시로 최초 활성화를 허용하지 않아요. ### 신뢰하는 실행 모듈로 시험하기 [섹션 제목: “신뢰하는 실행 모듈로 시험하기”](#신뢰하는-실행-모듈로-시험하기) 기본값은 OS에 맞는 모듈을 자동 선택해요. 통제된 기기 시험이나 앱이 직접 제공하는 모듈을 사용할 때는 `bridge_command: str`, `bridge_args: Sequence[str]`을 지정할 수 있어요. 신뢰하는 앱 배포물이나 시험 환경에서 제공한 실행 파일만 사용하세요. 고객 요청 JSON이나 활성화 응답의 경로를 실행하면 안 돼요. 샘플은 제공된 시험용 래퍼를 SIGNOX\_OFFLINE\_RUNTIME·SIGNOX\_OFFLINE\_EXECUTABLE로 지정할 수 있어요. 이 옵션으로 기기 신뢰와 서명 검증을 우회하지는 않아요. ## 메서드와 반환값 [섹션 제목: “메서드와 반환값”](#메서드와-반환값) | 시그니처 | 반환 타입 | 동작 | | ---------------------------------------- | --------------- | ----------------------------------------------------------------- | | `start(name=None)` | `dict` | 요청을 등록하고 브라우저 주소와 요청 번호를 반환해요. | | `create_request(name=None, renew=False)` | `str` | 공개 요청 JSON을 반환해요. name은 기기 표시명, renew는 설치 자격증명을 유지한 새 요청 생성 여부예요. | | `poll()` | `dict` | 승인 전에는 pending, 승인 후에는 응답을 적용한 검증 결과를 반환해요. | | `apply_response(content)` | `LicenseResult` | 응답 문자열을 읽고 요청·기기 일치를 검증한 뒤 적용해요. | | `validate()` | `LicenseResult` | 라이센스 상태와 현재 기능 권한을 확인해요. | | `deactivate()` | `LicenseResult` | 연결 기반 등록을 해제해요. 보호된 이용권은 이전 검토로 안내해요. | Node는 비동기 메서드예요. Java·C#·Python은 동기 메서드이므로 네트워크·장치 작업을 UI 스레드 밖에서 실행하세요. LicenseResult에는 유효 여부·코드·기능값이 들어 있어요. 요청·상태·가져오기 오류의 예외를 검증 성공으로 바꾸면 안 돼요. ## 실패했을 때 확인하기 [섹션 제목: “실패했을 때 확인하기”](#실패했을-때-확인하기) | 현상·코드 | 원인 | 조치와 재확인 결과 | | ----------------------------- | -------------------------------- | -------------------------------------------------- | | `STATE_INVALID` | 저장한 설치 상태가 없거나 손상됨 | 보존한 상태를 복원하세요. 복구할 수 없으면 새 설치에서 요청을 만들어 이전을 신청하세요. | | `DEVICE_TRUST_REQUIRED` | 검증되지 않은 장비 키에는 포털이 발급할 수 없음 | 운영자가 실제 기기의 키를 별도 신뢰 경로로 대조하고 등록한 뒤 다시 승인하세요. | | `TRANSFER_REQUIRED` | 다른 기기가 등록되어 있거나 보호된 반납을 확인할 수 없음 | 새 기기의 요청으로 이전을 신청하세요. 벤더는 그 요청을 검토해요. | | `ACTIVATION_RESPONSE_INVALID` | 다른 파일·서명·요청·기기 | 원래 기기에서 이 요청에 발급된 응답을 적용하세요. 응답 내용을 수정하지 마세요. | | `REQUEST_EXPIRED` | 요청의 7일 유효기간 경과 | 새 요청을 만들어 고객 승인을 다시 진행하세요. | | `NETWORK_ERROR` | 일시적인 연결·서비스 장애 | 연결을 복구하세요. 기존의 유효한 서명 이용권·캐시만 연결 없이 사용할 수 있어요. | | `REQUEST_ERROR` | 잘못된 주소·입력·접근 권한 | 요청을 수정하세요. 영구 HTTP 오류를 무한 재시도하지 마세요. | 서명된 정지·폐기·만료 거절을 받은 뒤 연결이 끊겨도 거절을 유지해야 해요. 이전 오프라인 이용권으로 덮지 마세요. 계속 연결되지 않은 기기는 즉시 원격 회수 신호를 받을 수 없으며, 포맷이나 파일 삭제만으로 옛 백업이 중단됐다고 증명할 수 없어요. [통합 검증 실행하기](/guides/integration-checks/) · [기기 복구·이전 알아보기](/guides/hwid/) 응답은 받았지만 로컬 검증에 실패하면 polling의 status는 application\_failed이고 result는 무효예요. applied와 유효한 result를 함께 확인해야 적용 성공이에요. 요청 생성과 적용에 같은 실행 모듈 설정을 사용하세요. 브라우저·파일 전달 방식은 기기 보호 유무를 선택하는 옵션이 아니고, 요청 이름을 바꾸어도 공급자가 바뀌지 않아요. 검증된 응답에서 OFFLINE\_NOT\_SUPPORTED가 나오면 필요한 로컬 실행 모듈을 사용할 수 없는 상태이므로 기존 환경을 복원하세요. 무조건 새 신뢰 등록을 요청하는 오류는 아니에요. # 설치와 테스트 준비 > 패키지와 제품 정보, 고객 라이센스를 준비해요 첫 활성화를 실행하기 전에 SDK와 제품 정보, 테스트 고객을 준비해요. 각 입력을 어디서 얻고 어떤 정보는 앱이 보관해야 하는지 알아보세요. ## 1. 언어에 맞는 패키지를 설치하세요 [섹션 제목: “1. 언어에 맞는 패키지를 설치하세요”](#1-언어에-맞는-패키지를-설치하세요) | SDK | 패키지 | 설치·실행 안내 | | ------ | ------------------------------------- | -------------------------- | | Node | `@signox/sdk@0.3.1` | [Node SDK](/sdk/node/) | | Java | `kr.signox:signox-sdk:0.3.1-SNAPSHOT` | [Java SDK](/sdk/java/) | | C# | `Signox.Sdk 0.3.1` | [C# SDK](/sdk/csharp/) | | Python | `signox-sdk 0.3.1` | [Python SDK](/sdk/python/) | 앱 프로젝트 디렉터리에서 언어별 가이드의 설치 명령을 실행하세요. 사용하는 환경에 설정된 패키지 저장소를 사용해요. 패키지를 찾지 못하면 앱 코드를 바꾸기 전에 이름·버전·저장소 접근을 확인하세요. SDK 패키지에는 기본 장치 실행 모듈이 들어 있어요. 샘플은 SDK를 의존성으로 사용하는 별도 소스 프로젝트예요. 샘플 ZIP만 내려받으면 SDK까지 설치되는 것은 아니에요. ## 2. 제품 ID와 공개키를 확인하세요 [섹션 제목: “2. 제품 ID와 공개키를 확인하세요”](#2-제품-id와-공개키를-확인하세요) 벤더 대시보드에서 라이센스로 보호할 제품을 여세요. 제품 상세 주소에 있는 UUID가 productId예요. 제품 설정에서 제품 공개키(SPKI PEM)를 복사하거나 내려받고 예제 프로젝트의 product-public-key.pem으로 저장하세요. 공개키는 서버 응답의 서명을 확인하는 데 사용해요. 서버 비밀키는 앱에 포함하면 안 돼요. 신뢰하는 앱 배포물에 올바른 제품 공개키를 고정하세요. 다른 제품의 공개키로는 이 제품의 응답을 검증할 수 없어요. ## 3. 테스트 고객의 라이센스를 준비하세요 [섹션 제목: “3. 테스트 고객의 라이센스를 준비하세요”](#3-테스트-고객의-라이센스를-준비하세요) 제품 정책에 demo\_export 불리언 기능을 추가하고 켜세요. 해당 정책으로 테스트 라이센스를 발급한 뒤 고객 포털에서 테스트 고객 계정에 구매 키를 등록하세요. 앱은 승인 과정에서 고객이 이 라이센스를 선택하도록 안내해요. 기능 거부도 확인하려면 demo\_export가 없는 유효한 라이센스를 하나 더 준비하세요. 벤더 계정은 제품·정책을 관리하고 고객 계정은 라이센스를 소유·승인해요. 두 계정은 로그인 영역이 달라요. ## 4. 예제 입력을 설정하세요 [섹션 제목: “4. 예제 입력을 설정하세요”](#4-예제-입력을-설정하세요) | 입력 | 설정할 값 | | ------------------------ | ------------------------------------------ | | `SIGNOX_API_URL` | 대상 환경의 API 주소예요. 포털 주소를 넣지 마세요. | | `SIGNOX_PRODUCT_ID` | 2단계에서 확인한 제품 UUID예요. | | `SIGNOX_PUBLIC_KEY_FILE` | product-public-key.pem 파일의 경로예요. | | `SIGNOX_STATE_DIR` | 이 설치만 사용하는 쓰기 가능한 영속 디렉터리예요. 전체 샘플에서 사용해요. | 앱을 실행할 셸이나 서비스 설정에 값을 넣으세요. SDK의 단일 파일 예제는 .signox-state를 사용해요. 앱 재시작·컨테이너 재생성 후에도 같은 위치를 유지하세요. 이미 생성한 상태 디렉터리를 공통 이미지나 소스 저장소에 넣지 마세요. ## 5. 첫 활성화를 실행하세요 [섹션 제목: “5. 첫 활성화를 실행하세요”](#5-첫-활성화를-실행하세요) 빠른 시작에서 start → 고객 브라우저 승인 → poll → 기능 검증을 연결하세요. 연결되지 않은 기기를 시험하려면 지원 실행 환경과 운영자의 기기 신뢰 등록도 먼저 준비해야 해요. [첫 고객 활성화 실행하기](/start/quickstart/) · [오프라인 실행 환경 확인하기](/guides/activation-offline/) # LLM Quick Reference > Activation flow, decision defaults, invariants, scenarios and common mistakes Use the installed Signox SDK 0.3.1 (`0.3.1-SNAPSHOT` for Java). Read the language guide for exact signatures. The public entry point for new integrations is `ActivationClient`. ## 1. Workflow model [섹션 제목: “1. Workflow model”](#1-workflow-model) App creates a public request → customer portal resolves app branding → customer signs in and selects an owned license → explicit device approval → server reserves the registration and issues a signed response → app verifies/applies it → actual feature gate. Automatic transport: `start` returns a portal URL, the host opens a system browser, and `poll` receives the result. Manual transport: export request text/file, import at customer portal `/activate` on a connected computer, carry `activation.signox` back, and apply its contents. These transports use the same ownership and one-device policy. ## 2. Decision defaults [섹션 제목: “2. Decision defaults”](#2-decision-defaults) * Use the SDK matching the app language. Do not implement signature checks or hardware enumeration in application code. * Let the SDK select the device provider. Do not ask an end user to choose TPM versus another device technology. * Use the provided evaluation API URL. Default SDK URLs do not prove that a test environment exists there. * Ask once for missing real inputs. Do not invent keys, replace the SDK with a mock, or silently use a new state directory after corruption. * An offline grant requires supported and explicitly trusted device protection. A machine without it must connect to apply its initial approval; network grace cannot bootstrap an empty installation. ## 3. Inputs and invariants [섹션 제목: “3. Inputs and invariants”](#3-inputs-and-invariants) | Input | Meaning | | ---------------------------------- | ---------------------------------------------------------------------------------------- | | Product UUID | Product ID from the vendor dashboard URL. | | Product SPKI PEM public key | Pinned in the trusted app distribution; not fetched from an untrusted request. | | API base URL | Supplied evaluation or production endpoint. | | Private persistent state directory | Unique to the installation; preserve through restarts and updates. | | Test customer/license | Owned or claimed in the customer portal; never embed a customer password in the SDK app. | | Feature code | A concrete permission such as `demo_export`. | One license has exactly one active server registration. Issuance reserves the device; it is not proof of application. The customer’s realm is separate from vendor and administrator realms. Product branding comes from server data and does not substitute for ownership checks. Only `VALID` and `IN_GRACE_PERIOD` permit use. Require `valid` AND the relevant feature value inside the real operation. A valid license without the CSV feature must not write a file. ## 4. SDK entry points [섹션 제목: “4. SDK entry points”](#4-sdk-entry-points) | Language | Construction | Operations | | -------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | Node | `new ActivationClient({productId, productPublicKey, baseUrl, stateDir})` | async `start`, `createRequest`, `poll`, `applyResponse`, `validate`, `deactivate` | | Java | `new ActivationClient(productId, config, File stateDir)` | synchronous camelCase operations with the same names | | C# | `new ActivationClient(productId, options, stateDir)` | synchronous `Start`, `CreateRequest`, `Poll`, `ApplyResponse`, `Validate`, `Deactivate` | | Python | `ActivationClient(product_id, product_public_key, state_dir=..., base_url=...)` | synchronous `start`, `create_request`, `poll`, `apply_response`, `validate`, `deactivate` | Import receives response TEXT, not a file path. A create-request method returns public JSON; keep private credentials app-side. Poll at intervals of at least 3 seconds. `pending` is not success. `application_failed` means a response arrived but validation did not succeed; inspect its result. `applied` plus a valid result confirms application. ## 5. Scenario entry points [섹션 제목: “5. Scenario entry points”](#5-scenario-entry-points) | Scenario | Sequence | Documentation | | -------------------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------- | | First connected activation | start → customer browser approval → poll → validate → feature | `/guides/activation-online/` | | Disconnected device | createRequest → portal file import/approval → applyResponse(text) → feature | `/guides/activation-offline/` | | Expired request | create a renewed request using the preserved identity; repeat approval | language SDK guide | | Restart/update | reuse the same private state | `/guides/hwid/` | | Same protected identity | request recovery; do not allocate a second active target | `/guides/hwid/` | | New device/lost unrecoverable identity | customer reason → vendor review of the exact destination request | `/guides/activation-online/` | | Real app acceptance | run the language sample; verify actual CSV write and refusal | `/samples/{language}/` and `/guides/integration-checks/` | ## 6. Device protection and recovery [섹션 제목: “6. Device protection and recovery”](#6-device-protection-and-recovery) Linux uses the TPM 2.0 runtime. Windows uses the Microsoft Platform Crypto Provider; macOS uses a Secure Enclave key. Each has a separate provider implementation under the common SDK entry point. Check the runtime matrix and recorded verification scope before claiming OS/device support. Software RSA/ECDH fixtures prove wire interoperability, not hardware origin. An operator must verify a public key on the real device through a trusted channel before enrollment. A provider label in customer JSON is not attestation. No automatic manufacturer-certificate validation or USB provider is supplied. Windows recovery requires the platform key to remain accessible. macOS retains only Secure Enclave-wrapped signing/agreement handles in private state. Losing those handles or a full format may require reviewed transfer. Do not infer recoverability merely from a matching machine name. The Linux trusted hardware anchor can recover a registration with a new local AK. ## 7. Failures and grace [섹션 제목: “7. Failures and grace”](#7-failures-and-grace) * `STATE_INVALID`: restore state; do not silently replace installation credentials. * `DEVICE_TRUST_REQUIRED`: operator enrollment is needed before the portal can issue. * `TRANSFER_REQUIRED`: request review from the destination device; never clear a protected slot automatically. * `ACTIVATION_RESPONSE_INVALID`: wrong product/request/device, corrupt envelope or invalid signature. Deny use. * `REQUEST_EXPIRED`: request lifetime is seven days; renew the request. * `NETWORK_ERROR`: temporary transport/service failure. Only previously valid signed rights may cover disconnection. * `REQUEST_ERROR`: permanent HTTP/input/access failure. Correct it instead of endless retry. * `SUSPENDED`, `REVOKED`, `EXPIRED`, signature or nonce failure: do not override with an old grant. Retain signed denial across subsequent connection failures. Network grace follows signed `networkGraceDays`, capped by the SDK’s local limit. Expiry grace uses `gracePeriodDays`. Neither supplies new permanent offline rights. ## 8. Common mistakes [섹션 제목: “8. Common mistakes”](#8-common-mistakes) Do not treat request registration, response issuance, receipt confirmation and active use as the same event. Do not export the state directory, raw retrieval secret, decrypted wrapping key or customer credentials. Do not claim that deleting a license file or formatting a machine proves secure return. Protected/offline grants cannot be immediately revoked while permanently disconnected. Software clock/denial records do not resist full state rollback. Vendor-approved replacement records this uncertainty; it does not prove zero overlapping physical use. ## 9. Verification and documentation routing [섹션 제목: “9. Verification and documentation routing”](#9-verification-and-documentation-routing) Use real packages and real app operations. Test valid and feature-denied licenses, altered response, restart, replacement review, wrong app and explicit denial followed by disconnection. Record command, exit code, expected/observed result and local evidence. Mark unavailable hardware and unexecuted checks `not_run`; approval waits are `pending`. Preserve the initial attempt, collect Q\&A in a separate local file, and propose improvements after the independent test. Do not call a mock or a test fixture a real device result. Start at `/start/install/`, then `/sdk/{node|java|csharp|python}/`. Prefix `/en` for English. The documentation MCP provides `list_docs({locale?})` and `read_doc({slug,locale?})`, default locale `ko`. `llms.txt` lists pages; replace a page’s final `/` with `.md` for source. The page title includes a Markdown copy action. # 개요 > 개요 Signox는 고객이 소유한 라이센스로 앱 기능을 제어해요. 개발자는 제품과 정책을 만들고 SDK를 넣은 뒤 실제 기능을 실행할 때 반환된 권한을 확인해요. 라이센스 하나에는 활성 기기 하나만 등록돼요. 지원하는 네 언어 모두 ActivationClient를 사용해요. 고객은 앱 브랜드가 표시된 고객 포털에서 기기를 승인하거나, 요청과 응답을 다른 PC로 옮겨 승인해요. 같은 등록을 전달하는 두 방법이며 별도 라이센스 상품을 선택하는 과정이 아니에요. 기기 보호 기능은 SDK가 감지해요. | 용어 | 뜻 | | ------------------- | ----------------------------------------- | | Product | 보호할 앱과 그 앱의 서명 키 | | Policy | 이용기간·기능·만료 유예·통신 유예를 정해요. 기기 수는 1개로 고정돼요. | | Request | 포털로 전달하는 공개 기기 요청 | | Activation response | 해당 요청과 기기에 묶인 서명된 응답 | | State directory | 앱이 보관하는 개인 설치 자격증명 | [설치](/start/install/) → [첫 활성화](/start/quickstart/) → [연동 검증](/guides/integration-checks/) [Node](/sdk/node/) · [Java](/sdk/java/) · [C#](/sdk/csharp/) · [Python](/sdk/python/) # 첫 고객 활성화 > 설치한 샘플로 고객 승인부터 실제 CSV 생성까지 확인해요 Node 샘플로 첫 고객 활성화를 완료하고 실제 CSV 파일을 만들어 봐요. 다른 언어를 사용한다면 해당 샘플에서도 같은 순서로 진행할 수 있어요. [Java](/samples/java/) · [C#](/samples/csharp/) · [Python](/samples/python/) ## 1. 샘플을 설치하고 입력을 준비하세요 [섹션 제목: “1. 샘플을 설치하고 입력을 준비하세요”](#1-샘플을-설치하고-입력을-준비하세요) Node 샘플을 내려받아 압축을 풀고 그 디렉터리에서 npm install을 실행하세요. 설치 안내에 따라 제품 UUID·공개키·API 주소와 고객이 소유한 라이센스를 준비하세요. [Node 샘플 다운로드와 설정](/samples/node/) · [테스트 입력 준비](/start/install/) ## 2. 고객 승인 화면을 여세요 [섹션 제목: “2. 고객 승인 화면을 여세요”](#2-고객-승인-화면을-여세요) ```bash node main.mjs activation-start ``` 출력된 portalUrl을 시스템 브라우저로 여세요. 앱 이름과 기기 이름을 확인하고 고객 계정으로 로그인한 뒤 라이센스를 선택해 확인창에서 승인하세요. 앱은 고객 비밀번호를 수집하지 않아요. ## 3. 승인 결과를 받으세요 [섹션 제목: “3. 승인 결과를 받으세요”](#3-승인-결과를-받으세요) ```bash node main.mjs activation-poll ``` 승인 전에는 pending이 나와요. 승인 후에는 SDK가 서명된 응답을 받아 적용해요. status=applied이고 result가 유효해야 적용 성공이에요. application\_failed라면 오류를 확인해 적용 단계를 해결하세요. ## 4. 실제 기능을 확인하세요 [섹션 제목: “4. 실제 기능을 확인하세요”](#4-실제-기능을-확인하세요) ```bash node main.mjs activation-status node main.mjs activation-export output.csv ``` 라이센스가 유효하고 demo\_export가 true여야 해요. 성공하면 output.csv에 아래 예제 행이 생겨요. 기능이 없는 유효한 라이센스에서는 내보내기가 종료 코드 3으로 끝나고 새 파일을 만들지 않아야 해요. ```text sensor,value demo-a,21.5 demo-b,24.0 ``` ## 앱에 연결이 없을 때 [섹션 제목: “앱에 연결이 없을 때”](#앱에-연결이-없을-때) ```bash node main.mjs activation-request request.signox ``` request.signox를 연결된 PC로 옮겨 고객 포털 /activate에서 가져오세요. 승인 후 activation.signox를 내려받아 원래 기기로 옮기세요. ```bash node main.mjs activation-apply activation.signox ``` 지원되는 기기 보호 기능과 신뢰 등록이 필요해요. 통신 유예는 이미 유효한 권한을 받은 설치에만 적용되며 최초 활성화를 대신하지 않아요. [실패·기기 교체까지 검증하기](/guides/integration-checks/) · [복구 절차 알아보기](/guides/hwid/) # AI 도구로 연동하기 > MCP 서버와 llms.txt, LLM Quick Reference로 AI 도구에 Signox 문서를 연결해요 Claude Code·Codex·Cursor 같은 AI 도구로 Signox 라이센스를 연동하는 방법이에요. 문서를 AI에 연결하고, 고객 포털 활성화와 기능 권한 검증을 실제 앱에 적용하는 방법을 알아보세요. AI가 올바른 코드를 만들려면 SDK 이름뿐 아니라 사용하는 버전, 필요한 입력과 성공 조건도 알아야 해요. Signox가 제공하는 자원을 연결하면 메서드나 요청 필드를 추측하는 일을 줄일 수 있어요. ## AI를 위한 Signox 자원 [섹션 제목: “AI를 위한 Signox 자원”](#ai를-위한-signox-자원) Signox는 AI가 문서를 찾고 읽을 수 있도록 세 가지 자원을 제공해요. | 자원 | 역할 | 언제 사용하나요? | | -------------------------------------------- | ----------------------------------- | ------------------------------------ | | [LLM Quick Reference](/start/llm-reference/) | 활성화 흐름·기본 판단 규칙·흔한 실수를 한 페이지에 정리해요. | 새 앱 연동을 시작할 때 먼저 전달하세요. | | MCP 서버 | AI가 문서 목록을 검색하고 필요한 원문을 읽게 해요. | 메서드와 오류의 자세한 설명이 필요할 때 사용하세요. | | [llms.txt](/llms.txt) | 문서의 구조와 페이지 경로를 알려주는 인덱스예요. | MCP 없이 문서를 찾아가거나 문서 소스를 등록할 때 사용하세요. | ## LLM Quick Reference 활용하기 [섹션 제목: “LLM Quick Reference 활용하기”](#llm-quick-reference-활용하기) [LLM Quick Reference](/start/llm-reference/)에는 `ActivationClient`의 동작, 고객 승인 흐름, 필수 입력과 잘못 쓰기 쉬운 패턴이 있어요. 짧은 영문 계약으로 제공하며, 상세 설명은 해당 페이지에서 연결한 가이드를 읽을 수 있어요. AI 채팅에 페이지 주소를 전달하고 다음처럼 요청하세요. ```text Signox LLM Quick Reference와 Node SDK 가이드를 읽고, 고객이 브라우저로 로그인해 기기를 활성화하는 앱을 만들어줘. SDK 버전은 0.3.1이야. 문서에 없는 메서드나 필드는 만들지 말아줘. ``` ## MCP 서버 연결하기 [섹션 제목: “MCP 서버 연결하기”](#mcp-서버-연결하기) MCP(Model Context Protocol)는 AI 도구가 외부 자료와 기능을 사용할 수 있게 연결하는 규약이에요. Signox 문서 MCP는 문서를 조회하는 서버이며 라이센스를 발급하거나 고객 대신 승인하지 않아요. 문서 MCP 주소는 `https://api.signox.kr/mcp`예요. 별도 테스트 환경을 사용한다면 제공받은 API 주소에 `/mcp`를 붙이세요. SDK API 주소와 고객 포털 주소는 서로 다르므로 혼동하지 마세요. ### 터미널 명령으로 연결하기 [섹션 제목: “터미널 명령으로 연결하기”](#터미널-명령으로-연결하기) 사용하는 AI 도구에 맞는 명령 한 개를 실행하세요. ```bash # Codex codex mcp add signox-docs --url https://api.signox.kr/mcp # Claude Code claude mcp add --transport http signox-docs https://api.signox.kr/mcp ``` 연결을 추가한 뒤 새 AI 세션에서 문서 도구가 표시되는지 확인하세요. Codex에서는 `codex mcp list`로 등록 목록을 볼 수 있어요. 자세한 설정은 [Codex MCP 안내](https://developers.openai.com/codex/mcp)와 [Claude Code MCP 안내](https://code.claude.com/docs/en/mcp)를 참고하세요. ### 설정 파일로 연결하기 [섹션 제목: “설정 파일로 연결하기”](#설정-파일로-연결하기) Codex의 프로젝트 설정 `.codex/config.toml`에는 다음 내용을 추가해요. 프로젝트 설정은 신뢰한 프로젝트에 적용돼요. ```toml [mcp_servers.signox-docs] url = "https://api.signox.kr/mcp" ``` Claude Code의 `.mcp.json` 또는 Cursor의 `.cursor/mcp.json`에서는 다음 형식을 사용해요. ```json { "mcpServers": { "signox-docs": { "type": "http", "url": "https://api.signox.kr/mcp" } } } ``` VS Code의 `.vscode/mcp.json`은 최상위 키가 `servers`예요. 사용하는 도구의 설정 형식을 확인하세요. ```json { "servers": { "signox-docs": { "type": "http", "url": "https://api.signox.kr/mcp" } } } ``` ### 문서 도구가 연결됐는지 확인하기 [섹션 제목: “문서 도구가 연결됐는지 확인하기”](#문서-도구가-연결됐는지-확인하기) AI에 “Signox 문서 MCP로 Node SDK 가이드를 읽어줘”라고 요청해보세요. | 도구 | 인자 | 동작 | | ----------- | --------------------------------------------------------------- | ------------------------------ | | `list_docs` | \`locale?: “ko" | "en”\`, 생략하면 한국어. 검색어 인자는 없어요. | | `read_doc` | `slug: string`에 `sdk/node` 같은 경로를 넣어요. `locale`은 선택이며 기본 한국어예요. | 선택한 문서의 전체 원문을 읽어요. | 연결이 되지 않으면 API 주소에 `/mcp`가 붙었는지, 대상 환경에 접속할 수 있는지, 설정 파일의 최상위 키가 도구와 맞는지 확인하세요. 설정을 바꾼 뒤에도 도구가 보이지 않으면 새 세션에서 다시 확인하세요. AI가 문서를 사용하지 않는다면 MCP로 조회해달라고 명시적으로 요청하세요. ## llms.txt 활용하기 [섹션 제목: “llms.txt 활용하기”](#llmstxt-활용하기) [llms.txt](/llms.txt)는 Signox 문서 목록을 AI가 읽기 좋은 형태로 제공해요. [llms-full.txt](/llms-full.txt)에는 문서 본문도 함께 들어 있어요. 처음부터 전체 문서를 넣기보다 인덱스에서 필요한 언어와 흐름을 찾아 읽게 하세요. 입력 길이를 줄이면서 현재 작업에 필요한 설명을 전달할 수 있어요. ```text 이 Wiki의 llms.txt에서 Java SDK와 기기 이전 문서를 찾아 읽어줘. 같은 기기 복구와 다른 기기 이전의 차이를 확인한 뒤 구현해줘. ``` ## AI에게 연동을 요청하기 [섹션 제목: “AI에게 연동을 요청하기”](#ai에게-연동을-요청하기) 다음 입력을 준비해 AI에 전달하세요. 비밀번호와 토큰을 채팅에 붙여넣지 말고 필요한 경우 비공개 입력 파일의 위치만 알려주세요. * 사용하는 언어와 SDK 버전. * 테스트 API 주소, 제품 UUID, 제품 공개키 파일 경로. * 설치마다 따로 보존할 상태 디렉터리. * 테스트 고객의 라이센스 준비 여부와 검증할 기능 코드. * 실제 오프라인 시험에 사용할 OS와 보호 기기 접근 방법. ### 작업 프롬프트 예시 [섹션 제목: “작업 프롬프트 예시”](#작업-프롬프트-예시) ```text Signox 0.3.1 SDK를 사용해 CSV 내보내기 앱에 라이센스를 붙여줘. LLM Quick Reference → 해당 언어 SDK → 샘플 → 활성화 가이드 순서로 읽어줘. 필요한 환경 입력은 제공한 비공개 파일에서 확인하고, 누락된 입력은 질문해줘. ActivationClient로 브라우저 승인과 요청·응답 파일 전달을 연결해줘. 고객 비밀번호는 앱이 받지 않고 고객 포털에서만 입력하게 해줘. valid와 demo_export가 모두 참일 때만 실제 CSV 파일을 생성해줘. 같은 상태로 재시작, 다른 기기 이전, 변조 응답, 기능 없는 라이센스, 서버의 명시적 거절을 받은 뒤 연결이 끊긴 경우를 검증해줘. 명령·종료 코드·예상 결과·관찰 결과·증거 경로를 기록해줘. 승인 대기·장비 미지원·미실행은 통과로 표시하지 말아줘. 질문과 답변은 별도 파일에 모으고, 최초 결과를 보존한 뒤 개선점을 제안해줘. ``` ### 문제를 물어보는 예시 [섹션 제목: “문제를 물어보는 예시”](#문제를-물어보는-예시) * “`STATE_INVALID`가 나와요. 상태 디렉터리를 바꾸었는지 확인하고 복구 절차를 알려줘.” * “새 기기에서 `TRANSFER_REQUIRED`가 나와요. 고객 요청과 벤더 승인 순서를 설명해줘.” * “라이센스는 유효한데 `demo_export`가 false예요. 정책과 실제 CSV 제어를 확인해줘.” ## 페이지 원문을 전달하기 [섹션 제목: “페이지 원문을 전달하기”](#페이지-원문을-전달하기) 페이지 제목 아래 **Markdown으로 복사** 버튼을 누르면 현재 문서의 원문을 복사할 수 있어요. 복사 권한이 없으면 **Markdown 원문** 링크를 여세요. 모든 Wiki 페이지는 마지막 `/`를 `.md`로 바꾸면 Markdown 원문을 읽을 수 있어요. 예를 들어 `/sdk/node/`의 원문은 [/sdk/node.md](/sdk/node.md)예요. 필요한 페이지의 원문을 AI에 전달하면 메뉴나 화면 코드를 제외한 설명을 읽힐 수 있어요. AI가 만든 코드도 실제 SDK로 실행해 확인해야 해요. [통합 검증 가이드](/guides/integration-checks/)에서 성공·실패 결과와 실제 기능을 함께 확인하세요.