Inkbox

> # Documentation index
> Fetch the complete documentation index at: https://inkbox.ai/sitemap.xml
> Use this file to discover all available pages before exploring further.

---

# Passthrough TLS
description: Terminate TLS in your own agent and obtain a per-tunnel signed certificate

---


# Passthrough TLS

In **passthrough mode**, your agent terminates TLS itself — you hold the private key, and Inkbox never sees it. We issue a signed certificate for your tunnel hostname so third parties get a trusted handshake.

Most users should stick with the default `edge` mode; passthrough is for compliance constraints requiring TLS to terminate inside your network, or for client-side cert pinning.

## Passthrough flow

1. **Create the tunnel** with `tls_mode: "passthrough"`. Status starts as `awaiting_cert` — DNS is provisioned but inbound TLS handshakes will fail until you complete step 3.
2. **Generate a CSR locally.** The Common Name (CN) must equal your tunnel hostname `{tunnel_name}.inkboxwire.com`. Keep the private key on the machine running your agent.
3. **POST the CSR to `/sign-csr`.** Inkbox validates the CN, signs it, and returns the certificate plus chain. Status flips to `active`.
4. **Renew before expiry.** Certificates are valid for 90 days. Call `/sign-csr` again with a fresh CSR before `cert_expires_at`.

### Generate a CSR with OpenSSL

```bash
openssl req -new -newkey rsa:2048 -nodes \\
  -keyout tunnel.key \\
  -out tunnel.csr \\
  -subj "/CN=my-agent.inkboxwire.com"
```

This produces `tunnel.key` (your private key — keep secret) and `tunnel.csr` (the CSR you'll submit below).

---

## Sign CSR `POST`


Submit a CSR for a passthrough tunnel and receive a signed certificate. Valid only for tunnels with `tls_mode: "passthrough"` in `awaiting_cert` or `active` state.

The first successful signing transitions the tunnel from `awaiting_cert` to `active`. Subsequent signings (renewals) are allowed while `active`.

Rate-limited to 10 successful signings per tunnel per day.

### Path parameters

| Parameter | Type | Description |
| :--- | :--- | :--- |
| `tunnel_id` | UUID | Tunnel ID |

### Request body

| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `csr_pem` | string | Yes | PEM-encoded CSR. CN must equal `{tunnel_name}.inkboxwire.com`. |

### Request example

```json
{
    "csr_pem": "-----BEGIN CERTIFICATE REQUEST-----\\nMIICij...\\n-----END CERTIFICATE REQUEST-----\\n"
}
```

### Response (200)

```json
{
    "cert_pem": "-----BEGIN CERTIFICATE-----\\nMIIFa...\\n-----END CERTIFICATE-----\\n",
    "chain_pem": "-----BEGIN CERTIFICATE-----\\nMIIEZ...\\n-----END CERTIFICATE-----\\n",
    "cert_fingerprint_sha256": "9a3f5b...",
    "cert_expires_at": "2026-07-20T12:30:00Z"
}
```

Concatenate `cert_pem` + `chain_pem` to produce the full chain your agent should present during the TLS handshake.

### Error responses

| Status | Description |
| :--- | :--- |
| 400 | CSR is malformed, or CN doesn't match `{tunnel_name}.inkboxwire.com` |
| 409 | Tunnel is not in passthrough mode, or status is not `awaiting_cert`/`active` |
| 429 | Daily signing rate limit exceeded |

### Code examples

**cURL**

```bash
CSR=$(cat tunnel.csr | jq -Rs .)
curl -X POST "https://inkbox.ai/api/v1/tunnels/TUNNEL_ID/sign-csr" \\
    -H "X-API-Key: YOUR_API_KEY" \\
    -H "Content-Type: application/json" \\
    -d "{\\"csr_pem\\": $CSR}"
```

**JavaScript**

```javascript
import { readFile } from "node:fs/promises";

const csrPem = await readFile("tunnel.csr", "utf8");
const response = await fetch(
    `https://inkbox.ai/api/v1/tunnels/${tunnelId}/sign-csr`,
    {
        method: "POST",
        headers: {
            "X-API-Key": "YOUR_API_KEY",
            "Content-Type": "application/json",
        },
        body: JSON.stringify({ csr_pem: csrPem }),
    }
);
const { cert_pem, chain_pem, cert_expires_at } = await response.json();
```

**Python**

```python
import requests

with open("tunnel.csr") as f:
    csr_pem = f.read()

response = requests.post(
    f"https://inkbox.ai/api/v1/tunnels/{tunnel_id}/sign-csr",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"csr_pem": csr_pem},
)
result = response.json()
cert_pem = result["cert_pem"]
chain_pem = result["chain_pem"]
cert_expires_at = result["cert_expires_at"]
```
