애플리케이션 인수인계

인증된 서버 요청 하나를 전용 바우치 묶음, 새로운 네이티브 게이트, 멱등 로컬 would-act 레코드에 묶되 외부 행동은 수행하지 않습니다.

이 예제가 무엇인지 먼저 알기

handoff/would-act.mjs는 작은 정보성 통합 예제입니다. SDK, 오케스트레이터, 기능 권한 서비스, 인증 시스템, 트랜잭션 관리자, 외부 행동 어댑터가 아닙니다. Node 내장 모듈만 가져오고 호출자가 건넨 절대 경로의 네이티브 실행 파일만 실행합니다.

유일한 영속 효과는 로컬 would_act 레코드입니다. 이 호출이 정확한 새로운 게이트에 도달했고 외부 행동은 수행하지 않았다고 기록합니다.

서버 문맥을 만들기 전에 인증하기

바깥 애플리케이션은 서버 문맥 파일을 만들기 전에 호출자를 인증해야 합니다. 예제는 비밀번호, 토큰, 쿠키, 인증서, 세션을 검사하지 않습니다.

인증이 끝난 뒤 애플리케이션은 다음 사실을 모두 소유합니다.

  • 인증된 행위자
  • 허용된 행위자 집합
  • 허용된 행동 집합
  • 신뢰할 현재 Unix 시각
  • 요청 ID와 저장 규칙
  • 서버가 정한 만료
  • 업무 사실인 daysopened

클라이언트가 요청에 행위자 이름을 적는 것만으로 그 행위자가 인증되지는 않습니다. 신뢰할 현재 시각을 고르거나 자기 만료를 늘릴 수도 없습니다.

요청 데이터와 신뢰 문맥 분리하기

요청 레코드는 닫힌 스키마 lispex.example-refund-request/v1을 사용합니다.

JSON
{
  "action": "refund",
  "actor": "operator-7",
  "days": 14,
  "expires_at_unix_seconds": 1785600000,
  "opened": false,
  "request_id": "request-123",
  "schema": "lispex.example-refund-request/v1"
}

독립적으로 신뢰한 서버 문맥은 닫힌 스키마 lispex.example-refund-server-context/v1을 사용합니다.

JSON
{
  "allowed_actions": [
    "refund"
  ],
  "allowed_actors": [
    "operator-7"
  ],
  "authenticated_actor": "operator-7",
  "now_unix_seconds": 1785599900,
  "schema": "lispex.example-refund-server-context/v1"
}

첫 블록은 ../refund-work/app/request.json에 저장하고 두 번째 블록은 ../refund-work/app/server-context.json에 저장하세요. 두 파일 모두 정확한 공백 두 칸 들여쓰기와 마지막 줄바꿈 하나를 유지해야 합니다.

행위자는 인증된 행위자와 바이트 단위로 같고 행위자 허용 목록에도 있어야 합니다. 행동은 행동 허용 목록에 있어야 합니다. 만료 시각은 신뢰할 현재 시각보다 뒤에 있어야 합니다.

두 파일은 이미 닫힌 정규 JSON이어야 합니다. 알 수 없는 키와 중복 키, 잘못된 UTF-8, 제어 문자, 안전하지 않은 숫자, 크기 초과, 뒤따르는 바이트는 네이티브가 시작되기 전에 거부됩니다. 문자열은 유니코드 정규화나 대소문자 변환을 하지 않고 허용한 스칼라 바이트를 그대로 보존합니다.

현재 요청 값 여섯 개를 모두 묶기

애플리케이션은 승인한 요청과 서버 문맥에서 정확한 검사 입력 하나를 다시 만듭니다.

JSON
{
  "input": "csk.checked-input/v1",
  "value": [
    14,
    false,
    "request-123",
    "operator-7",
    "refund",
    1785600000
  ]
}

값의 순서는 days, opened, request_id, actor, action, expires_at_unix_seconds입니다. 공백 두 칸 들여쓰기와 마지막 줄바꿈 하나를 포함한 정확한 정규 바이트를 CURRENT_REQUEST_CHECKED_JSON으로 저장하세요.

워크스페이스에 보존된 [14, false] 입력과 그 입력으로 발행한 묶음은 따라 하기 전용입니다. 여기서 다시 쓸 수 없습니다. 인수인계에는 여섯 값을 모두 서명 문맥에 묶은 전용 묶음이 필요합니다.

전용 묶음을 별도로 발행하기

발급자는 수신자 애플리케이션이 인수인계를 실행하기 전에 현재 요청 묶음을 만듭니다. 이 명령은 인수인계와 별개이며 워크스페이스와 애플리케이션 작업 루트 밖에서 발급자가 소유하는 키를 사용합니다.

SH
mkdir -p ../refund-work/app
ISSUER_KEY_URI="pkcs8-file:///absolute/path/outside-workspace/refund-issuer/key/private.pkcs8.der"
lispex vouch issue \
  --source-image ./generated/vouch/refund-window-native.lspx.png \
  --input ../refund-work/app/current-request.checked.json \
  --profile csk.checked-profile/v1 \
  --key-handle "$ISSUER_KEY_URI" \
  --out-dir ../refund-work/app/current-request-issued \
  --emit-bundle

발행은 애플리케이션 호출자를 인증하지 않고 요청한 행동 권한도 주지 않습니다. 수신자는 독립적으로 검토한 정책, 이미지, 검사 입력을 여전히 새로운 게이트에 제공해야 합니다.

로컬 원장 이름 공간 준비하기

호출자가 소유하는 실제 디렉터리 하나를 만들고 그 바로 아래에 실제 디렉터리 두 개를 미리 만드세요. 호출이 끝날 때까지 이 이름 공간을 안정적으로 유지해야 합니다.

SH
mkdir -p ../refund-work/app/request-ledger
mkdir -p ../refund-work/app/request-staging

예제는 두 하위 디렉터리를 암묵적으로 만들지 않습니다. 고정 형제 경로 .would-act-ledger-admission.lock을 원장 전체 배타 잠금으로 사용합니다. 이미 잠금이 있거나 스테이징 디렉터리가 비어 있지 않으면 운영자 검토가 필요하며 거부됩니다. 오래된 것처럼 보여도 잠금을 자동 회수하지 않습니다.

정확한 플래그 여덟 개로 인수인계 실행하기

워크스페이스 루트에서 모든 플래그를 정확히 한 번씩 건네세요. 네이티브 경로는 절대 경로여야 합니다. 다른 경로는 호출 디렉터리를 기준으로 한 번만 해석됩니다.

SH
node handoff/would-act.mjs \
  --native-bin /absolute/path/to/lispex \
  --bundle ../refund-work/app/current-request-issued/vouch-input-bundle.json \
  --trust-policy ../refund-work/vouch/policy.json \
  --source-image ./generated/vouch/refund-window-native.lspx.png \
  --checked-input ../refund-work/app/current-request.checked.json \
  --request-record ../refund-work/app/request.json \
  --server-context ../refund-work/app/server-context.json \
  --app-work-dir ../refund-work/app

자식 호출은 lispex vouch gate로 고정됩니다. 묶음, 정책, 이미지, 여섯 값 검사 입력, 정확한 검사 프로필, 요구 결정 approve, 호출자가 소유하는 새 보고서 경로를 모두 고정합니다. 셸 없이 인자 배열을 사용하고 최소한으로 닫힌 환경, 30초 제한 시간, 출력 크기 한도를 적용합니다. PATH에서 실행 파일을 찾지 않습니다. 자식 표준 출력과 표준 오류도 전달하지 않습니다.

정확한 로컬 레코드 읽기

승인된 새로운 게이트만 공개 단계에 도달할 수 있습니다. 최종 파일 이름은 원시 UTF-8 요청 ID의 일반 SHA-256을 소문자 16진수로 쓴 뒤 .json을 붙인 값입니다. 요청 ID는 레코드 안에 반복하지 않습니다.

레코드는 정규 순서의 필드 열 개만 가집니다.

JSON
{
  "action": "refund",
  "actor": "operator-7",
  "authority": "informative-local-example",
  "checked_input_sha256": "sha256:<64-lowercase-hex>",
  "expires_at_unix_seconds": "1785600000",
  "external_action_performed": false,
  "gate_report_sha256": "sha256:<64-lowercase-hex>",
  "required_decision": "approve",
  "schema": "lispex.example-would-act-record/v1",
  "would_act": true
}

checked_input_sha256는 정확한 검사 입력 파일 바이트의 일반 SHA-256입니다. 네이티브 게이트 보고서는 문맥 안에서 별도의 도메인 분리 입력 식별자를 사용합니다. gate_report_sha256은 승인한 정확한 보고서 바이트의 일반 SHA-256입니다. 어느 다이제스트도 저장된 보고서를 재사용 가능한 권한으로 바꾸지 않습니다.

expires_at_unix_seconds는 이 레코드에서 정규 십진 문자열입니다. 요청과 서버 문맥에서는 범위가 제한된 안전 정수입니다.

멱등성 경계 이해하기

스크립트는 용량 검사, 네이티브 실행, 레코드 공개 전에 원장 전체를 잠급니다. 같은 요청의 기존 레코드가 있으면 게이트를 다시 실행하지 않고 거부합니다. 배타 하드 링크 공개로 동시에 호출한 하나만 최종 경로를 얻습니다. 서로 다른 요청 ID도 원장 전체 입장 잠금을 공유하므로 전체 개수나 바이트 한도를 경쟁해 넘을 수 없습니다.

이 방식은 승인된 요청 ID마다 덮어쓰지 않는 로컬 레코드 하나를 제공합니다. 외부 결제나 환불을 정확히 한 번 수행하게 만들지는 않습니다. 로컬 공개 뒤 프로세스가 중단되어도 external_action_performed 값이 false인 레코드만 남습니다.

실제 트랜잭션은 애플리케이션 상태 기계에 두기

프로덕션 애플리케이션은 게이트와 외부 트랜잭션을 분리된 단계로 유지합니다. 실용적인 통합에는 보통 다음 항목이 모두 필요합니다.

  1. 호출자를 인증하고 서버 소유 요청을 불러옵니다.
  2. 행위자와 행동을 허가하고 만료된 일을 거부합니다.
  3. 정확한 요청을 전용 묶음에 결속하고 새로운 로컬 게이트를 실행합니다.
  4. 고유한 요청 ID로 애플리케이션 저장소에 요청을 원자적으로 예약합니다.
  5. 애플리케이션 소유 연결기를 통해 외부 트랜잭션을 제출합니다.
  6. 외부 시스템 응답을 저장하고 예약을 완료 상태로 바꿉니다.
  7. 제한 시간 초과나 결과를 모르는 상태를 재시도 전에 조정합니다.
  8. 검사 입력 다이제스트, 승인한 게이트 보고서 다이제스트, 트랜잭션 식별자, 최종 상태를 애플리케이션 감사 기록에 남깁니다.

로컬 예제는 의도적으로 4단계 전에 멈춥니다. 애플리케이션은 실제 외부 시스템에 맞춰 예약, 완료, 재시도, 조정을 설계해야 합니다. 공급자 멱등 키는 해당 공급자의 계약을 독립적으로 이해하고 키와 결과를 애플리케이션이 영속 저장할 때만 도움이 됩니다.

닫힌 거부 조건 알기

예제는 모든 부정 조건에서 외부 행동 전에 닫힌 채 실패합니다.

  • 누락, 중복, 알 수 없는 인자나 위치 인자는 파일시스템 접근 전에 사용법 종료값 2를 만듭니다.
  • 상대 네이티브 경로는 거부됩니다. 인수인계가 직접 읽거나 쓰는 요청, 문맥, 검사 입력, 작업 디렉터리, 스테이징, 원장 경로에는 안전하지 않은 경로 종류, 심볼릭 링크, 하드 링크, 정션, 재분석 지점을 허용하지 않습니다. 결정 번들, 수신자 정책, 이미지는 네이티브가 별도로 검사합니다.
  • 정규 형식이 아닌 요청이나 문맥 바이트, 잘못된 스키마, 알 수 없는 필드, 중복 키, 잘못된 문자열, 안전하지 않은 시각, 자원 한도 초과는 거부됩니다.
  • 행위자 불일치, 허용되지 않은 행위자, 허용되지 않은 행동, 만료되거나 잘못된 시각은 자식 실행 전에 거부됩니다.
  • 제공한 검사 입력과 다시 만든 여섯 값 입력 사이의 바이트 차이는 모두 거부됩니다.
  • 기존 입장 잠금, 비어 있지 않은 스테이징 디렉터리, 안전하지 않은 원장, 같은 요청의 기존 레코드, 소진된 원장 용량은 거부됩니다.
  • 실행 시작 실패, 제한 시간 초과, 시그널, 출력 초과, 0이 아닌 자식 종료, 보고서 부재, 예상하지 않은 자식 출력은 거부됩니다.
  • 인증 실패, 오래되거나 잘못된 보고서, 다른 프로필, 다른 입력 식별자, 실행 불일치, 결정 불일치, 통과하지 않은 게이트는 거부됩니다.
  • 레코드 직렬화, 스테이징, 공개, 정리 실패는 거부로 끝납니다. 정리 실패 전에 이미 공개된 레코드는 운영자 검토를 위해 남습니다.

애플리케이션 및 로컬 거부는 빈 표준 출력과 종료값 3을 냅니다. 크기가 제한된 표준 오류 문구는 진단일 뿐입니다. 뒤 단계의 성공이 앞선 거부를 지우지 않습니다.

마지막 주장을 좁게 유지하기

인수인계 레코드는 정확한 현재 호출 하나가 애플리케이션 소유 결정 지점까지 진행할 조건을 만족했다는 뜻입니다. 서버 문맥 너머의 호출자 신원, 정책의 정당성, 이 원장 밖의 요청 유일성, 검사한 만료 뒤의 신선도, 외부 트랜잭션 성공, 행동 권한은 증명하지 않습니다.

실용 결정 워크스페이스 · 리스펙스 바우치 사용하기 · 바우치 영수증