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
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@ jobs:
primary-tag: ${{ steps.meta.outputs.primary-tag }}
is-release: ${{ steps.meta.outputs.is-release }}

permissions:
contents: read
id-token: write # Required for Sigstore cosign keyless signing (OIDC → Fulcio)

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
Expand Down Expand Up @@ -496,13 +500,54 @@ jobs:

echo "### ✅ Pushed to Docker Hub" >> $GITHUB_STEP_SUMMARY

- name: Install cosign
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1

- name: Sign Docker images (keyless)
run: |
PRIMARY_TAG="${{ steps.meta.outputs.primary-tag }}"
IS_RELEASE="${{ steps.meta.outputs.is-release }}"
IS_STABLE="${{ steps.meta.outputs.is-stable }}"

echo "=== Signing ${DOCKER_IMAGE}:${PRIMARY_TAG} ==="
cosign sign --yes ${DOCKER_IMAGE}:${PRIMARY_TAG}

if [[ "$IS_STABLE" == "true" ]]; then
echo "=== Signing semver aliases ==="
cosign sign --yes ${DOCKER_IMAGE}:${{ steps.meta.outputs.minor-tag }}
cosign sign --yes ${DOCKER_IMAGE}:${{ steps.meta.outputs.major-tag }}
fi

if [[ "$IS_RELEASE" == "true" ]]; then
echo "=== Signing latest ==="
cosign sign --yes ${DOCKER_IMAGE}:latest
fi

echo "### 🔏 Docker Images Signed (Sigstore cosign)" >> $GITHUB_STEP_SUMMARY
echo "Keyless OIDC signing via GitHub Actions identity." >> $GITHUB_STEP_SUMMARY
echo "Verify: \`cosign verify --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp '^https://github\\.com/labsai/EDDI/\\.github/workflows/ci\\.yml@refs/(heads/main|tags/.+)$' ${DOCKER_IMAGE}:${PRIMARY_TAG}\`" >> $GITHUB_STEP_SUMMARY

# ─── Job 4: Smoke Test ──────────────────────────────────────────
smoke-test:
name: Smoke Test
runs-on: ubuntu-latest
needs: docker

steps:
- name: Install cosign
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1

- name: Verify Docker image signature
run: |
PRIMARY_TAG="${{ needs.docker.outputs.primary-tag }}"
echo "=== Verifying signature for ${DOCKER_IMAGE}:${PRIMARY_TAG} ==="
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github\.com/labsai/EDDI/\.github/workflows/ci\.yml@refs/(heads/main|tags/.+)$' \
${DOCKER_IMAGE}:${PRIMARY_TAG}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
echo "✅ Image signature verified"
echo "### 🔏 Image Signature Verified" >> $GITHUB_STEP_SUMMARY

- name: Start MongoDB
run: |
docker run -d --name mongodb \
Expand Down
26 changes: 26 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ When designing any new feature, always consider these before finalizing the desi

| File | Purpose |
| ------------------------------------------- | ----------------------------------------------------------- |
| `src/main/docker/Dockerfile.jvm` | Production JVM container image (digest-pinned base) |
| `src/main/resources/application.properties` | Quarkus config (CORS, health, OpenAPI, MongoDB) |
| `src/main/resources/initial-agents/` | Agent Father and sample agent configs |
| `.github/workflows/ci.yml` | CI/CD pipeline (build, test, Docker push, smoke test) |
Expand All @@ -556,6 +557,31 @@ When designing any new feature, always consider these before finalizing the desi
| `src/main/java/.../security/AuthStartupGuard.java` | Production auth enforcement guard |
| `.env.example` | Required environment variables |

### Docker & Container Security

#### Base Image Management

The production image (`Dockerfile.jvm`) uses a Red Hat UBI 9 base pinned by **SHA256 digest** for OpenSSF supply-chain compliance. This means:

- The `FROM` line must always include `@sha256:...` — never use a bare tag like `:1.24`
- Red Hat periodically republishes the same tag with security patches baked in

#### Trivy CVE Remediation Procedure

When Trivy (CI container scan) flags a base image CVE:

1. **Check for a newer digest first** — pull the latest image for the same tag and compare:
```bash
docker pull registry.access.redhat.com/ubi9/openjdk-25-runtime:1.24
# Check the digest in the pull output
docker run --rm <image> rpm -q <vulnerable-package>
```
2. **If the newer digest includes the fix**: update the `@sha256:...` in `Dockerfile.jvm` — done
3. **If no fixed digest exists yet**: use `microdnf update -y <package> && microdnf clean all` as a **temporary stopgap** in the `USER root` section, with a CVE comment. Remove it once a fixed base image is available
4. **Never remove the digest pin** to "auto-fix" CVEs — this violates OpenSSF supply-chain requirements

> **Key principle**: Digest update is the clean fix. `microdnf update` is the escape hatch.

---

## 5. Agent Config Authoring Reference
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

# E.D.D.I — Multi-Agent Orchestration Middleware for Conversational AI

[![Codacy Badge](https://app.codacy.com/project/badge/Grade/2c5d183d4bd24dbaa77427cfbf5d4074)](https://app.codacy.com/organizations/gh/labsai/dashboard?utm_source=github.com&utm_medium=referral&utm_content=labsai/EDDI&utm_campaign=Badge_Grade) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12355/badge)](https://www.bestpractices.dev/projects/12355) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/labsai/EDDI/badge)](https://securityscorecards.dev/viewer/?uri=github.com/labsai/EDDI)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12355/badge)](https://www.bestpractices.dev/projects/12355) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/labsai/EDDI/badge)](https://securityscorecards.dev/viewer/?uri=github.com/labsai/EDDI) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/2c5d183d4bd24dbaa77427cfbf5d4074)](https://app.codacy.com/organizations/gh/labsai/dashboard?utm_source=github.com&utm_medium=referral&utm_content=labsai/EDDI&utm_campaign=Badge_Grade)

[![CI](https://github.com/labsai/EDDI/actions/workflows/ci.yml/badge.svg)](https://github.com/labsai/EDDI/actions/workflows/ci.yml) [![CodeQL](https://github.com/labsai/EDDI/actions/workflows/codeql.yml/badge.svg)](https://github.com/labsai/EDDI/actions/workflows/codeql.yml) ![Tests](https://img.shields.io/badge/tests-4%2C600%2B-brightgreen) ![Coverage](https://img.shields.io/badge/coverage-%3E80%25-brightgreen)
[![CI](https://github.com/labsai/EDDI/actions/workflows/ci.yml/badge.svg)](https://github.com/labsai/EDDI/actions/workflows/ci.yml) [![CodeQL](https://github.com/labsai/EDDI/actions/workflows/codeql.yml/badge.svg)](https://github.com/labsai/EDDI/actions/workflows/codeql.yml) ![Tests](https://img.shields.io/badge/tests-5%2C100%2B-brightgreen) ![Coverage](https://img.shields.io/badge/coverage-%3E80%25-brightgreen)

[![Docker Pulls](https://img.shields.io/docker/pulls/labsai/eddi)](https://hub.docker.com/r/labsai/eddi) [![Repository: AI Ready](https://img.shields.io/badge/Repository-AI_Ready-blueviolet?logo=robot)](AGENTS.md)

**E.D.D.I** (Enhanced Dialog Driven Interface) is a production-grade, **config-driven multi-agent orchestration middleware** for conversational AI. It coordinates users, AI agents, and business systems through **intelligent routing, persistent memory, and API orchestration** — without writing code.

Built with **Java 25** and **Quarkus**. Ships as a **Red Hat-certified Docker image**. Native support for **MCP** (Model Context Protocol), **A2A** (Agent-to-Agent), **Slack**, **OpenAPI**, and **OAuth 2.0**.

**Latest version: 6.0.1** · [Website](https://eddi.labs.ai/) · [Documentation](https://docs.labs.ai/) · License: Apache 2.0
**Latest version: 6.0.2** · [Website](https://eddi.labs.ai/) · [Documentation](https://docs.labs.ai/) · License: Apache 2.0

---

Expand Down Expand Up @@ -407,7 +407,7 @@ Building a Quarkus app that talks to EDDI? Use the **[quarkus-eddi](https://gith
<dependency>
<groupId>io.quarkiverse.eddi</groupId>
<artifactId>quarkus-eddi</artifactId>
<version>6.0.1</version>
<version>6.0.2</version>
</dependency>
```

Expand Down
13 changes: 13 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ We will credit you in the security advisory unless you prefer to remain anonymou
- No `@JsonTypeInfo(use=Id.CLASS)` for untrusted payloads
- Read the [Security documentation](docs/security.md) before contributing security-sensitive code

## Release Integrity

All Docker image releases are **cryptographically signed** using [Sigstore cosign](https://github.com/sigstore/cosign) with keyless OIDC signing. Users can verify any image was built by the official CI pipeline:

```bash
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "^https://github\.com/labsai/EDDI/\.github/workflows/ci\.yml@refs/(heads/main|tags/.+)$" \
labsai/eddi:latest
```

For full details, see [Release Signing & Verification](docs/release-signing.md).

## Security-Related Documentation

- [Security Architecture](docs/security.md) — SSRF protection, sandboxed evaluation, tool hardening
Expand Down
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
- [RedHat OpenShift](redhat-openshift.md)
- [Setting Up EDDI on AWS with MongoDB Atlas](setup-eddi-on-aws-with-mongodb-atlas.md)
- [Release & Versioning Strategy](release-versioning.md)
- [Release Signing & Verification](release-signing.md)
- [Metrics & Monitoring](metrics.md)
- [Log Administration](log-administration.md)

Expand Down
154 changes: 154 additions & 0 deletions docs/release-signing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Release Signing & Verification

> **Audience:** Users, operators, and security auditors who need to verify the integrity of EDDI releases.

## What Is Signed?

EDDI's primary release artifacts are **Docker images** published to [Docker Hub: `labsai/eddi`](https://hub.docker.com/r/labsai/eddi). Starting with v6.0.0, every image pushed by the CI/CD pipeline is **cryptographically signed** using [Sigstore cosign](https://github.com/sigstore/cosign) with keyless OIDC signing.

This includes all images pushed after signing was enabled:
- Every build pushed from `main` (e.g., `labsai/eddi:6.0.0-b42`)
- Every release candidate (e.g., `labsai/eddi:6.0.0-RC2`)
- Every general availability release (e.g., `labsai/eddi:6.0.0`)
- The `latest` tag (updated on release tag pushes)

> **Note:** Images published before v6.0.0 are not signed. Signature verification only applies to images built after this feature was enabled.

---

## How Signing Works

EDDI uses **keyless signing** — there are no long-lived private keys to manage or protect:

1. The GitHub Actions CI pipeline builds and pushes the Docker image
2. GitHub provides an **OIDC identity token** proving the workflow identity
3. **Fulcio** (Sigstore's certificate authority) issues a short-lived certificate based on that identity
4. **cosign** signs the image using the ephemeral certificate
5. The signature is stored as an **OCI artifact** alongside the image in Docker Hub
6. The signing event is recorded in the **Rekor** public transparency log

```
GitHub Actions OIDC Token
┌─────────┐ ┌────────────┐
│ Fulcio │────▶│ Ephemeral │
│ (CA) │ │ Cert │
└─────────┘ └─────┬──────┘
┌──────────────┐
│ cosign sign │──▶ Signature stored in Docker Hub
└──────┬───────┘
┌──────────────┐
│ Rekor │──▶ Transparency log entry
│ (public) │
└──────────────┘
```

### Security Properties

| Property | How it's achieved |
|---|---|
| **No private key exposure** | Ephemeral keys exist only in runner memory for milliseconds — never stored anywhere |
| **Tamper evidence** | Signatures are recorded in the immutable Rekor transparency log |
| **Identity binding** | The signature proves the image was built by the `labsai/EDDI` GitHub Actions workflow |
| **Private key not on distribution site** | Docker Hub only stores the signature and public certificate, never a private key |

---

## How to Verify

### Prerequisites

Install cosign:

```bash
# macOS
brew install cosign

# Linux (download binary)
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign

# Or download from https://github.com/sigstore/cosign/releases
```
Comment thread
ginccc marked this conversation as resolved.

### Verify an Image

```bash
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "^https://github\.com/labsai/EDDI/\.github/workflows/ci\.yml@refs/(heads/main|tags/.+)$" \
labsai/eddi:6.0.0
Comment thread
ginccc marked this conversation as resolved.
```

Replace `6.0.0` with any tag you want to verify (`latest`, `6.0.0-RC2`, `6.0.0-b42`, etc.).

**Successful output** will show the verified certificate chain and Rekor log entry:

```
Verification for docker.io/labsai/eddi:6.0.0 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- Existence of the claims in the transparency log was verified offline
- The code-signing certificate was verified using trusted certificate authority
```

### Verify by Digest (Recommended)

For maximum security, verify by image digest instead of tag:

```bash
# Get the digest
docker pull labsai/eddi:6.0.0
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' labsai/eddi:6.0.0)

# Verify the digest
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "^https://github\.com/labsai/EDDI/\.github/workflows/ci\.yml@refs/(heads/main|tags/.+)$" \
$DIGEST
```

### Inspect the Transparency Log

Every signature is publicly recorded in [Rekor](https://rekor.sigstore.dev/). When you run `cosign verify`, the output includes the Rekor log index. You can also inspect the full transparency log entry for a signed image:

```bash
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "^https://github\.com/labsai/EDDI/\.github/workflows/ci\.yml@refs/(heads/main|tags/.+)$" \
--output-text \
labsai/eddi:6.0.0
```

This outputs the full certificate chain and Rekor log entry as JSON.

---

## Git Tag Signing

For version tags in the Git repository (e.g., `v6.0.0`, `v6.0.0-RC2`), maintainers sign tags using GPG or SSH keys:

```bash
# Create a signed tag
git tag -s v6.0.0 -m "Release 6.0.0"
git push origin v6.0.0

# Verify a signed tag
git tag -v v6.0.0
```

> **Note:** The primary release integrity guarantee is provided by the Docker image signing described above. Git tag signing provides an additional layer of assurance that the tag was created by an authorized maintainer.

---

## Related Documentation

- [Release & Versioning Strategy](release-versioning.md) — Docker tags, branching model, how to release
- [Security Policy](../SECURITY.md) — Vulnerability reporting, scope, security practices
- [CI/CD Pipeline](../.github/workflows/ci.yml) — The signing implementation
19 changes: 19 additions & 0 deletions docs/release-versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,22 @@ After tagging `v6.0.0`, update `pom.xml` on the feature branch to the next versi
# Update pom.xml: <version>6.1.0</version>
# CI builds will now produce 6.1.0-b1, 6.1.0-b2, etc.
```

---

## Release Signing

All Docker images pushed by CI are **cryptographically signed** using [Sigstore cosign](https://github.com/sigstore/cosign) with keyless OIDC signing. This ensures that users can verify any image was built by the official `labsai/EDDI` GitHub Actions pipeline.

For full details on how signing works and how to verify images, see [Release Signing & Verification](release-signing.md).

### Signed Git Tags

When creating release tags, use signed tags:

```bash
# Instead of: git tag v6.0.0
# Use:
git tag -s v6.0.0 -m "Release 6.0.0"
git push origin v6.0.0
```
2 changes: 1 addition & 1 deletion helm/eddi/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: >-
and integrate with LLM providers via a unified REST API.
type: application
version: 1.0.0
appVersion: "6.0.1"
appVersion: "6.0.2"
home: https://eddi.labs.ai
sources:
- https://github.com/labsai/EDDI
Expand Down
4 changes: 2 additions & 2 deletions k8s/base/eddi-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ metadata:
name: eddi
labels:
app.kubernetes.io/name: eddi
app.kubernetes.io/version: "6.0.1"
app.kubernetes.io/version: "6.0.2"
app.kubernetes.io/component: server
app.kubernetes.io/part-of: eddi
spec:
Expand All @@ -23,7 +23,7 @@ spec:
metadata:
labels:
app.kubernetes.io/name: eddi
app.kubernetes.io/version: "6.0.1"
app.kubernetes.io/version: "6.0.2"
app.kubernetes.io/component: server
app.kubernetes.io/part-of: eddi
annotations:
Expand Down
4 changes: 2 additions & 2 deletions k8s/quickstart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ metadata:
namespace: eddi
labels:
app.kubernetes.io/name: eddi
app.kubernetes.io/version: "6.0.1"
app.kubernetes.io/version: "6.0.2"
app.kubernetes.io/component: server
app.kubernetes.io/part-of: eddi
spec:
Expand All @@ -187,7 +187,7 @@ spec:
metadata:
labels:
app.kubernetes.io/name: eddi
app.kubernetes.io/version: "6.0.1"
app.kubernetes.io/version: "6.0.2"
app.kubernetes.io/component: server
app.kubernetes.io/part-of: eddi
annotations:
Expand Down
Loading
Loading