Песочница

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

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

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

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

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

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

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

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

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

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

Приложение заново строит один точный проверяемый вход из принятого запроса и серверного контекста.

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] и любой выданный для него bundle служат только пошаговому примеру. Их нельзя использовать здесь. Передача требует отдельного bundle, чей подписанный контекст связывает все шесть значений.

Выдайте отдельный bundle

Издатель создаёт bundle текущего запроса до того, как приложение получателя запускает передачу. Эта команда отделена от передачи и использует ключ издателя вне рабочего пространства и рабочего корня приложения.

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

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

Подготовьте пространство имён локального журнала

Создайте один настоящий каталог под управлением вызывающей стороны и два существующих настоящих прямых подкаталога. Сохраняйте пространство имён стабильным до завершения вызова.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Сохраняйте конечное утверждение узким

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

Практическое рабочее пространство решений · Использование Lispex Vouch · Квитанции Vouch