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
5 changes: 5 additions & 0 deletions .env.ci
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ NILAI_GUNICORN_WORKERS = 10
# - Do not put "https://" or "http://" in the domain name or / at the end
NILAI_SERVER_DOMAIN = "localhost"

# Attestation Config
ATTESTATION_HOST = "attestation"
ATTESTATION_PORT = 8080



# Postgres Docker Compose Config
POSTGRES_HOST = "postgres"
Expand Down
3 changes: 3 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ NILAI_GUNICORN_WORKERS = 10
# - Do not put "https://" or "http://" in the domain name or / at the end
NILAI_SERVER_DOMAIN = "localhost"

# Attestation Config
ATTESTATION_HOST = "attestation"
ATTESTATION_PORT = 8080

# Postgres Docker Compose Config
POSTGRES_HOST = "postgres"
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ jobs:
- name: Build vllm
run: docker build -t nillion/nilai-vllm:latest -f docker/vllm.Dockerfile .

- name: Build attestation
run: docker build -t nillion/nilai-attestation:latest -f docker/attestation.Dockerfile .

- name: Build nilal API
run: docker build -t nillion/nilai-api:latest -f docker/api.Dockerfile --target nilai .

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ nilAI is a platform designed to run on Confidential VMs with Trusted Execution E

#### Development Environment
```shell
# Build nilai_attestation endpoint
docker build -t nillion/nilai-attestation:latest -f docker/attestation.Dockerfile .
# Build vLLM docker container
docker build -t nillion/nilai-vllm:latest -f docker/vllm.Dockerfile .
# Build nilai_api container
Expand All @@ -50,6 +52,8 @@ docker compose -f docker-compose.yml \

#### Production Environment
```shell
# Build nilai_attestation endpoint
docker build -t nillion/nilai-attestation:latest -f docker/attestation.Dockerfile .
# Build vLLM docker container
docker build -t nillion/nilai-vllm:latest -f docker/vllm.Dockerfile .
# Build nilai_api container
Expand All @@ -68,6 +72,8 @@ up -d
#### Testing Without GPU

```shell
# Build nilai_attestation endpoint
docker build -t nillion/nilai-attestation:latest -f docker/attestation.Dockerfile .
# Build vLLM docker container
docker build -t nillion/nilai-vllm:latest -f docker/vllm.Dockerfile .
# Build nilai_api container
Expand All @@ -77,7 +83,7 @@ To deploy:
```shell
docker compose -f docker-compose.yml \
-f docker-compose.dev.yml \
-f docker/compose/docker-compose.llama-1b-gpu.yml \
-f docker/compose/docker-compose.llama-1b-cpu.yml \
up -d
```

Expand Down
2 changes: 1 addition & 1 deletion caddy/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
}

handle {
reverse_proxy api:8443
reverse_proxy api:8080
}
}
8 changes: 7 additions & 1 deletion docker-compose.dev.macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ services:
platform: linux/amd64
ports:
- "8080:8080"
- "8443:8443"
volumes:
- ./nilai-api/:/app/nilai-api/
- ./packages/:/app/packages/
attestation:
ports:
- "8081:8080"
volumes:
- ./nilai-attestation/:/app/nilai-attestation/
- ./packages/:/app/packages/
redis:
ports:
- "6379:6379"
Expand Down
7 changes: 6 additions & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ services:
api:
ports:
- "8080:8080"
- "8443:8443"
volumes:
- ./nilai-api/:/app/nilai-api/
- ./packages/:/app/packages/
attestation:
ports:
- "8081:8080"
volumes:
- ./nilai-attestation/:/app/nilai-attestation/
- ./packages/:/app/packages/
redis:
ports:
- "6379:6379"
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
services:
api:
attestation:
deploy:
resources:
reservations:
Expand Down
16 changes: 16 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ services:
environment:
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379
restart: unless-stopped
healthcheck:
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 10s
Expand All @@ -16,6 +17,7 @@ services:
image: 'redis:latest'
networks:
- frontend_net
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
Expand Down Expand Up @@ -126,6 +128,20 @@ services:
retries: 3
start_period: 15s
timeout: 10s
attestation:
image: nillion/nilai-attestation:latest
restart: unless-stopped
networks:
- backend_net
env_file:
- .env
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
retries: 3
start_period: 15s
timeout: 10s

caddy:
image: caddy:latest
container_name: caddy
Expand Down
3 changes: 1 addition & 2 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ docker build -t nillion/nilai-api:latest -f docker/api.Dockerfile .

docker run -it --rm \
-p 8080:8080 \
-p 8443:8443 \
-v hugging_face_models:/root/.cache/huggingface \
-v $(pwd)/db/users.sqlite:/app/db/users.sqlite \
nillion/nilai-api:latest
Expand All @@ -34,4 +33,4 @@ docker run -d --name etcd-server \
--env ALLOW_NONE_AUTHENTICATION=yes \
--env ETCD_ADVERTISE_CLIENT_URLS=http://etcd-server:2379 \
bitnami/etcd:latest
```
```
11 changes: 1 addition & 10 deletions docker/api.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
FROM golang:1.23.3-bullseye AS sev

COPY --link nilai-api/src/nilai_api/sev /app/sev
WORKDIR /app/sev

RUN go build -o libsevguest.so -buildmode=c-shared main.go

FROM python:3.12-slim AS nilai

COPY --link . /app/
COPY --from=sev /app/sev/libsevguest.so /app/nilai-api/src/nilai_api/sev/libsevguest.so
COPY --from=sev /app/sev/libsevguest.h /app/nilai-api/src/nilai_api/sev/libsevguest.h

WORKDIR /app/nilai-api/

Expand All @@ -21,6 +12,6 @@ rm -rf /var/lib/apt/lists/* && \
pip install uv && \
uv sync

EXPOSE 8080 8443
EXPOSE 8080

CMD ["./launch.sh"]
26 changes: 26 additions & 0 deletions docker/attestation.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FROM golang:1.23.3-bullseye AS sev

COPY --link nilai-attestation/src/nilai_attestation/attestation/sev /app/sev
WORKDIR /app/sev

RUN go build -o libsevguest.so -buildmode=c-shared main.go

FROM python:3.12-slim AS nilai

COPY --link . /app/
COPY --from=sev /app/sev/libsevguest.so /app/nilai-attestation/src/nilai_attestation/sev/libsevguest.so
COPY --from=sev /app/sev/libsevguest.h /app/nilai-attestation/src/nilai_attestation/sev/libsevguest.h

WORKDIR /app/nilai-attestation/

RUN apt-get update && \
apt-get install build-essential curl -y && \
apt-get clean && \
apt-get autoremove && \
rm -rf /var/lib/apt/lists/* && \
pip install uv && \
uv sync

EXPOSE 8080

CMD ["uv", "run", "gunicorn", "-c", "gunicorn.conf.py", "nilai_attestation.app:app"]
4 changes: 2 additions & 2 deletions nilai-api/gunicorn.conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from nilai_common.config import SETTINGS

# Bind to address and port
bind = ["0.0.0.0:8080", "0.0.0.0:8443"]
bind = ["0.0.0.0:8080"]

# Set the number of workers (2)
workers = SETTINGS["gunicorn_workers"]
workers = SETTINGS.gunicorn_workers

# Set the number of threads per worker (16)
threads = 1
Expand Down
1 change: 0 additions & 1 deletion nilai-api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,3 @@ build-backend = "hatchling.build"

[tool.uv.sources]
nilai-common = { workspace = true }
verifier = { workspace = true }
34 changes: 34 additions & 0 deletions nilai-api/src/nilai_api/attestation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from fastapi import HTTPException
import httpx
from nilai_common import SETTINGS, Nonce, AttestationReport
from nilai_common.logger import setup_logger

logger = setup_logger(__name__)


async def get_attestation_report(
nonce: Nonce | None,
) -> AttestationReport:
"""Get the attestation report for the given nonce"""

try:
attestation_url = f"http://{SETTINGS.attestation_host}:{SETTINGS.attestation_port}/attestation/report"
async with httpx.AsyncClient() as client:
response: httpx.Response = await client.get(attestation_url, params=nonce)
report = AttestationReport(**response.json())
return report
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))


async def verify_attestation_report(attestation_report: AttestationReport) -> bool:
"""Verify the attestation report"""
try:
attestation_url = f"http://{SETTINGS.attestation_host}:{SETTINGS.attestation_port}/attestation/verify"
async with httpx.AsyncClient() as client:
response: httpx.Response = await client.get(
attestation_url, params=attestation_report.model_dump()
)
return response.json()
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
20 changes: 12 additions & 8 deletions nilai-api/src/nilai_api/routers/private.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import logging
import os
from base64 import b64encode
from typing import AsyncGenerator, Union, List, Tuple
from typing import AsyncGenerator, Optional, Union, List, Tuple
from nilai_api.attestation import get_attestation_report
from nilai_api.handlers.nilrag import handle_nilrag

from fastapi import APIRouter, Body, Depends, HTTPException, status, Request
Expand All @@ -18,12 +19,13 @@

# Internal libraries
from nilai_common import (
AttestationResponse,
AttestationReport,
ChatRequest,
Message,
ModelMetadata,
SignedChatCompletion,
Usage,
Nonce,
)
from openai import AsyncOpenAI, OpenAI

Expand Down Expand Up @@ -55,10 +57,13 @@ async def get_usage(user: UserModel = Depends(get_user)) -> Usage:


@router.get("/v1/attestation/report", tags=["Attestation"])
async def get_attestation(user: UserModel = Depends(get_user)) -> AttestationResponse:
async def get_attestation(
nonce: Optional[Nonce] = None, user: UserModel = Depends(get_user)
) -> AttestationReport:
"""
Generate a cryptographic attestation report.

- **attestation_request**: Attestation request containing a nonce
- **user**: Authenticated user information (through HTTP Bearer header)
- **Returns**: Attestation details for service verification

Expand All @@ -70,11 +75,10 @@ async def get_attestation(user: UserModel = Depends(get_user)) -> AttestationRes
### Security Note
Provides cryptographic proof of the service's integrity and environment.
"""
return AttestationResponse(
verifying_key=state.verifying_key,
cpu_attestation=state.cpu_attestation,
gpu_attestation=state.gpu_attestation,
)

attestation_report = await get_attestation_report(nonce)
attestation_report.verifying_key = state.verifying_key
return attestation_report


@router.get("/v1/models", tags=["Model"])
Expand Down
14 changes: 13 additions & 1 deletion nilai-api/src/nilai_api/routers/public.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from nilai_api.state import state

# Internal libraries
from nilai_common import HealthCheckResponse
from nilai_common import HealthCheckResponse, AttestationReport
from nilai_api.attestation import verify_attestation_report

router = APIRouter()

Expand Down Expand Up @@ -33,3 +34,14 @@ async def health_check() -> HealthCheckResponse:
```
"""
return HealthCheckResponse(status="ok", uptime=state.uptime)


@router.post("/attestation/verify", tags=["Attestation"])
async def post_attestation(attestation_report: AttestationReport) -> bool:
"""
Verify a cryptographic attestation report.

- **attestation_report**: Attestation report to verify
- **Returns**: True if the attestation report is valid, False otherwise
"""
return await verify_attestation_report(attestation_report)
Loading
Loading