Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/kestra.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Kestra 2.0 candidate fixture

on:
push:
paths:
- "integrations/kestra/**"
- ".github/workflows/kestra.yml"
pull_request:
paths:
- "integrations/kestra/**"
- ".github/workflows/kestra.yml"
workflow_dispatch:

permissions:
contents: read

jobs:
native-synthetic-fixture:
runs-on: ubuntu-24.04
timeout-minutes: 15
defaults:
run:
working-directory: integrations/kestra
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Build pinned official runtime and fixture image
run: docker build -f tests/Dockerfile -t sanctionskit-kestra-fixture .
- name: Execute native flow with only container loopback available
run: |
mkdir -p test-results
docker run --rm --network none --cap-drop ALL \
--hostname kestra-fixture --add-host kestra-fixture:127.0.0.1 \
--security-opt no-new-privileges:true --read-only \
--memory 3g --cpus 2 --pids-limit 512 \
--user "$(id -u):$(id -g)" \
--tmpfs /tmp:rw,nosuid,nodev,size=1g,mode=1777 \
--tmpfs /work:rw,nosuid,nodev,size=1g,mode=1777 \
--mount "type=bind,source=$PWD/test-results,target=/evidence" \
sanctionskit-kestra-fixture
- name: Keep native fixture evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: kestra-2-native-fixture-${{ github.run_id }}
path: integrations/kestra/test-results/
retention-days: 7
if-no-files-found: warn
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ npm --prefix integrations/windmill test

[Node-RED](integrations/node-red/) includes an importable flow built with core nodes. Run a fictional screening, retrieve its evidence, and inspect a review record. It keeps credentials in the process environment and reuses a stable request key for manual retries. The README includes setup instructions and local runtime tests.

[Kestra](integrations/kestra/) submits a fictional batch, waits for its final state, and exports retained evidence and a CSV row summary. It keeps failed and cancelled rows visible for review. The Kestra 2.0.4 flow passed 24 native scenarios against a local HTTP fixture in an isolated container.

## Make your first request

1. [Create a workspace](https://www.sanctionskit.com/signup), then open [API keys](https://www.sanctionskit.com/dashboard/keys?environment=sandbox).
Expand Down Expand Up @@ -87,7 +89,7 @@ These scripts display responses for learning. When adapting them to an applicati

`REQUEST_KEY` is sent as the `Idempotency-Key` header. Generate it once for a new screening. If a request times out or needs a retry, keep **the same key and the same body**. Rerun the script without repeating the key-generation command. A new subject or changed body needs a new key.

The examples make one attempt and exit with a nonzero status on an HTTP or network error. They do not retry automatically. An error is an incomplete request, never a `no_match` result. See [idempotency](https://www.sanctionskit.com/docs/idempotency) and [error handling](https://www.sanctionskit.com/docs/errors) before adding retries to an application.
The standalone language examples make one attempt and exit with a nonzero status on an HTTP or network error. They do not retry automatically. An error is an incomplete request, never a `no_match` result. See [idempotency](https://www.sanctionskit.com/docs/idempotency) and [error handling](https://www.sanctionskit.com/docs/errors) before adding retries to an application.

## Adapt the example

Expand Down
5 changes: 5 additions & 0 deletions integrations/kestra/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__pycache__
**/__pycache__
tests/native-results.json
tests/last-failure.json
test-results
7 changes: 7 additions & 0 deletions integrations/kestra/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
__pycache__/
tests/native-results.json
tests/last-failure.json
tests/last-failure-diagnostics.json
test-results/
synthetic-review-bundle.json
synthetic-row-summary.csv
21 changes: 21 additions & 0 deletions integrations/kestra/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 SanctionsKit

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
83 changes: 83 additions & 0 deletions integrations/kestra/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Kestra batch screening and evidence

This Kestra 2.0.4 flow submits two fictional contacts to the SanctionsKit synthetic
sandbox, waits for the batch to finish, and collects retained evidence with a CSV
row summary. Completed, failed and cancelled rows stay visible. A successful
collection does not approve onboarding or mean that every row screened successfully.

All 24 native fixture scenarios passed on Kestra 2.0.4 with serdes 2.0.6 and
Temurin 25 in an isolated container. The [verified CI run](https://github.com/SanctionsKit/sanctions-kit-examples/actions/runs/37181224466)
used a local fictional HTTP server with Docker networking disabled. This verifies
the workflow and its error handling against fixtures; a hosted sandbox run remains
separate.

## Isolated fixture test

The CI job builds an image from official Python and Eclipse Temurin images pinned
by digest, downloads Kestra 2.0.4 with a checked SHA-256, and installs serdes 2.0.6
with a checked artifact hash. [runtime-lock.json](runtime-lock.json) records these
sources. Network access is needed only while building the image.

Execution uses Docker `--network none`, with no published ports or Docker socket.
The fixed hostname resolves to `127.0.0.1` through the container's hosts file, so
Java can identify the local host without DNS. The test runner checks that mapping
and refuses to start unless its only network interface is `lo`. It
runs as an unprivileged user with all Linux capabilities dropped, no new privileges,
a read-only root filesystem, and temporary writable directories. The only service
key is the invented string `local-mock-only`; no GitHub secret or real API key is
needed. No hosted SanctionsKit request can leave the container.

From this directory on a machine with Docker:

```sh
docker build -f tests/Dockerfile -t sanctionskit-kestra-fixture .
mkdir -p test-results
docker run --rm --network none --cap-drop ALL \
--hostname kestra-fixture --add-host kestra-fixture:127.0.0.1 \
--security-opt no-new-privileges:true --read-only \
--memory 3g --cpus 2 --pids-limit 512 \
--user "$(id -u):$(id -g)" \
--tmpfs /tmp:rw,nosuid,nodev,size=1g,mode=1777 \
--tmpfs /work:rw,nosuid,nodev,size=1g,mode=1777 \
--mount "type=bind,source=$PWD/test-results,target=/evidence" \
sanctionskit-kestra-fixture
```

The fixture imports the actual flow and changes only the API host, polling interval
and polling budget. Request bodies, evidence checks, HTTP limits and exports stay
unchanged. It expects all 24 scenarios to pass, including completion, rate-limit
retry, failed/cancelled rows, timeouts, HTTP errors, unsafe IDs, mismatched evidence,
pagination, row order and counters. It checks the parent execution and native Loop
iteration executions, uses the 2.0 outputs API, downloads the JSON/CSV files, and
checks logs and outputs for the fake key.

The server log, per-case results, isolation report and generated files go into
`test-results/`. A failed assertion also saves bounded parent and Loop task logs
and outputs, with fixture credentials redacted. A parser or import pass is not
a substitute for this run.

## Sandbox setup

Create a free [developer workspace](https://www.sanctionskit.com/signup?workflow=api)
and a sandbox key with `batches:write` and `results:read`. Save the key as the Kestra
secret `SANCTIONSKIT_API_KEY`. The sandbox uses synthetic records and needs no card.
Production screening needs a plan and payment method.

The two fixed contacts keep their partial birth years as strings. Each row requests
`sandbox@1` and standard retention. Use a fresh `request_key` for a new business
batch and the same key/body for retries. Redirects are disabled. A 202 only confirms
acceptance; the bounded loop waits for a terminal batch state.

This starter expects one complete ordered page. Unexpected pagination, counters,
IDs, contacts or retained requests stop the flow. It fetches same-ID evidence only
for completed rows and preserves unsuccessful row outcomes. A `potential_match`
needs review; `no_match` means no candidates within the selected coverage and
matching rules. Neither is an account approval or legal clearance.

The daily trigger is disabled. Before adapting this to larger inputs or enabling
scheduling, read the [batch API guide](https://www.sanctionskit.com/docs/batches) and
choose a durable business-batch key. No external notifications, account decisions
or database writes are part of this example.

No Kestra catalog submission or placement is claimed. Upstream blueprint licensing
and catalog acceptance remain separate from company source publication.
37 changes: 37 additions & 0 deletions integrations/kestra/runtime-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"checked_at": "2026-10-04T04:51:28.638389+00:00",
"kestra": {
"version": "2.0.4",
"url": "https://github.com/kestra-io/kestra/releases/download/v2.0.4/kestra-2.0.4",
"sha256": "88ee8d44c3d4a234c9d04689e72b8ba903ae8f9f2c6ae5f94bad89f3bcd68513"
},
"plugin": {
"coordinates": "io.kestra.plugin:plugin-serdes:2.0.6",
"sha256": "1394bf5ec0e7a57e446adc2b326e4ac72651d3fe525853d4d1c92bef60fe117e",
"maven_sha1_matches": true
},
"docker_images": [
{
"image": "docker.io/library/python",
"tag": "3.13-slim-trixie",
"index_digest": "sha256:bb2988715db2cf7ace7b53f38f3cffbef7c7046a656bee66245eb0ed386e2e81",
"linux_amd64_digest": "sha256:b92e6b9bb1ea9d826e9956fd8d30bf18bc384c130231d74fdf0ba3460c290b0b"
},
{
"image": "docker.io/library/eclipse-temurin",
"tag": "25-jre-noble",
"index_digest": "sha256:398f810215757dc1926390014272579fb0e57c41ef1c8aa4f64ae761613a168b",
"linux_amd64_digest": "sha256:78498f30dd330b06755c1b134039dcbf8324bedb385136819dd519b842a3816a"
}
],
"actions": {
"checkout": {
"version": "v7.0.1",
"sha": "3d3c42e5aac5ba805825da76410c181273ba90b1"
},
"upload_artifact": {
"version": "v7.0.1",
"sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"
}
}
}
Loading
Loading