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

# Idira

> Call Reducto from workloads attested by Idira Secure Workload Access with a SPIFFE JWT-SVID instead of a stored API key.

Idira Secure Workload Access (SWA) assigns each attested workload a SPIFFE ID and issues it a JWT-SVID. Reducto verifies the JWT-SVID and returns an access token. This guide covers the SWA-specific steps. The token rules are the same as in the [SPIFFE guide](https://docs.reducto.ai/security/workload-identity-federation/spiffe.md).

## Before you begin

You need:

* An SWA trust domain with an attestation policy that assigns a SPIFFE ID to your workload.
* Access from the workload to the SWA Agent's Workload API socket.
* The **Owner** or **Admin** role in your Reducto organization.

## Get the issuer and keys

Find your trust domain's `jwt.discovery_endpoints.oidc_discovery_url` and `jwt.discovery_endpoints.jwks_uri`. See Idira's [OIDC configuration](https://docs.cyberark.com/secrets-manager-saas/latest/en/content/conjurcloud/ccl-swa-oidc.htm) instructions. Reducto must reach both over HTTPS without credentials.

SWA signs with `RS512` and a 4096-bit RSA key by default. Reducto accepts it.

## Deliver a JWT-SVID to the workload

Run [spiffe-helper](https://github.com/spiffe/spiffe-helper) next to the workload to fetch and refresh the JWT-SVID. Save this as `helper.conf`:

```hcl theme={null}
agent_address = "/tmp/swa-agent/public/api.sock"
cert_dir      = "/var/run/spiffe"
daemon_mode   = true
jwt_svids = [{
  jwt_audience       = "https://platform.reducto.ai"
  jwt_svid_file_name = "reducto.jwt"
}]
```

```bash theme={null}
spiffe-helper -config helper.conf
```

In Kubernetes, run the helper as a sidecar. Share a memory-backed `emptyDir` at `/var/run/spiffe` with the application container, and mount the socket directory `/tmp/swa-agent/public` into the helper. On VMs, set `agent_address` to `/run/swa-agent/api.sock`.

## Inspect the token

From inside the workload, decode the token and check these fields:

```bash theme={null}
cut -d. -f2 /var/run/spiffe/reducto.jwt | tr '_-' '/+' | base64 -d 2>/dev/null | jq .
```

| Field | Expected value |
| - | - |
| Header `alg` | The SWA signing algorithm, for example `RS512` |
| Header `kid` | A key ID in the trust domain's JWKS |
| `iss` | The trust domain's HTTPS issuer URL |
| `sub` | The workload's SPIFFE ID, for example `spiffe://prod.acme.com/k8s/ns/payments/sa/invoice-parser` |
| `aud` | Contains `https://platform.reducto.ai` |
| `iat`, `exp` | A token that has not expired |

## Configure Reducto

In [Studio](https://studio.reducto.ai), open **Settings > Workload identity** and create a provider with the **Idira** preset:

| Field | Value |
| - | - |
| **Name** | `idira-prod` |
| **Issuer** | The JWT-SVID's `iss` |
| **Audience** | `https://platform.reducto.ai` |
| **Key source** | OIDC discovery |

If Reducto cannot reach the discovery document, use **Uploaded JWKS** with the keys from `jwks_uri`, and update them when SWA rotates its keys.

Add a mapping with `sub` = the exact SPIFFE ID from the token, for example `spiffe://prod.acme.com/k8s/ns/payments/sa/invoice-parser`.

## Call Reducto

Set both environment variables in the application container. The SDK then needs no arguments and reads the token file on every exchange.

```bash theme={null}
export REDUCTO_IDENTITY_PROVIDER_ID="wip_8f2k3m9q1r5t7v0x2z4b6d8f"
export REDUCTO_IDENTITY_TOKEN_FILE="/var/run/spiffe/reducto.jwt"
```

```python Python theme={null}
from reducto import Reducto

client = Reducto()

result = client.parse.run(input="https://cdn.reducto.ai/samples/fidelity-example.pdf")
print(result.result.chunks[0].content[:500])
```

```javascript Node.js theme={null}
import Reducto from "reductoai";

const client = new Reducto();

const result = await client.parse.run({
  input: "https://cdn.reducto.ai/samples/fidelity-example.pdf",
});
console.log(result.result.chunks[0].content.slice(0, 500));
```

```bash cURL theme={null}
ACCESS_TOKEN=$(jq -n \
  --arg subject_token "$(cat "$REDUCTO_IDENTITY_TOKEN_FILE")" \
  --arg provider_id "$REDUCTO_IDENTITY_PROVIDER_ID" \
  '{
    grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
    subject_token_type: "urn:ietf:params:oauth:token-type:jwt",
    subject_token: $subject_token,
    identity_provider_id: $provider_id
  }' \
  | curl -sS --fail-with-body -X POST "https://platform.reducto.ai/oauth/token" \
      -H "Content-Type: application/json" -d @- \
  | jq -r .access_token)

curl -X POST "https://platform.reducto.ai/parse" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"input":"https://cdn.reducto.ai/samples/fidelity-example.pdf"}'
```

Make sure `REDUCTO_API_KEY` is not set in the container. It takes precedence over the workload identity variables.

## Troubleshooting

| Symptom | What to check |
| - | - |
| The helper gets no identity | The workload matches the SWA attestation policy and can reach the agent socket. |
| The token file is missing, empty or expired | The helper is running, the shared volume path matches, and host clocks are in sync. |
| Verification fails | `iss`, `aud`, `kid` and expiry against the provider, and that Reducto can reach discovery and the JWKS. |
| No mapping matches | The provider ID and the exact `sub` value. Run **Test a token** in Studio. |
| More than one mapping matches | Overlapping wildcard mappings under the same provider. Narrow or disable one. |

For SWA diagnostics, see Idira's [OpenAI integration guide](https://docs.cyberark.com/secrets-manager-saas/latest/en/content/conjurcloud/ccl-swa-openai.htm), which uses the same agent and helper setup.

## Idira best practices

* Use one SPIFFE ID per application and environment, and match it exactly.
* Keep the token file on memory-backed storage, readable only by the application.
* Keep spiffe-helper running so it replaces the JWT-SVID before it expires.
