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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
Expand Down
39 changes: 39 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Pages

# The site runs the wheel built from this commit in the browser (Pyodide), so it always matches main.
on:
push:
branches: ["main"]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- name: Build wheel and assemble site
run: |
uv build --wheel --out-dir dist
mkdir -p _site/wheels
cp site/index.html _site/
cp dist/*.whl _site/wheels/
(cd dist && ls *.whl) > _site/wheels/latest.txt
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: _site
- id: deployment
uses: actions/deploy-pages@v4
35 changes: 35 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Release

# Publishing a GitHub release uploads that version to PyPI via trusted publishing (no stored token).
on:
release:
types: [published]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- name: Tag must match the package version
run: test "v$(uv version --short)" = "${{ github.event.release.tag_name }}"
- run: uv build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

pypi:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/sentinel-agent-gateway/
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

**A deny-by-default security gateway, human-approval sandbox and MCP proxy for AI agents**

[![PyPI](https://img.shields.io/pypi/v/sentinel-agent-gateway.svg)](https://pypi.org/project/sentinel-agent-gateway/)
[![CI](https://github.com/Chirudeva-Reddy/sentinel-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/Chirudeva-Reddy/sentinel-agent/actions)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue.svg)](https://www.python.org/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%20%E2%80%93%203.14-blue.svg)](https://www.python.org/)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)

**[Live demo: runs in your browser](https://chirudeva-reddy.github.io/sentinel-agent/)** · `pip install sentinel-agent-gateway`

[Why](#why) • [Quickstart](#quickstart) • [How it works](#how-it-works) • [Evaluation](#evaluation) • [Architecture](docs/ARCHITECTURE.md)

</div>
Expand Down Expand Up @@ -40,8 +43,8 @@ between the agent and its tools and checks **both directions**:
## Quickstart

```bash
git clone https://github.com/Chirudeva-Reddy/sentinel-agent.git && cd sentinel-agent
uv sync --all-extras # or: pip install -e ".[server,dashboard,mcp]"
pip install "sentinel-agent-gateway[server,dashboard,mcp,demo]" # the import name is `sentinel`
# or from source: git clone https://github.com/Chirudeva-Reddy/sentinel-agent.git && cd sentinel-agent && uv sync --all-extras
```

```bash
Expand Down
17 changes: 14 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
[project]
name = "sentinel-agent"
version = "0.1.0"
description = "Zero-Trust Security Gateway & Human-in-the-Loop Sandbox for Autonomous AI Agents"
name = "sentinel-agent-gateway"
version = "0.2.0"
description = "Deny-by-default security gateway, human-approval sandbox and MCP proxy for AI agents"
readme = "README.md"
authors = [
{ name = "Chirudeva Reddy", email = "chirudevareddy03@gmail.com" }
]
requires-python = ">=3.10"
license = "Apache-2.0"
keywords = ["ai-agents", "llm-security", "prompt-injection", "mcp", "guardrails"]
classifiers = [
"Programming Language :: Python :: 3",
"Topic :: Security",
"Intended Audience :: Developers",
]
dependencies = [
"pydantic>=2.7.0",
"typer>=0.12.0",
Expand Down Expand Up @@ -42,6 +49,10 @@ dev = [
"ruff>=0.4.0",
]

[project.urls]
Homepage = "https://chirudeva-reddy.github.io/sentinel-agent/"
Repository = "https://github.com/Chirudeva-Reddy/sentinel-agent"

[project.scripts]
sentinel = "sentinel.cli:app"

Expand Down
5 changes: 3 additions & 2 deletions sentinel/detectors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ def analyze(self, call: NormalizedCall) -> DetectorFinding: ...


def load_detectors(policy: PolicyEngine) -> list[Detector]:
eps = sorted(entry_points(group=GROUP), key=lambda ep: ep.name)
# Dedupe by (name, target): stale metadata from an old install would otherwise double-count a detector.
eps = sorted({(ep.name, ep.value): ep for ep in entry_points(group=GROUP)}.values(), key=lambda ep: ep.name)
if not eps:
# Fail closed: a gateway with no detectors would allow everything.
raise RuntimeError(f"No detectors registered under '{GROUP}'. Is sentinel-agent installed (pip install -e .)?")
raise RuntimeError(f"No detectors registered under '{GROUP}'. Is sentinel-agent-gateway installed?")
return [ep.load()(policy) for ep in eps]


Expand Down
Loading
Loading