# Bare-metal

## Kubernetes Deployment

> Deploy the Trust1Validation Service as a Docker container on Kubernetes (GKE or any K8s cluster)

The Trust1Validation Service can be deployed on Kubernetes. Example configurations, ConfigMaps, Secrets, and service manifests are provided and work on any cloud provider or on-premise cluster. Docker Compose is also an option if Kubernetes is not available.

***

### Table of Contents

1. Overview
2. Prerequisites
3. Repository Setup
4. Building the Docker Image
5. Kubernetes Configuration
6. Deploying (GKE reference)
7. Configuration Property Reference
8. Keystore Management
9. Teardown
10. Troubleshooting

***

### 1. Overview

The deployment pipeline:

```
Source code (feature/branding_airbus)
  → Docker multi-stage build (Maven 3.9 / Temurin 11 → Tomcat 9)
  → Push to container registry (e.g. Google Artifact Registry)
  → Deploy to Kubernetes via kubectl
  → Configuration injected from Kubernetes ConfigMap at pod startup
```

All Kubernetes and Docker artefacts live under the `deploy/` folder.

#### GKE reference environments (Trust1Team internal)

| Environment      | GCP Project        | GKE Cluster       | Namespace |
| ---------------- | ------------------ | ----------------- | --------- |
| Staging          | `t1t-saas-signbox` | `trust1connector` | `t1c`     |
| Production (new) | `t1t-pre-prod`     | `t1t-services`    | `wallid`  |
| Production (old) | `t1t-pre-prod`     | `trust1connector` | `t1c`     |

If you are deploying to your own cluster, substitute your registry, cluster, and namespace values where these GKE specifics appear.

***

### 2. Prerequisites

#### Local machine

| Tool                                   | Purpose                                                                 | Notes                                         |
| -------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------- |
| Docker (with BuildKit)                 | Build the container image                                               | Use `--platform linux/amd64` for x86 clusters |
| `kubectl`                              | Apply Kubernetes manifests                                              | Install via your cloud provider's CLI         |
| `gcloud` CLI *(GKE only)*              | Authenticate to GCP, push to Artifact Registry, get cluster credentials | `apt install google-cloud-cli`                |
| 1Password CLI `op` *(Trust1Team only)* | Retrieve GCP service-account JSON during Docker build                   | Internal tooling                              |
| Git                                    | Clone the correct branch                                                |                                               |

#### Kubernetes access

Your `kubeconfig` must be configured for the target cluster. For GKE:

```bash
gcloud container clusters get-credentials <cluster-name> \
  --zone <zone> --project <project-id>
```

***

### 3. Repository Setup

```bash
git clone https://github.com/Trust1Team/t1c-dss-api.git
cd t1c-dss-api
git checkout feature/branding_airbus
```

Verify the branch:

```bash
git branch
# * feature/branding_airbus
```

***

### 4. Building the Docker Image

The `Dockerfile` at the repository root is a two-stage build:

1. **Build stage** (`maven:3.9.2-eclipse-temurin-11`) — compiles the WAR.
2. **Runtime stage** (`tomcat:9`) — copies the WAR to `/usr/local/tomcat/webapps/ROOT.war`, exposes port `8080`.

#### Build manually

```bash
# Build the image (always target linux/amd64 for x86 K8s nodes)
docker build --platform linux/amd64 -t t1c-dss-api .
```

For Trust1Team builds that authenticate to a private Maven registry via 1Password:

```bash
op document get wa5yysnilqvw6oujzpumnbjpbq --output ./credentials.json
docker build --platform linux/amd64 -t t1c-dss-api .
rm credentials.json    # Remove immediately — do not commit
```

> **Security:** `credentials.json` is consumed inside the Docker layer and must be removed from the working directory after the build.

#### Tag and push to a registry

```bash
# Generic example
docker tag t1c-dss-api <your-registry>/<image-name>:<version>
docker push <your-registry>/<image-name>:<version>

# GKE Artifact Registry example
REGISTRY="europe-west1-docker.pkg.dev/t1t-saas-signbox/t1t-images"
docker tag t1c-dss-api ${REGISTRY}/t1c-dss-api:5.13.10
docker push ${REGISTRY}/t1c-dss-api:5.13.10
```

#### Test locally before deploying

```bash
docker run -it --rm --name t1c-dss-demo -p 8080:8080 t1c-dss-api
# Open http://localhost:8080
```

#### Save for offline transfer

```bash
docker save t1c-dss-api | gzip > t1c-dss-api_5_13_10.tar.gz
```

***

### 5. Kubernetes Configuration

The application reads its configuration from a Kubernetes ConfigMap mounted into the pod. The ConfigMap is created from the `dss.properties` file in the repository.

#### Create the config ConfigMap

```bash
kubectl create configmap t1c-dss-api-config \
  --from-file=./dss-demo-webapp/src/main/resources/dss.properties \
  -n <namespace>
```

#### Update configuration without redeploying the image

```bash
kubectl delete configmap t1c-dss-api-config -n <namespace>
kubectl create configmap t1c-dss-api-config \
  --from-file=./dss-demo-webapp/src/main/resources/dss.properties \
  -n <namespace>
kubectl rollout restart deployment/t1c-dss-api -n <namespace>
```

#### Complete `dss.properties` example (Kubernetes)

```properties
# https://cloud.spring.io/spring-cloud-static/spring-cloud-kubernetes/1.0.2.RELEASE/multi/
# multi__kubernetes_propertysource_implementations.html

# ── JDBC database cache ───────────────────────────────────────────────
datasource.jdbc.enabled      = true
datasource.driver.class      = org.hsqldb.jdbcDriver
datasource.url               = jdbc:hsqldb:mem:testdb
datasource.username          = sa
datasource.password          =

# ── Revocation cache timings (seconds) ───────────────────────────────
cache.expiration                  = 86400
cache.crl.default.next.update     = 600
cache.crl.max.next.update         = 10800
cache.ocsp.default.next.update    = 60
cache.ocsp.max.next.update        = 180

# ── EU LOTL ──────────────────────────────────────────────────────────
oj.content.keystore.type          = PKCS12
oj.content.keystore.filename      = keystore.p12
oj.content.keystore.password      = dss-password
current.oj.url                    = https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=uriserv:OJ.C_.2019.276.01.0001.01.ENG
current.lotl.url                  = https://ec.europa.eu/tools/lotl/eu-lotl.xml
lotl.country.code                 = EU

# ── AdES LOTL (optional) ─────────────────────────────────────────────
tl.loader.ades.enabled            = false
tl.loader.ades.lotlUrl            = https://ec.europa.eu/tools/lotl/mra/ades-lotl.xml
tl.loader.ades.keystore.type      = PKCS12
tl.loader.ades.keystore.filename  = ades/ades-keystore.p12
tl.loader.ades.keystore.password  = ks-password
tl.loader.ades.tsl.type           = http://ec.europa.eu/tools/lotl/mra/ades-lotl-tsl-type

# ── Server signing token ──────────────────────────────────────────────
dss.server.signing.keystore.type      = PKCS12
dss.server.signing.keystore.filename  = user_a_rsa.p12
dss.server.signing.keystore.password  = password

# ── TSA / timestamping ────────────────────────────────────────────────
tsp-source = classpath:config/tsp-config.xml

# ── LOTL refresh schedule ─────────────────────────────────────────────
cron.tl.loader.enable         = true
cron.initial.delay.tl.loader  = 0
cron.delay.tl.loader          = 3600000

# ── File upload limits ────────────────────────────────────────────────
multipart.maxFileSize     = 52428800
multipart.maxInMemorySize = 52428800

# ── Validation policies ───────────────────────────────────────────────
default.validation.policy             = policy/constraint.xml
default.certificate.validation.policy = policy/certificate-constraint.xml

# ── Security headers ─────────────────────────────────────────────────
cookie.secure                = false
web.security.cookie.samesite = strict

# ── HTTP data loader timeouts (ms) ───────────────────────────────────
dataloader.connection.timeout         = 5000
dataloader.connection.request.timeout = 5000
dataloader.redirect.enabled           = true

# ── CXF ──────────────────────────────────────────────────────────────
cxf.debug        = true
cxf.mtom.enabled = true

# ── mTLS gateway (uncomment and fill in if required) ─────────────────
# proxy.gateway.enabled         = true
# proxy.gateway.url             = https://esb.corp.com:8443/dss-proxy/
# proxy.ssl.client.auth.enabled = true
# proxy.ssl.keystore.path       = /etc/ssl/dss/proxy-client.p12
# proxy.ssl.keystore.type       = PKCS12
# proxy.ssl.keystore.password   = <password>
```

#### Keystore ConfigMaps

Keystores (PKCS12 files) are mounted as separate ConfigMaps into the pod's classpath:

```bash
# Trusted root CA keystore
kubectl create configmap t1c-dss-api-keystore \
  --from-file=keystore.p12=./deploy/<env>/10_keystores/keystore.p12 \
  -n <namespace>

# Intermediate / adjunct CA keystore
kubectl create configmap t1c-dss-api-keystore-intermediate \
  --from-file=keystore-intermediate.p12=./deploy/<env>/10_keystores/keystore-intermediate.p12 \
  -n <namespace>
```

Replace `<namespace>` with `t1c` (staging / prod-old) or `wallid` (prod-new) or your own namespace.

#### Service and Deployment manifest

A minimal `service.yaml` for the pod:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: t1c-dss-api-service
  namespace: <namespace>
spec:
  type: NodePort
  selector:
    app: t1c-dss-api
  ports:
    - port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: t1c-dss-api
  namespace: <namespace>
spec:
  replicas: 1
  selector:
    matchLabels:
      app: t1c-dss-api
  template:
    metadata:
      labels:
        app: t1c-dss-api
    spec:
      containers:
        - name: t1c-dss-api
          image: <your-registry>/t1c-dss-api:<version>
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "1024Mi"
            limits:
              memory: "1024Mi"
          envFrom:
            - configMapRef:
                name: t1c-dss-api-config
          volumeMounts:
            - name: keystore
              mountPath: /usr/local/tomcat/webapps/ROOT/WEB-INF/classes/keystore.p12
              subPath: keystore.p12
              readOnly: true
            - name: keystore-intermediate
              mountPath: /usr/local/tomcat/webapps/ROOT/WEB-INF/classes/keystore-intermediate.p12
              subPath: keystore-intermediate.p12
              readOnly: true
      volumes:
        - name: keystore
          configMap:
            name: t1c-dss-api-keystore
        - name: keystore-intermediate
          configMap:
            name: t1c-dss-api-keystore-intermediate
```

***

### 6. Deploying (GKE reference)

The `deploy/` folder contains ready-made scripts for the Trust1Team GKE environments. Use these as a template or replace them with your own CI/CD pipeline.

#### Staging

```bash
# Step 1 — Build and push image to Artifact Registry
./deploy/staging/00_setenv_build_push.sh <version>
# e.g.: ./deploy/staging/00_setenv_build_push.sh 5.13.10

# Step 2 — Apply ConfigMaps and deploy to Kubernetes
./deploy/staging/01_deploy.sh
```

`01_deploy.sh` recreates the keystore ConfigMaps, creates the config ConfigMap from `dss.properties`, and applies the service manifest from `deploy/staging/12_service/service.yaml`.

#### Production (new)

```bash
./deploy/prod_new/00_setenv_build_push.sh <version>
./deploy/prod_new/01_deploy.sh
```

Targets the `t1t-pre-prod` project, `t1t-services` cluster, `wallid` namespace.

#### Production (old / legacy)

```bash
./deploy/prod_old/00_setenv_build_push.sh <version>
./deploy/prod_old/01_deploy.sh
```

Uses the separate `airbus-dss-api` image name. Use the new production environment for all future deployments unless maintaining the legacy cluster is explicitly required.

***

### 7. Configuration Property Reference

All properties are set in `dss.properties` and injected via the Kubernetes ConfigMap. Full details of each property group:

#### Database (JDBC Cache)

| Property                  | Default                  | Description                                                       |
| ------------------------- | ------------------------ | ----------------------------------------------------------------- |
| `datasource.jdbc.enabled` | `true`                   | Enable JDBC-backed caching                                        |
| `datasource.driver.class` | `org.hsqldb.jdbcDriver`  | JDBC driver. Default: in-memory HSQLDB (data lost on pod restart) |
| `datasource.url`          | `jdbc:hsqldb:mem:testdb` | JDBC connection URL                                               |
| `datasource.username`     | `sa`                     | Database username                                                 |
| `datasource.password`     | *(empty)*                | Database password                                                 |

> For persistent caching across pod restarts, use PostgreSQL.

#### Revocation Cache Timings (seconds)

| Property                         | Default | Description                           |
| -------------------------------- | ------- | ------------------------------------- |
| `cache.expiration`               | `86400` | General cache expiration (24 h)       |
| `cache.crl.default.next.update`  | `600`   | Default CRL refresh interval (10 min) |
| `cache.crl.max.next.update`      | `10800` | Maximum CRL cache lifetime (3 h)      |
| `cache.ocsp.default.next.update` | `60`    | Default OCSP refresh interval (1 min) |
| `cache.ocsp.max.next.update`     | `180`   | Maximum OCSP cache lifetime (3 min)   |

#### EU LOTL

| Property                       | Default                                       | Description                          |
| ------------------------------ | --------------------------------------------- | ------------------------------------ |
| `current.lotl.url`             | `https://ec.europa.eu/tools/lotl/eu-lotl.xml` | EU List of Trusted Lists URL         |
| `lotl.country.code`            | `EU`                                          | Country code                         |
| `oj.content.keystore.filename` | `keystore.p12`                                | OJ trust anchor keystore (classpath) |
| `oj.content.keystore.password` | `dss-password`                                | OJ keystore password                 |
| `cron.delay.tl.loader`         | `3600000`                                     | LOTL refresh interval (ms, 1 h)      |

#### AdES LOTL (optional)

| Property                           | Default                  | Description                            |
| ---------------------------------- | ------------------------ | -------------------------------------- |
| `tl.loader.ades.enabled`           | `false`                  | Enable AdES LOTL for non-EU signatures |
| `tl.loader.ades.lotlUrl`           | AdES LOTL URL            | URL of the AdES LOTL                   |
| `tl.loader.ades.keystore.filename` | `ades/ades-keystore.p12` | AdES keystore (classpath)              |

#### Server-side Signing Keystore

| Property                               | Default          | Description                          |
| -------------------------------------- | ---------------- | ------------------------------------ |
| `dss.server.signing.keystore.type`     | `PKCS12`         | Keystore type                        |
| `dss.server.signing.keystore.filename` | `user_a_rsa.p12` | Classpath path to server signing key |
| `dss.server.signing.keystore.password` | `password`       | Keystore password                    |

Replace the default test key with a production-provisioned key.

#### Timestamp Authority (TSA/TSP)

| Property     | Default                           | Description                          |
| ------------ | --------------------------------- | ------------------------------------ |
| `tsp-source` | `classpath:config/tsp-config.xml` | Spring resource path to TSP bean XML |

The default XML uses a self-signed local TSA (signs entirely in-process). To use an external RFC 3161 TSA, mount a `tsp-config.xml` via ConfigMap and set:

```properties
tsp-source = file:/usr/local/tomcat/conf/tsp-config.xml
```

**External TSA example `tsp-config.xml`:**

```xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    <bean id="tspSource"
          class="eu.europa.esig.dss.service.tsp.OnlineTSPSource">
        <property name="tspServer" value="https://your-tsa-endpoint/tsa"/>
    </bean>
</beans>
```

To route TSA requests through the mTLS gateway, see the mTLS Gateway remarks.

#### HTTP Data Loader Timeouts

| Property                                | Default | Description                       |
| --------------------------------------- | ------- | --------------------------------- |
| `dataloader.connection.timeout`         | `5000`  | TCP connect timeout (ms)          |
| `dataloader.connection.request.timeout` | `5000`  | Connection pool wait timeout (ms) |
| `dataloader.redirect.enabled`           | `true`  | Follow HTTP redirects             |

Increase if the K8s cluster has high latency to LOTL/CRL/OCSP endpoints.

#### Trusted Certificate Sources

**Root CA keystore** (trust anchors for signature validation):

| Property                           | Default        | Description                                                    |
| ---------------------------------- | -------------- | -------------------------------------------------------------- |
| `trusted.source.keystore.type`     | `PKCS12`       | Keystore type                                                  |
| `trusted.source.keystore.filename` | `keystore.p12` | Classpath path (mounted from `t1c-dss-api-keystore` ConfigMap) |
| `trusted.source.keystore.password` | `dss-password` | Keystore password                                              |

**Intermediate CA keystore** (assists chain building):

| Property                                        | Default                     | Description                                                                 |
| ----------------------------------------------- | --------------------------- | --------------------------------------------------------------------------- |
| `trusted.source.keystore-intermediate.type`     | `PKCS12`                    | Keystore type                                                               |
| `trusted.source.keystore-intermediate.filename` | `keystore-intermediate.p12` | Classpath path (mounted from `t1c-dss-api-keystore-intermediate` ConfigMap) |
| `trusted.source.keystore-intermediate.password` | `dss-password`              | Keystore password                                                           |

#### Outbound HTTP Proxy (standard CONNECT)

> For ESB / API Gateway with a context path, use the gateway proxy settings below. The two modes are mutually exclusive.

| Property              | Default  | Description                |
| --------------------- | -------- | -------------------------- |
| `proxy.http.enabled`  | `false`  | Enable HTTP CONNECT proxy  |
| `proxy.http.host`     | *(none)* | Proxy hostname             |
| `proxy.http.port`     | *(none)* | Proxy port                 |
| `proxy.https.enabled` | `false`  | Enable HTTPS CONNECT proxy |
| `proxy.https.host`    | *(none)* | Proxy hostname             |
| `proxy.https.port`    | *(none)* | Proxy port                 |

#### API Gateway / ESB Intercepting Proxy (mTLS)

For a full walkthrough see Remarks — mTLS Gateway.

| Property                        | Default  | Description                                                |
| ------------------------------- | -------- | ---------------------------------------------------------- |
| `proxy.gateway.enabled`         | `false`  | Enable gateway URL rewriting                               |
| `proxy.gateway.url`             | *(none)* | Full gateway URL including context path, must end with `/` |
| `proxy.gateway.trust.all`       | `false`  | Skip PKIX on gateway cert — **DEV/TEST ONLY**              |
| `proxy.ssl.client.auth.enabled` | `false`  | Present mTLS client certificate to gateway                 |
| `proxy.ssl.keystore.path`       | *(none)* | Path to PKCS12 keystore in the pod filesystem              |
| `proxy.ssl.keystore.type`       | `PKCS12` | Keystore format                                            |
| `proxy.ssl.keystore.password`   | *(none)* | Keystore password                                          |

**Providing the client cert keystore via Kubernetes Secret:**

```bash
kubectl create secret generic t1c-dss-proxy-client-cert \
  --from-file=proxy-client.p12=/path/to/proxy-client.p12 \
  -n <namespace>
```

Add to `service.yaml`:

```yaml
volumeMounts:
  - name: proxy-client-cert
    mountPath: /etc/ssl/dss
    readOnly: true
volumes:
  - name: proxy-client-cert
    secret:
      secretName: t1c-dss-proxy-client-cert
```

**Providing the keystore password via Kubernetes Secret:**

```bash
kubectl create secret generic t1c-dss-proxy-secrets \
  --from-literal=proxy.ssl.keystore.password=<password> \
  -n <namespace>
```

Add `secretRef` to `envFrom` in the Deployment:

```yaml
envFrom:
  - configMapRef:
      name: t1c-dss-api-config
  - secretRef:
      name: t1c-dss-proxy-secrets
```

#### Validation Policies

| Property                                | Default                             | Description                                      |
| --------------------------------------- | ----------------------------------- | ------------------------------------------------ |
| `default.validation.policy`             | `policy/constraint.xml`             | Default policy for document signature validation |
| `default.certificate.validation.policy` | `policy/certificate-constraint.xml` | Default policy for certificate validation        |

Custom policies can be mounted via ConfigMap and referenced with a `classpath:` path.

#### File Upload Limits

| Property                    | Default    | Description                                     |
| --------------------------- | ---------- | ----------------------------------------------- |
| `multipart.maxFileSize`     | `52428800` | Maximum file size per upload (bytes, 50 MB)     |
| `multipart.maxInMemorySize` | `52428800` | Maximum in-memory multipart size (bytes, 50 MB) |

#### Web Security Headers

| Property                       | Default  | Description                                             |
| ------------------------------ | -------- | ------------------------------------------------------- |
| `cookie.secure`                | `false`  | Set to `true` when fronted by a TLS-terminating ingress |
| `web.security.cookie.samesite` | `strict` | `SameSite` cookie attribute                             |

#### CXF Web Service Settings

| Property           | Default | Description                                                    |
| ------------------ | ------- | -------------------------------------------------------------- |
| `cxf.debug`        | `true`  | Log full request/response payloads — set `false` in production |
| `cxf.mtom.enabled` | `true`  | Enable MTOM for SOAP binary attachments                        |

***

### 8. Keystore Management

Two keystores are managed as Kubernetes ConfigMaps:

| ConfigMap                           | File                        | Purpose                                        |
| ----------------------------------- | --------------------------- | ---------------------------------------------- |
| `t1c-dss-api-keystore`              | `keystore.p12`              | Root CA trust anchors for signature validation |
| `t1c-dss-api-keystore-intermediate` | `keystore-intermediate.p12` | Intermediate CAs for chain building            |

P12 source files are stored per environment under `deploy/<env>/10_keystores/`. To update a keystore:

1. Replace the P12 file in the appropriate `deploy/<env>/10_keystores/` directory.
2. Recreate the ConfigMap:

```bash
kubectl delete configmap t1c-dss-api-keystore -n <namespace>
kubectl create configmap t1c-dss-api-keystore \
  --from-file=keystore.p12=./deploy/<env>/10_keystores/keystore.p12 \
  -n <namespace>
```

3. Restart the pod:

```bash
kubectl rollout restart deployment/t1c-dss-api -n <namespace>
```

***

### 9. Teardown

```bash
# Using the provided teardown script
./deploy/<env>/xx_delete.sh

# Or manually
kubectl delete deployment  t1c-dss-api                    -n <namespace>
kubectl delete service     t1c-dss-api-service            -n <namespace>
kubectl delete configmap   t1c-dss-api-config             -n <namespace>
kubectl delete configmap   t1c-dss-api-keystore           -n <namespace>
kubectl delete configmap   t1c-dss-api-keystore-intermediate -n <namespace>
```

***

### 10. Troubleshooting

#### Pod fails to start

```bash
kubectl describe pod -l app=t1c-dss-api -n <namespace>
kubectl logs -l app=t1c-dss-api -n <namespace> --previous
```

Common causes:

| Symptom                   | Cause                                              | Fix                                                                              |
| ------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------- |
| ConfigMap not found       | `t1c-dss-api-config` missing or in wrong namespace | Recreate ConfigMap in the correct namespace                                      |
| Keystore error at startup | Keystore ConfigMap missing or path wrong           | Ensure both `t1c-dss-api-keystore` and `t1c-dss-api-keystore-intermediate` exist |
| `ImagePullBackOff`        | Image tag not found in registry                    | Verify the tag in `service.yaml` matches the pushed version                      |
| `OOMKilled`               | Insufficient memory limit                          | Increase `resources.limits.memory` in `service.yaml`                             |

#### LOTL validation warnings at startup

```
Pivot LOTL '...' is not valid (INDETERMINATE/NO_CERTIFICATE_CHAIN_FOUND)
```

The pod cannot reach `ec.europa.eu`. Ensure the cluster has egress access to EU Commission endpoints. If egress is blocked, configure an outbound proxy (section 7 — Outbound HTTP Proxy).

#### Port-forward for local debugging

```bash
kubectl port-forward deployment/t1c-dss-api 8080:8080 -n <namespace>
# Open http://localhost:8080
```

A utility script is available at `deploy/_utils/dev_port_forward_staging.sh`.

#### Update configuration without rebuilding the image

1. Edit `dss-demo-webapp/src/main/resources/dss.properties`.
2. Recreate the ConfigMap (see section 5).
3. Restart: `kubectl rollout restart deployment/t1c-dss-api -n <namespace>`.

#### mTLS gateway issues

See the mTLS Gateway remarks for a full troubleshooting table covering client cert not presented, `SSLHandshakeException`, `Broken pipe`, and trust-all flag left on in production.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://t1t.gitbook.io/t1c-signature-validation-service/deployment/bare-metal.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
