Песочница

Передача приложению

Свяжите один аутентифицированный серверный запрос с отдельным пакетом Ваучера, новой проверкой допуска в Native и идемпотентной локальной записью would-act, которую приложение использует для решения о действии.

Поймите назначение примера

handoff/would-act.mjs является эталонным примером локальной передачи. Он проверяет закрытые запрос и контекст, запускает Native по абсолютному пути, переданному вызывающей стороной, и публикует запись would_act. Хост-приложение владеет аутентификацией, состоянием транзакции и соединителем внешнего действия.

Единственный долговременный эффект состоит в локальной записи would_act. Она говорит, что этот вызов дошёл до точной новой проверки допуска и что внешнее действие не выполнялось.

Аутентифицируйте до создания серверного контекста

Окружающее приложение аутентифицирует вызывающего пользователя, а затем создаёт файл серверного контекста из серверных фактов.

После аутентификации приложение владеет всеми следующими фактами.

  • аутентифицированный исполнитель
  • допустимое множество исполнителей
  • допустимое множество действий
  • доверенное текущее время Unix
  • ID запроса и правило его хранения
  • срок, выбранный сервером
  • деловые факты days и opened

Аутентифицированного исполнителя, доверенное текущее время и срок задаёт серверный контекст.

Разделите данные запроса и доверенный контекст

Запись запроса использует закрытую схему 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, управляющие символы, небезопасные числа, превышение размера и лишние байты отвергаются до запуска Native. Строки не нормализуются и не переводятся в другой регистр. Точные байты принятых скалярных значений сохраняются.

Свяжите все шесть значений текущего запроса

Приложение заново строит один точный проверяемый вход из принятого запроса и серверного контекста. Значение поля input csk.checked-input/v1 называет фиксированную форму файла проверяемого входа, а поле value несёт сам вход.

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] использует собственный пакет. Текущая передача использует отдельный пакет, чей подписанный контекст связывает все шесть значений запроса.

Выдайте отдельный пакет

Издатель создаёт пакет текущего запроса до того, как приложение получателя запускает передачу. Эта команда отделена от передачи и использует ключ издателя вне каталога примера и рабочего корня приложения. Значение --profile csk.checked-profile/v1 называет фиксированный проверяемый профиль с формами, участвующими в Ваучере.

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 как одну исключительную блокировку всего журнала. Существующая блокировка или непустой staging требует проверки оператором и вызывает отказ. Блокировка не удаляется автоматически только из-за возраста.

Запустите передачу с точными восемью флагами

Из корня примера передайте каждый флаг ровно один раз. Путь Native должен быть абсолютным. Остальные пути разрешаются один раз относительно каталога вызова.

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 и новый путь отчёта под управлением вызывающей стороны. Он использует массив аргументов без shell, закрытое дочернее окружение, предел времени 30 секунд и заданный предел вывода. Вызывающая сторона передаёт точный путь executable и сохраняет stdout и stderr в локальной записи.

Прочитайте точную локальную запись

Только успешно пройденная новая проверка допуска достигает публикации. Конечное имя файла является обычным SHA-256 необработанного ID запроса в UTF-8, записанным 64 строчными шестнадцатеричными знаками с окончанием .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 точных байтов файла проверяемого входа. Отчёт проверки допуска в Native использует в контексте другую доменно разделённую идентичность входа. gate_report_sha256 является обычным SHA-256 точных байтов принятого отчёта. Новое полномочие создаёт свежая проверка допуска с текущим запросом.

expires_at_unix_seconds записан здесь десятичной строкой заданного вида. В запросе и серверном контексте это безопасное целое число в заданных границах.

Поймите границу идемпотентности

Скрипт блокирует весь журнал до проверки вместимости, запуска Native и публикации записи. При любой существующей записи того же запроса он отказывает без новой проверки допуска. Исключительная публикация жёсткой ссылкой позволяет только одному конкурентному вызову получить конечный путь. Разные ID запросов тоже делят блокировку допуска ко всему журналу и не могут одновременно превысить общий предел количества или байтов.

Пример даёт одну локальную запись без перезаписи на каждый принятый ID запроса. Конечный автомат транзакции приложения использует эту запись для однократного платежа или возврата. Запись, опубликованная до деловой транзакции, сохраняет external_action_performed со значением false.

Поместите настоящую транзакцию в конечный автомат приложения

Рабочее приложение сохраняет проверку допуска и внешнюю транзакцию раздельными шагами. Практической интеграции обычно нужны все следующие действия.

  1. Аутентифицировать пользователя и загрузить серверный запрос.
  2. Разрешить исполнителя и действие и отвергнуть просроченную работу.
  3. Связать точный запрос с отдельным пакетом и запустить новую локальную проверку допуска.
  4. Атомарно зарезервировать запрос в хранилище приложения под уникальным ID.
  5. Отправить внешнюю транзакцию через соединитель под управлением приложения.
  6. Сохранить ответ внешней системы и завершить резервирование.
  7. Согласовать тайм-аут или неизвестный исход до любой повторной попытки.
  8. Сохранить хэш проверяемого входа, хэш принятого отчёта проверки допуска, идентичность транзакции и конечное состояние в аудите приложения.

Локальный пример намеренно останавливается до шага 4. Приложение обязано спроектировать резервирование, завершение, повторы и согласование для настоящей внешней системы. Ключ идемпотентности поставщика помогает только тогда, когда контракт поставщика независимо понятен, а приложение долговременно хранит ключ и исход.

Знайте закрытые отказы

Пример закрыто отказывает до внешнего действия во всех отрицательных случаях.

  • Отсутствующие, повторные, неизвестные или позиционные аргументы дают код использования 2 до доступа к файловой системе.
  • Относительный путь Native отвергается. Пути запроса, контекста, проверяемого входа, рабочих каталогов, staging и журнала, которые передача сама читает или пишет, также не допускают небезопасный вид пути, символические и жёсткие ссылки, junction и reparse point. Native отдельно проверяет пакет решения, политику получателя и изображение.
  • Байты запроса или контекста не в заданном виде, неверные схемы, неизвестные поля, повторные ключи, плохие строки, небезопасное время и превышение ресурсов отвергаются.
  • Несовпадение исполнителя, запрещённый исполнитель, запрещённое действие и истёкшее или неверное время отвергаются до запуска потомка.
  • Любое различие байтов между переданным входом и заново построенным входом из шести значений отвергается.
  • Существующая блокировка допуска, непустой staging, небезопасный журнал, существующая запись того же запроса или исчерпанная вместимость журнала вызывают отказ.
  • Ошибка запуска, тайм-аут, сигнал, переполнение вывода, ненулевой код потомка, отсутствие отчёта и неожиданный вывод потомка вызывают отказ.
  • Ошибка аутентификации, старый или неправильный отчёт, другой профиль, другая идентичность входа, несогласие выполнения, несовпадение решения и непредоставленный результат проверки допуска вызывают отказ.
  • Ошибка сериализации, staging, публикации или очистки записи заканчивается отказом. Уже опубликованная до ошибки очистки запись остаётся для проверки оператором.

Отказы приложения и локальной среды возвращают код 3 с пустым stdout. Диагностическое сообщение stderr использует заданный бюджет байтов. Ранний отказ остаётся конечным статусом этого вызова.

Поля записи и политика приложения

Запись передачи означает, что один точный текущий вызов может перейти к точке решения под управлением приложения. Серверный контекст предоставляет идентичность пользователя, проверка политики — критерии корректности, журнал — уникальность запроса, а срок — актуальность. Хост-приложение записывает внешнюю транзакцию и полномочия действия.

Готовый пример возврата · Использование Ваучера Лиспекса · Квитанции Ваучера