Skip to content

Commit 13cb033

Browse files
authored
Merge pull request #22 from aitechnav/fix_attack_detect
ci: docs
2 parents 9559857 + 7ffce40 commit 13cb033

28 files changed

Lines changed: 1905 additions & 132 deletions

.github/workflows/docker.yml

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
name: Publish Docker Image
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
inputs:
8+
version:
9+
description: "Version to publish (e.g. 0.0.9). Must match pyproject.toml."
10+
required: true
11+
type: string
12+
branch:
13+
description: "Branch to build from"
14+
required: true
15+
type: string
16+
default: "main"
17+
publish_dockerhub:
18+
description: "Also publish to Docker Hub"
19+
required: true
20+
type: boolean
21+
default: false
22+
dockerhub_image:
23+
description: "Docker Hub image, e.g. aitechnav/sentinelguard-gateway"
24+
required: false
25+
type: string
26+
default: ""
27+
28+
permissions:
29+
contents: read
30+
id-token: write
31+
packages: write
32+
33+
jobs:
34+
docker:
35+
runs-on: ubuntu-latest
36+
37+
steps:
38+
- name: Resolve release inputs
39+
id: release
40+
env:
41+
DISPATCH_VERSION: ${{ inputs.version }}
42+
DISPATCH_BRANCH: ${{ inputs.branch }}
43+
RELEASE_TAG: ${{ github.event.release.tag_name }}
44+
run: |
45+
if [ "${{ github.event_name }}" = "release" ]; then
46+
VERSION="${RELEASE_TAG#v}"
47+
REF="${RELEASE_TAG}"
48+
else
49+
VERSION="${DISPATCH_VERSION}"
50+
REF="${DISPATCH_BRANCH}"
51+
fi
52+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
53+
echo "ref=${REF}" >> "$GITHUB_OUTPUT"
54+
55+
- uses: actions/checkout@v4
56+
with:
57+
ref: ${{ steps.release.outputs.ref }}
58+
59+
- name: Verify version matches
60+
run: |
61+
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
62+
if [ "$VERSION" != "${{ steps.release.outputs.version }}" ]; then
63+
echo "ERROR: pyproject.toml version ($VERSION) does not match ${{ steps.release.outputs.version }}"
64+
exit 1
65+
fi
66+
echo "Version verified: $VERSION"
67+
68+
- name: Prepare image tags
69+
id: images
70+
env:
71+
VERSION: ${{ steps.release.outputs.version }}
72+
DOCKERHUB_IMAGE_INPUT: ${{ inputs.dockerhub_image }}
73+
DOCKERHUB_IMAGE_VAR: ${{ vars.DOCKERHUB_IMAGE }}
74+
PUBLISH_DOCKERHUB_INPUT: ${{ inputs.publish_dockerhub }}
75+
DOCKERHUB_USERNAME_SET: ${{ secrets.DOCKERHUB_USERNAME != '' }}
76+
DOCKERHUB_TOKEN_SET: ${{ secrets.DOCKERHUB_TOKEN != '' }}
77+
run: |
78+
OWNER=$(echo "$GITHUB_REPOSITORY_OWNER" | tr '[:upper:]' '[:lower:]')
79+
GHCR_IMAGE="ghcr.io/${OWNER}/sentinelguard-gateway"
80+
DOCKERHUB_IMAGE=$(echo "${DOCKERHUB_IMAGE_INPUT:-$DOCKERHUB_IMAGE_VAR}" | tr '[:upper:]' '[:lower:]')
81+
PUBLISH_DOCKERHUB="${PUBLISH_DOCKERHUB_INPUT:-false}"
82+
83+
if [ "${{ github.event_name }}" = "release" ] && [ -n "$DOCKERHUB_IMAGE" ]; then
84+
PUBLISH_DOCKERHUB=true
85+
fi
86+
87+
if [ "$PUBLISH_DOCKERHUB" = "true" ] && [ -z "$DOCKERHUB_IMAGE" ]; then
88+
echo "ERROR: Docker Hub publishing was requested, but no Docker Hub image was configured."
89+
echo "Set the DOCKERHUB_IMAGE repo variable or workflow input, for example aitechnav/sentinelguard-gateway."
90+
exit 1
91+
fi
92+
93+
if [ "$PUBLISH_DOCKERHUB" = "true" ] && { [ "$DOCKERHUB_USERNAME_SET" != "true" ] || [ "$DOCKERHUB_TOKEN_SET" != "true" ]; }; then
94+
echo "ERROR: Docker Hub publishing was requested, but DOCKERHUB_USERNAME or DOCKERHUB_TOKEN is missing."
95+
exit 1
96+
fi
97+
98+
{
99+
echo "tags<<EOF"
100+
echo "${GHCR_IMAGE}:${VERSION}"
101+
echo "${GHCR_IMAGE}:latest"
102+
if [ "$PUBLISH_DOCKERHUB" = "true" ] && [ -n "$DOCKERHUB_IMAGE" ]; then
103+
echo "${DOCKERHUB_IMAGE}:${VERSION}"
104+
echo "${DOCKERHUB_IMAGE}:latest"
105+
fi
106+
echo "EOF"
107+
echo "ghcr_image=${GHCR_IMAGE}"
108+
echo "dockerhub_image=${DOCKERHUB_IMAGE}"
109+
echo "publish_dockerhub=${PUBLISH_DOCKERHUB}"
110+
} >> "$GITHUB_OUTPUT"
111+
112+
- name: Set up Docker Buildx
113+
uses: docker/setup-buildx-action@v3
114+
115+
- name: Login to GHCR
116+
uses: docker/login-action@v3
117+
with:
118+
registry: ghcr.io
119+
username: ${{ github.actor }}
120+
password: ${{ secrets.GITHUB_TOKEN }}
121+
122+
- name: Login to Docker Hub
123+
if: steps.images.outputs.publish_dockerhub == 'true'
124+
uses: docker/login-action@v3
125+
with:
126+
username: ${{ secrets.DOCKERHUB_USERNAME }}
127+
password: ${{ secrets.DOCKERHUB_TOKEN }}
128+
129+
- name: Build and push Docker image
130+
id: build
131+
uses: docker/build-push-action@v6
132+
with:
133+
context: .
134+
file: ./Dockerfile
135+
platforms: linux/amd64,linux/arm64
136+
push: true
137+
tags: ${{ steps.images.outputs.tags }}
138+
build-args: |
139+
SENTINELGUARD_EXTRAS=gateway,monitoring
140+
labels: |
141+
org.opencontainers.image.title=SentinelGuard Gateway
142+
org.opencontainers.image.description=SentinelGuard OpenAI-compatible LLM gateway with security scanning
143+
org.opencontainers.image.version=${{ steps.release.outputs.version }}
144+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
145+
org.opencontainers.image.revision=${{ github.sha }}
146+
provenance: true
147+
sbom: true
148+
149+
- name: Install cosign
150+
uses: sigstore/cosign-installer@v3
151+
152+
- name: Sign published image tags
153+
env:
154+
TAGS: ${{ steps.images.outputs.tags }}
155+
DIGEST: ${{ steps.build.outputs.digest }}
156+
run: |
157+
while IFS= read -r tag; do
158+
if [ -n "$tag" ]; then
159+
cosign sign --yes "${tag}@${DIGEST}"
160+
fi
161+
done <<EOF
162+
${TAGS}
163+
EOF
164+
165+
- name: Summary
166+
run: |
167+
echo "## Docker Publish Successful" >> "$GITHUB_STEP_SUMMARY"
168+
echo "- Version: ${{ steps.release.outputs.version }}" >> "$GITHUB_STEP_SUMMARY"
169+
echo "- Tags:" >> "$GITHUB_STEP_SUMMARY"
170+
while IFS= read -r tag; do
171+
if [ -n "$tag" ]; then
172+
echo " - \`${tag}\`" >> "$GITHUB_STEP_SUMMARY"
173+
fi
174+
done <<EOF
175+
${{ steps.images.outputs.tags }}
176+
EOF
177+
echo "" >> "$GITHUB_STEP_SUMMARY"
178+
echo "SBOM/provenance attestations were generated by Docker Buildx." >> "$GITHUB_STEP_SUMMARY"
179+
echo "Image tags were signed with cosign keyless signing." >> "$GITHUB_STEP_SUMMARY"

.github/workflows/docs.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Publish Docs
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- "docs/**"
8+
- "overrides/**"
9+
- "mkdocs.yml"
10+
- "requirements-docs.txt"
11+
- ".github/workflows/docs.yml"
12+
workflow_dispatch:
13+
14+
permissions:
15+
contents: read
16+
pages: write
17+
id-token: write
18+
19+
concurrency:
20+
group: pages
21+
cancel-in-progress: false
22+
23+
jobs:
24+
build:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- name: Set up Python
30+
uses: actions/setup-python@v5
31+
with:
32+
python-version: "3.12"
33+
34+
- name: Install documentation dependencies
35+
run: |
36+
python -m pip install --upgrade pip
37+
pip install -r requirements-docs.txt
38+
39+
- name: Build documentation
40+
run: mkdocs build --strict --site-dir site
41+
42+
- name: Upload GitHub Pages artifact
43+
uses: actions/upload-pages-artifact@v3
44+
with:
45+
path: site
46+
47+
deploy:
48+
needs: build
49+
runs-on: ubuntu-latest
50+
environment:
51+
name: github-pages
52+
url: ${{ steps.deployment.outputs.page_url }}
53+
steps:
54+
- name: Deploy to GitHub Pages
55+
id: deployment
56+
uses: actions/deploy-pages@v4

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ RUN python -m pip install --upgrade pip \
1717
EXPOSE 8080
1818

1919
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
20-
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/gateway/health', timeout=3)"
20+
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/gateway/v1/health', timeout=3)"
2121

2222
ENTRYPOINT ["sentinelguard"]
2323
CMD ["gateway", "--host", "0.0.0.0", "--port", "8080"]

QUICKSTART.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ pip install "sentinelguard[gateway,monitoring]"
1515
sentinelguard init
1616
```
1717

18+
For Docker Compose, run `sentinelguard init`, copy `.env.example` to `.env`,
19+
set a provider key, then start `docker-compose.sentinelguard.yml`.
20+
1821
For optional model-backed detection with automatic background warmup:
1922

2023
```bash
@@ -94,12 +97,26 @@ sentinelguard scan prompt "Ignore previous instructions" --format json
9497
# List available scanners
9598
sentinelguard scanners list
9699

100+
# Create and edit scanner config
101+
sentinelguard config init --preset standard --output sentinelguard.yaml
102+
sentinelguard config set prompt_scanners.pii.threshold 0.3 --file sentinelguard.yaml
103+
sentinelguard config disable toxicity --type prompt --file sentinelguard.yaml
104+
105+
# Edit gateway routing and security config
106+
sentinelguard gateway-config set gateway.routing_strategy weighted --file sentinelguard-gateway.yaml
107+
sentinelguard gateway-config set gateway.cache_enabled true --file sentinelguard-gateway.yaml
108+
sentinelguard gateway-config get gateway.providers.0.name --file sentinelguard-gateway.yaml
109+
97110
# Start API server
98111
sentinelguard serve --port 8000
99112

100113
# Create gateway starter files
101114
sentinelguard init
102115

116+
# Start the generated Docker gateway
117+
cp .env.example .env
118+
docker compose -f docker-compose.sentinelguard.yml up --build
119+
103120
# Start OpenAI-compatible LLM gateway from generated config
104121
export OPENAI_API_KEY="sk-..."
105122
export SENTINELGUARD_GATEWAY_API_KEY="local-gateway-token"

README.md

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,22 @@ print(report.summary())
5353
pip install sentinelguard
5454
```
5555

56+
## Documentation Website
57+
58+
The documentation site is built with MkDocs Material and published with GitHub
59+
Pages:
60+
61+
```text
62+
https://aitechnav.github.io/Sentinel_Guard/
63+
```
64+
65+
Build it locally:
66+
67+
```bash
68+
pip install -r requirements-docs.txt
69+
mkdocs serve
70+
```
71+
5672
For gateway mode, install the gateway extra and generate starter files:
5773

5874
```bash
@@ -65,6 +81,7 @@ sentinelguard init
6581
- `sentinelguard.yaml` for scanner policy
6682
- `sentinelguard-gateway.yaml` for routing, provider, auth, cache, and audit settings
6783
- `.env.example` for provider and gateway keys
84+
- `Dockerfile.sentinelguard` for a small gateway image built from PyPI
6885
- `docker-compose.sentinelguard.yml` for container-based gateway deployment
6986
- `README.sentinelguard.md` with local next steps
7087

@@ -193,6 +210,19 @@ For a quick single-provider run without generated files:
193210
sentinelguard gateway --provider openai --port 8080
194211
```
195212

213+
Manage scanner and gateway YAML from the CLI:
214+
215+
```bash
216+
# Scanner policy
217+
sentinelguard config set prompt_scanners.pii.threshold 0.3 --file sentinelguard.yaml
218+
sentinelguard config disable toxicity --type prompt --file sentinelguard.yaml
219+
220+
# Gateway routing/security settings
221+
sentinelguard gateway-config set gateway.routing_strategy weighted --file sentinelguard-gateway.yaml
222+
sentinelguard gateway-config set gateway.cache_enabled true --file sentinelguard-gateway.yaml
223+
sentinelguard gateway-config get gateway.providers.0.name --file sentinelguard-gateway.yaml
224+
```
225+
196226
Or run the gateway as a standalone Docker proxy:
197227

198228
```bash
@@ -208,15 +238,20 @@ docker run --rm -p 8080:8080 \
208238
With Docker Compose:
209239

210240
```bash
211-
docker build -t sentinelguard-gateway:local .
212-
sentinelguard init --docker-image sentinelguard-gateway:local
241+
sentinelguard init
213242
cp .env.example .env
214243
# Edit .env and set at least one upstream provider key.
215244
export OPENAI_API_KEY="sk-..."
216245
export SENTINELGUARD_GATEWAY_API_KEY="local-gateway-token"
217-
docker compose -f docker-compose.sentinelguard.yml up
246+
docker compose -f docker-compose.sentinelguard.yml up --build
218247
```
219248

249+
The generated Docker setup builds `Dockerfile.sentinelguard`, which installs
250+
the configured SentinelGuard version from PyPI. Set `SENTINELGUARD_VERSION` in
251+
`.env` if you want the container to use a different released package version.
252+
Official release images can be published through the GitHub Actions workflow
253+
documented in `docs/docker-release.md`.
254+
220255
From this repository, the included `docker-compose.yml` can also build the
221256
gateway directly. For local Hugging Face model-backed detection inside that
222257
image:
@@ -400,10 +435,21 @@ gateway:
400435
weight: 1
401436
```
402437
403-
The gateway exposes operational discovery endpoints:
438+
The gateway exposes stable management endpoints under `/gateway/v1`. Older
439+
unversioned endpoints remain available as compatibility aliases.
404440

405441
```text
442+
GET /gateway/v1/contract
443+
GET /gateway/v1/health
444+
GET /gateway/v1/routes
445+
GET /gateway/v1/models
446+
GET /gateway/v1/usage
447+
GET /gateway/v1/provider-health
448+
449+
# OpenAI-compatible model endpoint
406450
GET /v1/models
451+
452+
# Compatibility aliases
407453
GET /models
408454
GET /routes
409455
GET /gateway/usage
@@ -413,6 +459,10 @@ GET /gateway/health
413459
GET /admin
414460
```
415461

462+
Use `/gateway/v1/contract` as the stable API contract for dashboards,
463+
automation, and operational integrations. See `docs/gateway-api.md` for the
464+
gateway API stability rule.
465+
416466
Gateway state can run in memory for local development or in SQLite for
417467
persistent virtual-key usage, spend, and budget counters. Response caching can
418468
use memory, SQLite, or Redis:

0 commit comments

Comments
 (0)