agentgate-pdp 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentgate-pdp
3
+ Version: 0.2.0
4
+ Summary: Context-aware trust authorization for agentic AI systems
5
+ Author-email: AgentGate <olame109@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/agentgate/agentgate
8
+ Project-URL: Documentation, https://agentgate.dev/docs
9
+ Keywords: ai,agents,security,authorization,llm,langchain,policy,trust,pdp,agentic
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Security
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: httpx>=0.27.0
21
+ Provides-Extra: langchain
22
+ Requires-Dist: langchain-core>=0.3.0; extra == "langchain"
23
+ Requires-Dist: langchain-anthropic>=0.2.0; extra == "langchain"
24
+ Requires-Dist: langgraph>=0.2.0; extra == "langchain"
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
28
+
29
+ # AgentGate
30
+
31
+ **A Policy Decision Point (PDP) for AI agents. Every action evaluated before it executes.**
32
+
33
+ [![PyPI version](https://img.shields.io/pypi/v/agentgate.svg)](https://pypi.org/project/agentgate/)
34
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
36
+
37
+ ---
38
+
39
+ AI agents make decisions autonomously. AgentGate makes sure those decisions are **authorized**.
40
+
41
+ It sits between your agent and its tools. Before any action runs — reading a file, calling an API, writing to a database — AgentGate checks it against identity, scope, purpose, and behavioral context. The response is `PERMIT`, `ESCALATE`, or `DENY` in milliseconds.
42
+
43
+ ```
44
+ Agent wants to delete /confidential/salary.xlsx
45
+ ↓
46
+ AgentGate PDP
47
+ ┌──────────────────────────────┐
48
+ │ Identity check ✓ │
49
+ │ Scope check ✗ (!) │
50
+ │ Purpose alignment ✗ │
51
+ │ Behavioral check ✓ │
52
+ │ │
53
+ │ Trust score: 18/100 │
54
+ │ Decision: DENY │
55
+ └──────────────────────────────┘
56
+ ↓
57
+ Action never executes
58
+ ```
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install agentgate
64
+ ```
65
+
66
+ ## Quickstart (3 lines)
67
+
68
+ ```python
69
+ from agentgate import AgentGate
70
+
71
+ gate = AgentGate("http://localhost:8000", api_key="your-key")
72
+ gate.register("my_agent", "ReportBot", "Summarize quarterly reports",
73
+ authorized_resources=["/reports/*"], authorized_actions=["read"])
74
+
75
+ result = gate.authorize("read", "/reports/q3.pdf")
76
+ # {"decision": "PERMIT", "trust_breakdown": {...}, "explanation": "..."}
77
+ ```
78
+
79
+ ## LangChain — drop-in enforcement
80
+
81
+ ```bash
82
+ pip install agentgate[langchain]
83
+ ```
84
+
85
+ ```python
86
+ from agentgate.langchain import AgentGateToolkit
87
+
88
+ toolkit = AgentGateToolkit(
89
+ agentgate_url="http://localhost:8000",
90
+ api_key="your-key",
91
+ agent_id="report_agent",
92
+ name="ReportBot",
93
+ declared_purpose="Summarize quarterly business reports",
94
+ authorized_resources=["/reports/*"],
95
+ authorized_actions=["read"],
96
+ processes_external_content=True, # enables prompt injection scanning
97
+ )
98
+
99
+ safe_tools = toolkit.wrap([read_document, list_documents, send_email])
100
+ agent = create_react_agent(llm, safe_tools)
101
+ # Every tool call now goes through AgentGate before executing
102
+ ```
103
+
104
+ ## What gets caught
105
+
106
+ | Threat | How AgentGate stops it |
107
+ |--------|------------------------|
108
+ | Agent reads `/confidential/salary.xlsx` (out of scope) | DENY — `RESOURCE_OUT_OF_SCOPE` |
109
+ | Agent tries `delete` (not in authorized actions) | DENY — `UNAUTHORIZED_ACTION` |
110
+ | Child agent exceeds parent's delegation scope | DENY — `CHAIN_SCOPE_VIOLATION` |
111
+ | Agent makes 80 requests/min (velocity attack) | DENY — `CRITICAL_VELOCITY` |
112
+ | Document contains "ignore previous instructions" | Content blocked before agent sees it |
113
+ | Unknown agent attempts access | DENY — `UNREGISTERED_AGENT` |
114
+
115
+ ## Multi-agent delegation enforcement
116
+
117
+ AgentGate validates the full delegation chain. A child agent cannot exceed its parent's scope — enforced at both registration and authorization time.
118
+
119
+ ```python
120
+ # Orchestrator registers with full scope
121
+ gate.register("orchestrator", "Orchestrator", "Manage document workflow",
122
+ authorized_resources=["/documents/*"], authorized_actions=["read", "write"])
123
+
124
+ # Analyst is delegated a subset — enforced at registration
125
+ resp = httpx.post(f"{url}/agents/delegate", json={
126
+ "parent_agent_id": "orchestrator",
127
+ "parent_token": orchestrator_token,
128
+ "child_agent_id": "analyst",
129
+ "child_resources": ["/documents/public/*"], # subset of parent
130
+ "child_actions": ["read"], # subset of parent
131
+ })
132
+
133
+ # Analyst tries to access /confidential/ — blocked at authorization time
134
+ # {"decision": "DENY", "attack_flags": ["CHAIN_SCOPE_VIOLATION"]}
135
+ ```
136
+
137
+ ## Natural language policies
138
+
139
+ Write security rules in plain English. AgentGate converts them to enforced policies using Claude.
140
+
141
+ ```python
142
+ import httpx
143
+
144
+ # These become hard rules checked before any trust scoring
145
+ httpx.post(f"{url}/policies", json={"rule": "Agents must never delete files"})
146
+ httpx.post(f"{url}/policies", json={"rule": "No agent should read salary data outside business hours"})
147
+ httpx.post(f"{url}/policies", json={"rule": "Flag any access to /hr folder"})
148
+ ```
149
+
150
+ The policy engine runs **before** the trust score — a matching DENY policy is always a hard block regardless of score.
151
+
152
+ ## Human-in-the-loop approval
153
+
154
+ Mark an agent as requiring human approval for escalated decisions:
155
+
156
+ ```python
157
+ gate.register(..., requires_human_approval=True)
158
+
159
+ result = gate.authorize("read", "/confidential/merger_details.pdf")
160
+ if result["decision"] == "PENDING":
161
+ # Agent is paused — human approves or denies via dashboard
162
+ # Wrapper polls automatically; auto-denies after 90 seconds
163
+ request_id = result["request_id"]
164
+ ```
165
+
166
+ ## Prompt injection detection
167
+
168
+ ```python
169
+ # Register as processing external content
170
+ gate.register(..., processes_external_content=True)
171
+
172
+ # Scan before passing content to the agent
173
+ scan = gate.scan(document_content)
174
+ if scan["level"] == "injection":
175
+ raise ValueError(f"Injection blocked: {scan['evidence']}")
176
+ ```
177
+
178
+ ## Context manager and decorator
179
+
180
+ ```python
181
+ # Decorator
182
+ @gate.guard("read", resource_arg="path")
183
+ def read_document(path: str) -> str:
184
+ return open(path).read()
185
+
186
+ # Context manager
187
+ with gate.operation("write", "/reports/output.pdf"):
188
+ write_report(data)
189
+ ```
190
+
191
+ ## Run the server
192
+
193
+ ```bash
194
+ # Docker (recommended)
195
+ docker compose up
196
+
197
+ # Or directly
198
+ pip install agentgate[server]
199
+ python -m agentgate.server
200
+ ```
201
+
202
+ Dashboard at `http://localhost:8000` — live feed of every decision, trust scores, delegation tree, policy editor, and compliance report export.
203
+
204
+ ## Architecture
205
+
206
+ ```
207
+ Your Agent
208
+ |
209
+ AgentGate SDK (pip install agentgate)
210
+ | POST /authorize
211
+ v
212
+ AgentGate PDP Server
213
+ ├── Policy Engine (NL rules → hard blocks, checked first)
214
+ ├── Trust Scoring (identity + delegation + purpose + behavioral)
215
+ │ ├── Identity (token, authorized actions, resource scope)
216
+ │ ├── Delegation (chain depth, scope attenuation, trust decay)
217
+ │ ├── Purpose (semantic alignment of request to declared purpose)
218
+ │ └── Behavioral (per-agent baseline anomaly detection)
219
+ ├── HITL Approval (ESCALATE → human decision → APPROVED/DENIED)
220
+ └── Audit Log (every decision, exportable as PDF/CSV)
221
+ |
222
+ PERMIT / ESCALATE / DENY / PENDING
223
+ |
224
+ Tool executes (or doesn't)
225
+ ```
226
+
227
+ ## Configuration
228
+
229
+ Environment variables (`.env`):
230
+
231
+ ```
232
+ AGENTGATE_API_KEY=your-secret-key
233
+ ANTHROPIC_API_KEY=sk-ant-... # for NL policy parsing + purpose scoring
234
+ AGENTGATE_ALERT_TOPIC=your-ntfy-topic # for push alerts (ntfy.sh)
235
+ AGENTGATE_PORT=8000
236
+ ```
237
+
238
+ ## License
239
+
240
+ MIT — see [LICENSE](LICENSE).
241
+
242
+ ---
243
+
244
+ Built at University of Ottawa · ELG5901 · 2026
@@ -0,0 +1,216 @@
1
+ # AgentGate
2
+
3
+ **A Policy Decision Point (PDP) for AI agents. Every action evaluated before it executes.**
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/agentgate.svg)](https://pypi.org/project/agentgate/)
6
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
8
+
9
+ ---
10
+
11
+ AI agents make decisions autonomously. AgentGate makes sure those decisions are **authorized**.
12
+
13
+ It sits between your agent and its tools. Before any action runs — reading a file, calling an API, writing to a database — AgentGate checks it against identity, scope, purpose, and behavioral context. The response is `PERMIT`, `ESCALATE`, or `DENY` in milliseconds.
14
+
15
+ ```
16
+ Agent wants to delete /confidential/salary.xlsx
17
+ ↓
18
+ AgentGate PDP
19
+ ┌──────────────────────────────┐
20
+ │ Identity check ✓ │
21
+ │ Scope check ✗ (!) │
22
+ │ Purpose alignment ✗ │
23
+ │ Behavioral check ✓ │
24
+ │ │
25
+ │ Trust score: 18/100 │
26
+ │ Decision: DENY │
27
+ └──────────────────────────────┘
28
+ ↓
29
+ Action never executes
30
+ ```
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install agentgate
36
+ ```
37
+
38
+ ## Quickstart (3 lines)
39
+
40
+ ```python
41
+ from agentgate import AgentGate
42
+
43
+ gate = AgentGate("http://localhost:8000", api_key="your-key")
44
+ gate.register("my_agent", "ReportBot", "Summarize quarterly reports",
45
+ authorized_resources=["/reports/*"], authorized_actions=["read"])
46
+
47
+ result = gate.authorize("read", "/reports/q3.pdf")
48
+ # {"decision": "PERMIT", "trust_breakdown": {...}, "explanation": "..."}
49
+ ```
50
+
51
+ ## LangChain — drop-in enforcement
52
+
53
+ ```bash
54
+ pip install agentgate[langchain]
55
+ ```
56
+
57
+ ```python
58
+ from agentgate.langchain import AgentGateToolkit
59
+
60
+ toolkit = AgentGateToolkit(
61
+ agentgate_url="http://localhost:8000",
62
+ api_key="your-key",
63
+ agent_id="report_agent",
64
+ name="ReportBot",
65
+ declared_purpose="Summarize quarterly business reports",
66
+ authorized_resources=["/reports/*"],
67
+ authorized_actions=["read"],
68
+ processes_external_content=True, # enables prompt injection scanning
69
+ )
70
+
71
+ safe_tools = toolkit.wrap([read_document, list_documents, send_email])
72
+ agent = create_react_agent(llm, safe_tools)
73
+ # Every tool call now goes through AgentGate before executing
74
+ ```
75
+
76
+ ## What gets caught
77
+
78
+ | Threat | How AgentGate stops it |
79
+ |--------|------------------------|
80
+ | Agent reads `/confidential/salary.xlsx` (out of scope) | DENY — `RESOURCE_OUT_OF_SCOPE` |
81
+ | Agent tries `delete` (not in authorized actions) | DENY — `UNAUTHORIZED_ACTION` |
82
+ | Child agent exceeds parent's delegation scope | DENY — `CHAIN_SCOPE_VIOLATION` |
83
+ | Agent makes 80 requests/min (velocity attack) | DENY — `CRITICAL_VELOCITY` |
84
+ | Document contains "ignore previous instructions" | Content blocked before agent sees it |
85
+ | Unknown agent attempts access | DENY — `UNREGISTERED_AGENT` |
86
+
87
+ ## Multi-agent delegation enforcement
88
+
89
+ AgentGate validates the full delegation chain. A child agent cannot exceed its parent's scope — enforced at both registration and authorization time.
90
+
91
+ ```python
92
+ # Orchestrator registers with full scope
93
+ gate.register("orchestrator", "Orchestrator", "Manage document workflow",
94
+ authorized_resources=["/documents/*"], authorized_actions=["read", "write"])
95
+
96
+ # Analyst is delegated a subset — enforced at registration
97
+ resp = httpx.post(f"{url}/agents/delegate", json={
98
+ "parent_agent_id": "orchestrator",
99
+ "parent_token": orchestrator_token,
100
+ "child_agent_id": "analyst",
101
+ "child_resources": ["/documents/public/*"], # subset of parent
102
+ "child_actions": ["read"], # subset of parent
103
+ })
104
+
105
+ # Analyst tries to access /confidential/ — blocked at authorization time
106
+ # {"decision": "DENY", "attack_flags": ["CHAIN_SCOPE_VIOLATION"]}
107
+ ```
108
+
109
+ ## Natural language policies
110
+
111
+ Write security rules in plain English. AgentGate converts them to enforced policies using Claude.
112
+
113
+ ```python
114
+ import httpx
115
+
116
+ # These become hard rules checked before any trust scoring
117
+ httpx.post(f"{url}/policies", json={"rule": "Agents must never delete files"})
118
+ httpx.post(f"{url}/policies", json={"rule": "No agent should read salary data outside business hours"})
119
+ httpx.post(f"{url}/policies", json={"rule": "Flag any access to /hr folder"})
120
+ ```
121
+
122
+ The policy engine runs **before** the trust score — a matching DENY policy is always a hard block regardless of score.
123
+
124
+ ## Human-in-the-loop approval
125
+
126
+ Mark an agent as requiring human approval for escalated decisions:
127
+
128
+ ```python
129
+ gate.register(..., requires_human_approval=True)
130
+
131
+ result = gate.authorize("read", "/confidential/merger_details.pdf")
132
+ if result["decision"] == "PENDING":
133
+ # Agent is paused — human approves or denies via dashboard
134
+ # Wrapper polls automatically; auto-denies after 90 seconds
135
+ request_id = result["request_id"]
136
+ ```
137
+
138
+ ## Prompt injection detection
139
+
140
+ ```python
141
+ # Register as processing external content
142
+ gate.register(..., processes_external_content=True)
143
+
144
+ # Scan before passing content to the agent
145
+ scan = gate.scan(document_content)
146
+ if scan["level"] == "injection":
147
+ raise ValueError(f"Injection blocked: {scan['evidence']}")
148
+ ```
149
+
150
+ ## Context manager and decorator
151
+
152
+ ```python
153
+ # Decorator
154
+ @gate.guard("read", resource_arg="path")
155
+ def read_document(path: str) -> str:
156
+ return open(path).read()
157
+
158
+ # Context manager
159
+ with gate.operation("write", "/reports/output.pdf"):
160
+ write_report(data)
161
+ ```
162
+
163
+ ## Run the server
164
+
165
+ ```bash
166
+ # Docker (recommended)
167
+ docker compose up
168
+
169
+ # Or directly
170
+ pip install agentgate[server]
171
+ python -m agentgate.server
172
+ ```
173
+
174
+ Dashboard at `http://localhost:8000` — live feed of every decision, trust scores, delegation tree, policy editor, and compliance report export.
175
+
176
+ ## Architecture
177
+
178
+ ```
179
+ Your Agent
180
+ |
181
+ AgentGate SDK (pip install agentgate)
182
+ | POST /authorize
183
+ v
184
+ AgentGate PDP Server
185
+ ├── Policy Engine (NL rules → hard blocks, checked first)
186
+ ├── Trust Scoring (identity + delegation + purpose + behavioral)
187
+ │ ├── Identity (token, authorized actions, resource scope)
188
+ │ ├── Delegation (chain depth, scope attenuation, trust decay)
189
+ │ ├── Purpose (semantic alignment of request to declared purpose)
190
+ │ └── Behavioral (per-agent baseline anomaly detection)
191
+ ├── HITL Approval (ESCALATE → human decision → APPROVED/DENIED)
192
+ └── Audit Log (every decision, exportable as PDF/CSV)
193
+ |
194
+ PERMIT / ESCALATE / DENY / PENDING
195
+ |
196
+ Tool executes (or doesn't)
197
+ ```
198
+
199
+ ## Configuration
200
+
201
+ Environment variables (`.env`):
202
+
203
+ ```
204
+ AGENTGATE_API_KEY=your-secret-key
205
+ ANTHROPIC_API_KEY=sk-ant-... # for NL policy parsing + purpose scoring
206
+ AGENTGATE_ALERT_TOPIC=your-ntfy-topic # for push alerts (ntfy.sh)
207
+ AGENTGATE_PORT=8000
208
+ ```
209
+
210
+ ## License
211
+
212
+ MIT — see [LICENSE](LICENSE).
213
+
214
+ ---
215
+
216
+ Built at University of Ottawa · ELG5901 · 2026
@@ -0,0 +1,205 @@
1
+ """
2
+ AgentGate SDK — drop-in trust authorization for AI agents.
3
+
4
+ Quickstart:
5
+
6
+ from agentgate import AgentGate
7
+
8
+ gate = AgentGate("http://localhost:8000")
9
+ gate.register("my_agent", "MyBot", "Summarize PDF reports", ["/reports/*"], ["read"])
10
+
11
+ # Authorize before any action
12
+ result = gate.authorize("read", "/reports/q3.pdf")
13
+
14
+ # Decorator style
15
+ @gate.guard("read", resource_arg="path")
16
+ def read_document(path: str) -> str:
17
+ return open(path).read()
18
+
19
+ # Scan for prompt injection
20
+ scan = gate.scan(email_body)
21
+ if scan["level"] == "injection":
22
+ raise ValueError("Injection detected — content blocked")
23
+ """
24
+
25
+ __version__ = "0.2.0"
26
+
27
+ import uuid
28
+ import httpx
29
+ from functools import wraps
30
+
31
+ from agentgate.exceptions import AgentGateDenied, AgentGateEscalated, AgentGateNotRegistered
32
+
33
+
34
+ class AgentGate:
35
+ """
36
+ Client for the AgentGate Policy Decision Point.
37
+
38
+ Args:
39
+ url: AgentGate server URL (e.g. "http://localhost:8000")
40
+ raise_on_deny: If True (default), .authorize() raises AgentGateDenied on DENY
41
+ raise_on_escalate: If True, .authorize() raises AgentGateEscalated on ESCALATE
42
+ timeout: HTTP request timeout in seconds (default 30)
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ url: str,
48
+ api_key: str = "",
49
+ raise_on_deny: bool = True,
50
+ raise_on_escalate: bool = False,
51
+ timeout: float = 30.0,
52
+ ):
53
+ self.url = url.rstrip("/")
54
+ self._headers = {"X-API-Key": api_key} if api_key else {}
55
+ self.raise_on_deny = raise_on_deny
56
+ self.raise_on_escalate = raise_on_escalate
57
+ self.timeout = timeout
58
+ self._agent_id: str | None = None
59
+ self._token: str | None = None
60
+
61
+ # ── Registration ─────────────────────────────────────────────────────────
62
+
63
+ def register(
64
+ self,
65
+ agent_id: str,
66
+ name: str,
67
+ declared_purpose: str,
68
+ authorized_resources: list[str],
69
+ authorized_actions: list[str],
70
+ delegation_depth: int = 0,
71
+ delegated_by: str | None = None,
72
+ scope_at_delegation: list[str] | None = None,
73
+ processes_external_content: bool = False,
74
+ ) -> "AgentGate":
75
+ """Register this agent with AgentGate. Returns self for chaining."""
76
+ payload: dict = {
77
+ "agent_id": agent_id,
78
+ "name": name,
79
+ "declared_purpose": declared_purpose,
80
+ "authorized_resources": authorized_resources,
81
+ "authorized_actions": authorized_actions,
82
+ "delegation_depth": delegation_depth,
83
+ "processes_external_content": processes_external_content,
84
+ }
85
+ if delegated_by:
86
+ payload["delegated_by"] = delegated_by
87
+ if scope_at_delegation:
88
+ payload["scope_at_delegation"] = scope_at_delegation
89
+
90
+ r = httpx.post(f"{self.url}/agents/register", json=payload, headers=self._headers, timeout=self.timeout)
91
+ r.raise_for_status()
92
+ self._agent_id = agent_id
93
+ self._token = r.json()["token"]
94
+ return self
95
+
96
+ # ── Authorization ─────────────────────────────────────────────────────────
97
+
98
+ def authorize(self, action: str, resource: str, justification: str = "") -> dict:
99
+ """Request authorization before performing an action.
100
+
101
+ Returns dict with decision, trust_breakdown, explanation, attack_flags.
102
+ Raises AgentGateDenied on DENY (if raise_on_deny=True).
103
+ """
104
+ if not self._agent_id or not self._token:
105
+ raise AgentGateNotRegistered("Call gate.register(...) before gate.authorize()")
106
+
107
+ r = httpx.post(
108
+ f"{self.url}/authorize",
109
+ headers=self._headers,
110
+ json={
111
+ "agent_id": self._agent_id,
112
+ "token": self._token,
113
+ "action": action,
114
+ "resource": resource,
115
+ "justification": justification,
116
+ "request_id": str(uuid.uuid4()),
117
+ },
118
+ timeout=self.timeout,
119
+ )
120
+ r.raise_for_status()
121
+ result = r.json()
122
+ decision = result["decision"]
123
+
124
+ if decision == "DENY" and self.raise_on_deny:
125
+ raise AgentGateDenied(action, resource, result.get("explanation", ""))
126
+ if decision == "ESCALATE" and self.raise_on_escalate:
127
+ raise AgentGateEscalated(action, resource, result.get("explanation", ""))
128
+
129
+ return result
130
+
131
+ # ── Content scan ──────────────────────────────────────────────────────────
132
+
133
+ def scan(self, content: str) -> dict:
134
+ """Scan document/email content for prompt injection before processing.
135
+
136
+ Returns dict with level ("clean"/"suspicious"/"injection"), confidence, evidence.
137
+ """
138
+ if not self._agent_id:
139
+ raise AgentGateNotRegistered("Call gate.register(...) before gate.scan()")
140
+
141
+ r = httpx.post(
142
+ f"{self.url}/scan",
143
+ headers=self._headers,
144
+ json={"agent_id": self._agent_id, "content": content},
145
+ timeout=self.timeout,
146
+ )
147
+ r.raise_for_status()
148
+ return r.json()
149
+
150
+ # ── Helpers ───────────────────────────────────────────────────────────────
151
+
152
+ def check(self, action: str, resource: str, justification: str = "") -> bool:
153
+ """Non-raising boolean check. Returns False on DENY, True otherwise."""
154
+ try:
155
+ result = self.authorize(action, resource, justification)
156
+ return result["decision"] != "DENY"
157
+ except AgentGateDenied:
158
+ return False
159
+
160
+ def guard(self, action: str, resource_arg: str = "resource"):
161
+ """Decorator — authorizes before the wrapped function runs.
162
+
163
+ @gate.guard("read", resource_arg="path")
164
+ def read_file(path: str) -> str:
165
+ return open(path).read()
166
+ """
167
+ def decorator(func):
168
+ @wraps(func)
169
+ def wrapper(*args, **kwargs):
170
+ resource = kwargs.get(resource_arg) or (args[0] if args else "unknown")
171
+ self.authorize(action, str(resource), f"Calling {func.__name__}")
172
+ return func(*args, **kwargs)
173
+ return wrapper
174
+ return decorator
175
+
176
+ def operation(self, action: str, resource: str, justification: str = ""):
177
+ """Context manager — authorizes on enter.
178
+
179
+ with gate.operation("delete", "/confidential/salary.xlsx"):
180
+ delete_file(...)
181
+ """
182
+ return _AuthorizedOperation(self, action, resource, justification)
183
+
184
+ @property
185
+ def agent_id(self) -> str | None:
186
+ return self._agent_id
187
+
188
+ @property
189
+ def token(self) -> str | None:
190
+ return self._token
191
+
192
+
193
+ class _AuthorizedOperation:
194
+ def __init__(self, gate: AgentGate, action: str, resource: str, justification: str):
195
+ self._gate = gate
196
+ self._action = action
197
+ self._resource = resource
198
+ self._justification = justification
199
+
200
+ def __enter__(self):
201
+ self._gate.authorize(self._action, self._resource, self._justification)
202
+ return self
203
+
204
+ def __exit__(self, *_):
205
+ pass
@@ -0,0 +1,21 @@
1
+ class AgentGateDenied(Exception):
2
+ """Raised when AgentGate returns a hard DENY decision."""
3
+ def __init__(self, action: str, resource: str, explanation: str = ""):
4
+ self.action = action
5
+ self.resource = resource
6
+ self.explanation = explanation
7
+ super().__init__(f"AgentGate DENIED {action} on {resource}: {explanation}")
8
+
9
+
10
+ class AgentGateEscalated(Exception):
11
+ """Raised when AgentGate returns ESCALATE and strict mode is on."""
12
+ def __init__(self, action: str, resource: str, explanation: str = ""):
13
+ self.action = action
14
+ self.resource = resource
15
+ self.explanation = explanation
16
+ super().__init__(f"AgentGate ESCALATED {action} on {resource}: {explanation}")
17
+
18
+
19
+ class AgentGateNotRegistered(Exception):
20
+ """Raised when .authorize() is called before .register()."""
21
+ pass
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentgate-pdp
3
+ Version: 0.2.0
4
+ Summary: Context-aware trust authorization for agentic AI systems
5
+ Author-email: AgentGate <olame109@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/agentgate/agentgate
8
+ Project-URL: Documentation, https://agentgate.dev/docs
9
+ Keywords: ai,agents,security,authorization,llm,langchain,policy,trust,pdp,agentic
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Security
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: httpx>=0.27.0
21
+ Provides-Extra: langchain
22
+ Requires-Dist: langchain-core>=0.3.0; extra == "langchain"
23
+ Requires-Dist: langchain-anthropic>=0.2.0; extra == "langchain"
24
+ Requires-Dist: langgraph>=0.2.0; extra == "langchain"
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
28
+
29
+ # AgentGate
30
+
31
+ **A Policy Decision Point (PDP) for AI agents. Every action evaluated before it executes.**
32
+
33
+ [![PyPI version](https://img.shields.io/pypi/v/agentgate.svg)](https://pypi.org/project/agentgate/)
34
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
36
+
37
+ ---
38
+
39
+ AI agents make decisions autonomously. AgentGate makes sure those decisions are **authorized**.
40
+
41
+ It sits between your agent and its tools. Before any action runs — reading a file, calling an API, writing to a database — AgentGate checks it against identity, scope, purpose, and behavioral context. The response is `PERMIT`, `ESCALATE`, or `DENY` in milliseconds.
42
+
43
+ ```
44
+ Agent wants to delete /confidential/salary.xlsx
45
+ ↓
46
+ AgentGate PDP
47
+ ┌──────────────────────────────┐
48
+ │ Identity check ✓ │
49
+ │ Scope check ✗ (!) │
50
+ │ Purpose alignment ✗ │
51
+ │ Behavioral check ✓ │
52
+ │ │
53
+ │ Trust score: 18/100 │
54
+ │ Decision: DENY │
55
+ └──────────────────────────────┘
56
+ ↓
57
+ Action never executes
58
+ ```
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install agentgate
64
+ ```
65
+
66
+ ## Quickstart (3 lines)
67
+
68
+ ```python
69
+ from agentgate import AgentGate
70
+
71
+ gate = AgentGate("http://localhost:8000", api_key="your-key")
72
+ gate.register("my_agent", "ReportBot", "Summarize quarterly reports",
73
+ authorized_resources=["/reports/*"], authorized_actions=["read"])
74
+
75
+ result = gate.authorize("read", "/reports/q3.pdf")
76
+ # {"decision": "PERMIT", "trust_breakdown": {...}, "explanation": "..."}
77
+ ```
78
+
79
+ ## LangChain — drop-in enforcement
80
+
81
+ ```bash
82
+ pip install agentgate[langchain]
83
+ ```
84
+
85
+ ```python
86
+ from agentgate.langchain import AgentGateToolkit
87
+
88
+ toolkit = AgentGateToolkit(
89
+ agentgate_url="http://localhost:8000",
90
+ api_key="your-key",
91
+ agent_id="report_agent",
92
+ name="ReportBot",
93
+ declared_purpose="Summarize quarterly business reports",
94
+ authorized_resources=["/reports/*"],
95
+ authorized_actions=["read"],
96
+ processes_external_content=True, # enables prompt injection scanning
97
+ )
98
+
99
+ safe_tools = toolkit.wrap([read_document, list_documents, send_email])
100
+ agent = create_react_agent(llm, safe_tools)
101
+ # Every tool call now goes through AgentGate before executing
102
+ ```
103
+
104
+ ## What gets caught
105
+
106
+ | Threat | How AgentGate stops it |
107
+ |--------|------------------------|
108
+ | Agent reads `/confidential/salary.xlsx` (out of scope) | DENY — `RESOURCE_OUT_OF_SCOPE` |
109
+ | Agent tries `delete` (not in authorized actions) | DENY — `UNAUTHORIZED_ACTION` |
110
+ | Child agent exceeds parent's delegation scope | DENY — `CHAIN_SCOPE_VIOLATION` |
111
+ | Agent makes 80 requests/min (velocity attack) | DENY — `CRITICAL_VELOCITY` |
112
+ | Document contains "ignore previous instructions" | Content blocked before agent sees it |
113
+ | Unknown agent attempts access | DENY — `UNREGISTERED_AGENT` |
114
+
115
+ ## Multi-agent delegation enforcement
116
+
117
+ AgentGate validates the full delegation chain. A child agent cannot exceed its parent's scope — enforced at both registration and authorization time.
118
+
119
+ ```python
120
+ # Orchestrator registers with full scope
121
+ gate.register("orchestrator", "Orchestrator", "Manage document workflow",
122
+ authorized_resources=["/documents/*"], authorized_actions=["read", "write"])
123
+
124
+ # Analyst is delegated a subset — enforced at registration
125
+ resp = httpx.post(f"{url}/agents/delegate", json={
126
+ "parent_agent_id": "orchestrator",
127
+ "parent_token": orchestrator_token,
128
+ "child_agent_id": "analyst",
129
+ "child_resources": ["/documents/public/*"], # subset of parent
130
+ "child_actions": ["read"], # subset of parent
131
+ })
132
+
133
+ # Analyst tries to access /confidential/ — blocked at authorization time
134
+ # {"decision": "DENY", "attack_flags": ["CHAIN_SCOPE_VIOLATION"]}
135
+ ```
136
+
137
+ ## Natural language policies
138
+
139
+ Write security rules in plain English. AgentGate converts them to enforced policies using Claude.
140
+
141
+ ```python
142
+ import httpx
143
+
144
+ # These become hard rules checked before any trust scoring
145
+ httpx.post(f"{url}/policies", json={"rule": "Agents must never delete files"})
146
+ httpx.post(f"{url}/policies", json={"rule": "No agent should read salary data outside business hours"})
147
+ httpx.post(f"{url}/policies", json={"rule": "Flag any access to /hr folder"})
148
+ ```
149
+
150
+ The policy engine runs **before** the trust score — a matching DENY policy is always a hard block regardless of score.
151
+
152
+ ## Human-in-the-loop approval
153
+
154
+ Mark an agent as requiring human approval for escalated decisions:
155
+
156
+ ```python
157
+ gate.register(..., requires_human_approval=True)
158
+
159
+ result = gate.authorize("read", "/confidential/merger_details.pdf")
160
+ if result["decision"] == "PENDING":
161
+ # Agent is paused — human approves or denies via dashboard
162
+ # Wrapper polls automatically; auto-denies after 90 seconds
163
+ request_id = result["request_id"]
164
+ ```
165
+
166
+ ## Prompt injection detection
167
+
168
+ ```python
169
+ # Register as processing external content
170
+ gate.register(..., processes_external_content=True)
171
+
172
+ # Scan before passing content to the agent
173
+ scan = gate.scan(document_content)
174
+ if scan["level"] == "injection":
175
+ raise ValueError(f"Injection blocked: {scan['evidence']}")
176
+ ```
177
+
178
+ ## Context manager and decorator
179
+
180
+ ```python
181
+ # Decorator
182
+ @gate.guard("read", resource_arg="path")
183
+ def read_document(path: str) -> str:
184
+ return open(path).read()
185
+
186
+ # Context manager
187
+ with gate.operation("write", "/reports/output.pdf"):
188
+ write_report(data)
189
+ ```
190
+
191
+ ## Run the server
192
+
193
+ ```bash
194
+ # Docker (recommended)
195
+ docker compose up
196
+
197
+ # Or directly
198
+ pip install agentgate[server]
199
+ python -m agentgate.server
200
+ ```
201
+
202
+ Dashboard at `http://localhost:8000` — live feed of every decision, trust scores, delegation tree, policy editor, and compliance report export.
203
+
204
+ ## Architecture
205
+
206
+ ```
207
+ Your Agent
208
+ |
209
+ AgentGate SDK (pip install agentgate)
210
+ | POST /authorize
211
+ v
212
+ AgentGate PDP Server
213
+ ├── Policy Engine (NL rules → hard blocks, checked first)
214
+ ├── Trust Scoring (identity + delegation + purpose + behavioral)
215
+ │ ├── Identity (token, authorized actions, resource scope)
216
+ │ ├── Delegation (chain depth, scope attenuation, trust decay)
217
+ │ ├── Purpose (semantic alignment of request to declared purpose)
218
+ │ └── Behavioral (per-agent baseline anomaly detection)
219
+ ├── HITL Approval (ESCALATE → human decision → APPROVED/DENIED)
220
+ └── Audit Log (every decision, exportable as PDF/CSV)
221
+ |
222
+ PERMIT / ESCALATE / DENY / PENDING
223
+ |
224
+ Tool executes (or doesn't)
225
+ ```
226
+
227
+ ## Configuration
228
+
229
+ Environment variables (`.env`):
230
+
231
+ ```
232
+ AGENTGATE_API_KEY=your-secret-key
233
+ ANTHROPIC_API_KEY=sk-ant-... # for NL policy parsing + purpose scoring
234
+ AGENTGATE_ALERT_TOPIC=your-ntfy-topic # for push alerts (ntfy.sh)
235
+ AGENTGATE_PORT=8000
236
+ ```
237
+
238
+ ## License
239
+
240
+ MIT — see [LICENSE](LICENSE).
241
+
242
+ ---
243
+
244
+ Built at University of Ottawa · ELG5901 · 2026
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ agentgate/__init__.py
4
+ agentgate/exceptions.py
5
+ agentgate_pdp.egg-info/PKG-INFO
6
+ agentgate_pdp.egg-info/SOURCES.txt
7
+ agentgate_pdp.egg-info/dependency_links.txt
8
+ agentgate_pdp.egg-info/requires.txt
9
+ agentgate_pdp.egg-info/top_level.txt
@@ -0,0 +1,10 @@
1
+ httpx>=0.27.0
2
+
3
+ [dev]
4
+ pytest>=8.0
5
+ pytest-asyncio>=0.23
6
+
7
+ [langchain]
8
+ langchain-core>=0.3.0
9
+ langchain-anthropic>=0.2.0
10
+ langgraph>=0.2.0
@@ -0,0 +1 @@
1
+ agentgate
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "agentgate-pdp"
7
+ version = "0.2.0"
8
+ description = "Context-aware trust authorization for agentic AI systems"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "AgentGate", email = "olame109@gmail.com" }
14
+ ]
15
+ keywords = ["ai", "agents", "security", "authorization", "llm", "langchain", "policy", "trust", "pdp", "agentic"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Topic :: Security",
20
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ ]
26
+ dependencies = [
27
+ "httpx>=0.27.0",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ langchain = [
32
+ "langchain-core>=0.3.0",
33
+ "langchain-anthropic>=0.2.0",
34
+ "langgraph>=0.2.0",
35
+ ]
36
+ dev = [
37
+ "pytest>=8.0",
38
+ "pytest-asyncio>=0.23",
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/agentgate/agentgate"
43
+ Documentation = "https://agentgate.dev/docs"
44
+
45
+ [tool.setuptools.packages.find]
46
+ where = ["."]
47
+ include = ["agentgate*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+