> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# DSAR portability + supply-chain attestation

> DSAR exports return plain portable JSON by default (GDPR Article 20). Releases are signed with Sigstore and accompanied by a CycloneDX SBOM.

## DSAR export contract (W3.1)

`POST /api/v1/dsar` with `{ requestType: "export", subjectId: "<customerId>" }`
enqueues an async export job. Polling `GET /api/v1/dsar?status=completed`
returns the request with the export attached.

### Default response shape

The exported `data` field is **plain portable JSON** (Article 20 compliant):

```json theme={null}
{
  "customerId": "cust-123",
  "exportedAt": "2026-04-28T12:00:00.000Z",
  "encrypted": false,
  "data": {
    "interactions": [...],
    "summaries": [...],
    "variantAssignments": [...],
    "deliveries": [...],
    "auditLogs": [...],
    "consentRecords": [...],
    "dsarRequests": [...]
  },
  "counts": {
    "interactions": 1234,
    "summaries": 56,
    "variantAssignments": 12,
    "deliveries": 89,
    "auditLogs": 234,
    "consentRecords": 6,
    "dsarRequests": 1
  }
}
```

**ConsentRecord rows are now part of the export** (per Article 7
demonstrable-consent requirement). This is new in W3.1.

### Encrypted at-rest envelope

For long-term archival, request encryption with `?encrypt=true` (when
calling `exportSubjectData` programmatically) or set the corresponding
flag on the DSAR worker job. The encrypted shape:

```json theme={null}
{
  "encrypted": true,
  "data": {
    "ciphertext": "<base64-encrypted-blob>",
    "format": "encrypted-aes-256-gcm"
  }
}
```

The encryption uses the platform's AES-256-GCM key registry (versioned,
rotatable). Decryption requires `lib/encryption.decrypt()` with the same
key version.

### Why the change

The third-party audit (2026-04-28) flagged that the previous DSAR export
returned only an encrypted blob, failing GDPR Article 20's
right-to-portability. W3.1 makes plain JSON the default; encryption is
opt-in for at-rest storage.

## Resumable Flow runs (W3.2)

Long-running pipeline runs now persist a per-node checkpoint after each
successful step. On retry of a failed run with the same `runId`,
already-completed nodes are skipped and their cached output is reloaded
from the checkpoint.

Two backends:

| Store                     | When                                                     |
| ------------------------- | -------------------------------------------------------- |
| **In-memory store**       | Tests, ephemeral local dev                               |
| **Postgres-backed store** | Production — persists to the `flow_run_checkpoint` table |

Migration: apply the flow-run-checkpoints SQL once per deployment
(idempotent). The Helm chart's existing manual-SQL apply step picks
it up automatically.

The flow runtime exposes a resumable DAG runner together with both
checkpoint stores. Four canonical scenarios are covered by the
runtime's checkpoint tests: clean run, retry-after-failure, clear,
and lifecycle hooks.

## Release supply chain (W3.3)

Pushing a tag (`git tag v0.5.0 && git push origin v0.5.0`) triggers
`.github/workflows/release.yml`:

1. Checkout, install, generate Prisma client, run vitest.
2. Generate CycloneDX 1.5 SBOM from `package-lock.json` via
   `tools/scripts/generate-sbom.ts`.
3. Upload SBOM as a workflow artifact (365-day retention).
4. **If AWS\_ROLE\_ARN is configured as a repo variable:**
   * Build + push Docker image to ECR.
   * Sign the image with Cosign (keyless via GitHub OIDC).
   * Generate SLSA v1 in-toto provenance via
     `tools/scripts/generate-slsa-attestation.ts`.
   * `cosign attest --type slsaprovenance1` attaches the provenance.
5. Create a GitHub Release with the SBOM file attached and
   auto-generated release notes.

If the **AWS\_ROLE\_ARN** secret isn't set (e.g. on forks), the workflow still builds the
SBOM and creates the release — only the image-signing steps are skipped.

### Verifying a signed release

After a release lands:

```bash theme={null}
cosign verify \
  --certificate-identity-regexp "https://github\.com/kaireonai/platform/\.github/workflows/release\.yml" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  422500312304.dkr.ecr.us-east-1.amazonaws.com/kaireon-api:v0.5.0
```

And to pull the SLSA provenance:

```bash theme={null}
cosign verify-attestation \
  --type slsaprovenance1 \
  --certificate-identity-regexp "https://github\.com/kaireonai/platform/\.github/workflows/release\.yml" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  422500312304.dkr.ecr.us-east-1.amazonaws.com/kaireon-api:v0.5.0 \
  | jq '.payload | @base64d | fromjson | .predicate'
```

## Performance baseline (W3.4)

`platform/perf/recommend.js` is a k6 scenario file with three pre-baked
RPS levels (1K / 5K / 10K). Running them is operator-driven (requires a
load-test environment isolated from playground + credentialed API
keys). The baseline procedure + run command is committed to
`platform/perf/baselines/2026-04-28-smoke.md`.

## What ships with this surface

* DSAR exporter — produces a portable JSON dossier per data subject, with optional encrypt opt-in.
* DSAR fire-drill suite — 13 tests including 3 covering portable JSON, encrypt opt-in, and consent-record inclusion.
* Flow checkpoint runtime — both in-memory and Postgres-backed stores plus the resumable DAG runner.
* Flow checkpoint test suite — 4 canonical scenarios (clean run, retry-after-failure, clear, hooks).
* Flow checkpoint SQL migration (manual-sql/07).
* SBOM and SLSA attestation generators (in `tools/scripts`).
* Release workflow (`.github/workflows/release.yml`).
* Recommend k6 perf baseline + smoke run notes (`platform/perf/baselines/2026-04-28-smoke.md`).
