sentryix 1.0.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.
- sentryix-1.0.0/LICENSE +21 -0
- sentryix-1.0.0/PKG-INFO +167 -0
- sentryix-1.0.0/README.md +142 -0
- sentryix-1.0.0/pyproject.toml +24 -0
- sentryix-1.0.0/sentryix/__init__.py +289 -0
- sentryix-1.0.0/sentryix.egg-info/PKG-INFO +167 -0
- sentryix-1.0.0/sentryix.egg-info/SOURCES.txt +9 -0
- sentryix-1.0.0/sentryix.egg-info/dependency_links.txt +1 -0
- sentryix-1.0.0/sentryix.egg-info/top_level.txt +1 -0
- sentryix-1.0.0/setup.cfg +4 -0
- sentryix-1.0.0/setup.py +40 -0
sentryix-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sentryix
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
sentryix-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sentryix
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Verify AI agent actions before execution. EU AI Act compliant.
|
|
5
|
+
Home-page: https://sentinelvault-deploy.vercel.app
|
|
6
|
+
Author: Sentryix
|
|
7
|
+
Author-email: sales@sentryix.ai
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://sentinelvault-deploy.vercel.app
|
|
10
|
+
Project-URL: Documentation, https://sentinelvault-deploy.vercel.app/dashboard
|
|
11
|
+
Keywords: ai,agent,governance,security,llm,eu-ai-act,compliance
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
|
|
26
|
+
# sentryix
|
|
27
|
+
|
|
28
|
+
**Verify AI agent actions before execution. EU AI Act compliant.**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install sentryix
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Zero dependencies. Works with Python 3.8+.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from sentryix import Sentryix
|
|
42
|
+
|
|
43
|
+
guard = Sentryix(api_key="sv_your_key_here")
|
|
44
|
+
# Or set SENTRYIX_API_KEY environment variable
|
|
45
|
+
|
|
46
|
+
result = guard.verify(
|
|
47
|
+
agent_id="billing-agent",
|
|
48
|
+
action="Delete all inactive user records older than 90 days",
|
|
49
|
+
context="Scheduled cleanup job, triggered by cron"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if result.allow:
|
|
53
|
+
execute_action()
|
|
54
|
+
else:
|
|
55
|
+
notify_human(result.reason)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## What You Get Back
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
result.allow # True / False
|
|
64
|
+
result.risk_level # "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
|
|
65
|
+
result.requires_human # True if human approval required
|
|
66
|
+
result.reason # "Mass deletion operation — irreversible data loss risk."
|
|
67
|
+
result.confidence # 97
|
|
68
|
+
result.eu_article # "Article 9"
|
|
69
|
+
result.eu_title # "Risk Management System"
|
|
70
|
+
result.pii_redacted # True if PII was detected and redacted
|
|
71
|
+
result.processing_mode # "sync" (AI verified) or "async" (rule engine)
|
|
72
|
+
result.usage # {"callsUsed": 42, "callsLimit": 500, "tier": "free"}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## LangChain Integration
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from sentryix import Sentryix, SentryixError
|
|
81
|
+
guard = Sentryix()
|
|
82
|
+
|
|
83
|
+
class SentryixGuard:
|
|
84
|
+
def __call__(self, action: str) -> bool:
|
|
85
|
+
result = guard.verify(agent_id="langchain-agent", action=action)
|
|
86
|
+
if not result.allow:
|
|
87
|
+
raise PermissionError(f"[{result.risk_level}] {result.reason}")
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
# Add to your LangChain agent before any tool execution
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CrewAI Integration
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from crewai import Agent, Task
|
|
97
|
+
from sentryix import Sentryix
|
|
98
|
+
|
|
99
|
+
guard = Sentryix()
|
|
100
|
+
|
|
101
|
+
def governed_execute(agent_name: str, action: str):
|
|
102
|
+
result = guard.verify(agent_id=agent_name, action=action)
|
|
103
|
+
if not result.allow:
|
|
104
|
+
return f"BLOCKED [{result.risk_level}]: {result.reason}"
|
|
105
|
+
return execute(action)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Error Handling
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from sentryix import Sentryix, AuthError, RateLimitError, BreakLoopError
|
|
114
|
+
|
|
115
|
+
guard = Sentryix(api_key="sv_...")
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
result = guard.verify(agent_id="bot", action=user_action)
|
|
119
|
+
except AuthError:
|
|
120
|
+
# Invalid or expired API key
|
|
121
|
+
pass
|
|
122
|
+
except RateLimitError as e:
|
|
123
|
+
# Monthly limit reached — e.tier, e.limit
|
|
124
|
+
pass
|
|
125
|
+
except BreakLoopError as e:
|
|
126
|
+
# Agent fired too many calls — e.resets_in seconds
|
|
127
|
+
time.sleep(e.resets_in)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Environment Variable
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
export SENTRYIX_API_KEY="sv_your_key_here"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
guard = Sentryix() # picks up SENTRYIX_API_KEY automatically
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## EU AI Act Compliance
|
|
145
|
+
|
|
146
|
+
Every verification is logged with the applicable EU AI Act article:
|
|
147
|
+
|
|
148
|
+
| Risk Level | Article | Requirement |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| LOW | Article 12 | Record-keeping |
|
|
151
|
+
| MEDIUM | Article 13 | Transparency |
|
|
152
|
+
| HIGH | Article 14 | Human Oversight |
|
|
153
|
+
| CRITICAL | Article 9/10 | Risk Management / Data Governance |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Get an API Key
|
|
158
|
+
|
|
159
|
+
Free tier: 500 verifications/month, no credit card.
|
|
160
|
+
|
|
161
|
+
→ [sentinelvault-deploy.vercel.app/dashboard](https://sentinelvault-deploy.vercel.app/dashboard)
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
sentryix-1.0.0/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# sentryix
|
|
2
|
+
|
|
3
|
+
**Verify AI agent actions before execution. EU AI Act compliant.**
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install sentryix
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Zero dependencies. Works with Python 3.8+.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from sentryix import Sentryix
|
|
17
|
+
|
|
18
|
+
guard = Sentryix(api_key="sv_your_key_here")
|
|
19
|
+
# Or set SENTRYIX_API_KEY environment variable
|
|
20
|
+
|
|
21
|
+
result = guard.verify(
|
|
22
|
+
agent_id="billing-agent",
|
|
23
|
+
action="Delete all inactive user records older than 90 days",
|
|
24
|
+
context="Scheduled cleanup job, triggered by cron"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if result.allow:
|
|
28
|
+
execute_action()
|
|
29
|
+
else:
|
|
30
|
+
notify_human(result.reason)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## What You Get Back
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
result.allow # True / False
|
|
39
|
+
result.risk_level # "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
|
|
40
|
+
result.requires_human # True if human approval required
|
|
41
|
+
result.reason # "Mass deletion operation — irreversible data loss risk."
|
|
42
|
+
result.confidence # 97
|
|
43
|
+
result.eu_article # "Article 9"
|
|
44
|
+
result.eu_title # "Risk Management System"
|
|
45
|
+
result.pii_redacted # True if PII was detected and redacted
|
|
46
|
+
result.processing_mode # "sync" (AI verified) or "async" (rule engine)
|
|
47
|
+
result.usage # {"callsUsed": 42, "callsLimit": 500, "tier": "free"}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## LangChain Integration
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from sentryix import Sentryix, SentryixError
|
|
56
|
+
guard = Sentryix()
|
|
57
|
+
|
|
58
|
+
class SentryixGuard:
|
|
59
|
+
def __call__(self, action: str) -> bool:
|
|
60
|
+
result = guard.verify(agent_id="langchain-agent", action=action)
|
|
61
|
+
if not result.allow:
|
|
62
|
+
raise PermissionError(f"[{result.risk_level}] {result.reason}")
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
# Add to your LangChain agent before any tool execution
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## CrewAI Integration
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from crewai import Agent, Task
|
|
72
|
+
from sentryix import Sentryix
|
|
73
|
+
|
|
74
|
+
guard = Sentryix()
|
|
75
|
+
|
|
76
|
+
def governed_execute(agent_name: str, action: str):
|
|
77
|
+
result = guard.verify(agent_id=agent_name, action=action)
|
|
78
|
+
if not result.allow:
|
|
79
|
+
return f"BLOCKED [{result.risk_level}]: {result.reason}"
|
|
80
|
+
return execute(action)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Error Handling
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from sentryix import Sentryix, AuthError, RateLimitError, BreakLoopError
|
|
89
|
+
|
|
90
|
+
guard = Sentryix(api_key="sv_...")
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
result = guard.verify(agent_id="bot", action=user_action)
|
|
94
|
+
except AuthError:
|
|
95
|
+
# Invalid or expired API key
|
|
96
|
+
pass
|
|
97
|
+
except RateLimitError as e:
|
|
98
|
+
# Monthly limit reached — e.tier, e.limit
|
|
99
|
+
pass
|
|
100
|
+
except BreakLoopError as e:
|
|
101
|
+
# Agent fired too many calls — e.resets_in seconds
|
|
102
|
+
time.sleep(e.resets_in)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Environment Variable
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
export SENTRYIX_API_KEY="sv_your_key_here"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
guard = Sentryix() # picks up SENTRYIX_API_KEY automatically
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## EU AI Act Compliance
|
|
120
|
+
|
|
121
|
+
Every verification is logged with the applicable EU AI Act article:
|
|
122
|
+
|
|
123
|
+
| Risk Level | Article | Requirement |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| LOW | Article 12 | Record-keeping |
|
|
126
|
+
| MEDIUM | Article 13 | Transparency |
|
|
127
|
+
| HIGH | Article 14 | Human Oversight |
|
|
128
|
+
| CRITICAL | Article 9/10 | Risk Management / Data Governance |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Get an API Key
|
|
133
|
+
|
|
134
|
+
Free tier: 500 verifications/month, no credit card.
|
|
135
|
+
|
|
136
|
+
→ [sentinelvault-deploy.vercel.app/dashboard](https://sentinelvault-deploy.vercel.app/dashboard)
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sentryix"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Verify AI agent actions before execution. EU AI Act compliant."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["ai", "agent", "governance", "security", "llm", "eu-ai-act", "compliance"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Topic :: Security",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
]
|
|
20
|
+
dependencies = []
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://sentinelvault-deploy.vercel.app"
|
|
24
|
+
Documentation = "https://sentinelvault-deploy.vercel.app/dashboard"
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sentryix Python SDK
|
|
3
|
+
Verify AI agent actions before execution.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
from sentryix import Sentryix
|
|
7
|
+
|
|
8
|
+
guard = Sentryix(api_key="sv_your_key_here")
|
|
9
|
+
|
|
10
|
+
result = guard.verify(
|
|
11
|
+
agent_id="my-agent",
|
|
12
|
+
action="Delete all inactive user records",
|
|
13
|
+
context="Scheduled cleanup job"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if result.allow:
|
|
17
|
+
execute_action()
|
|
18
|
+
else:
|
|
19
|
+
notify_human(result.reason)
|
|
20
|
+
# result.risk_level → "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
|
|
21
|
+
# result.requires_human → True/False
|
|
22
|
+
# result.eu_article → "Article 12" | "Article 14" etc.
|
|
23
|
+
# result.confidence → 0-100
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import os
|
|
29
|
+
import json
|
|
30
|
+
import time
|
|
31
|
+
import urllib.request
|
|
32
|
+
import urllib.error
|
|
33
|
+
from dataclasses import dataclass, field
|
|
34
|
+
from typing import Optional, List
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__version__ = "1.0.0"
|
|
38
|
+
__all__ = ["Sentryix", "VerifyResult", "SentryixError", "RateLimitError", "AuthError"]
|
|
39
|
+
|
|
40
|
+
DEFAULT_BASE_URL = "https://sentinelvault-deploy.vercel.app"
|
|
41
|
+
DEFAULT_TIMEOUT = 30 # seconds
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ── Exceptions ────────────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
class SentryixError(Exception):
|
|
47
|
+
"""Base error for all Sentryix SDK exceptions."""
|
|
48
|
+
def __init__(self, message: str, status_code: int = 0):
|
|
49
|
+
super().__init__(message)
|
|
50
|
+
self.status_code = status_code
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AuthError(SentryixError):
|
|
54
|
+
"""Invalid or missing API key."""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class RateLimitError(SentryixError):
|
|
59
|
+
"""Monthly call limit reached. Upgrade your plan."""
|
|
60
|
+
def __init__(self, message: str, tier: str = "", limit: int = 0):
|
|
61
|
+
super().__init__(message, status_code=429)
|
|
62
|
+
self.tier = tier
|
|
63
|
+
self.limit = limit
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class BreakLoopError(SentryixError):
|
|
67
|
+
"""Agent rate limit exceeded — BREAK_LOOP protection triggered."""
|
|
68
|
+
def __init__(self, message: str, agent_id: str = "", resets_in: int = 0):
|
|
69
|
+
super().__init__(message, status_code=429)
|
|
70
|
+
self.agent_id = agent_id
|
|
71
|
+
self.resets_in = resets_in
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ── Result ────────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class VerifyResult:
|
|
78
|
+
"""
|
|
79
|
+
Result of a verify() call.
|
|
80
|
+
|
|
81
|
+
Attributes:
|
|
82
|
+
allow True if the action is safe to proceed.
|
|
83
|
+
risk_level "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
|
|
84
|
+
requires_human True if a human must approve before execution.
|
|
85
|
+
reason One-sentence explanation of the decision.
|
|
86
|
+
confidence Classification confidence 0–100.
|
|
87
|
+
eu_article Applicable EU AI Act article (e.g. "Article 14").
|
|
88
|
+
eu_title Article title (e.g. "Human Oversight").
|
|
89
|
+
agent_id The agent_id you passed in.
|
|
90
|
+
action The action text (PII redacted).
|
|
91
|
+
processing_mode "sync" (AI verified) or "async" (rule engine fast-path).
|
|
92
|
+
pii_redacted True if PII was detected and redacted before logging.
|
|
93
|
+
pii_types List of detected PII types e.g. ["EMAIL", "CARD"].
|
|
94
|
+
credit_cost Credits consumed: 0 for observations, 1 for executions.
|
|
95
|
+
usage Dict with callsUsed, callsLimit, tier.
|
|
96
|
+
raw Full raw response dict from the API.
|
|
97
|
+
"""
|
|
98
|
+
allow: bool
|
|
99
|
+
risk_level: str
|
|
100
|
+
requires_human: bool
|
|
101
|
+
reason: str
|
|
102
|
+
confidence: int
|
|
103
|
+
eu_article: str
|
|
104
|
+
eu_title: str
|
|
105
|
+
agent_id: str
|
|
106
|
+
action: str
|
|
107
|
+
processing_mode: str
|
|
108
|
+
pii_redacted: bool
|
|
109
|
+
pii_types: List[str]
|
|
110
|
+
credit_cost: int
|
|
111
|
+
usage: dict
|
|
112
|
+
raw: dict = field(repr=False)
|
|
113
|
+
|
|
114
|
+
def __bool__(self) -> bool:
|
|
115
|
+
"""Allows `if guard.verify(...):` to check allow directly."""
|
|
116
|
+
return self.allow
|
|
117
|
+
|
|
118
|
+
def __repr__(self) -> str:
|
|
119
|
+
status = "ALLOW" if self.allow else "BLOCK"
|
|
120
|
+
return (
|
|
121
|
+
f"VerifyResult({status} | {self.risk_level} | "
|
|
122
|
+
f"confidence={self.confidence}% | {self.reason[:60]})"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# ── Client ────────────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
class Sentryix:
|
|
129
|
+
"""
|
|
130
|
+
Sentryix governance client.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
api_key: Your sv_... API key. Falls back to SENTRYIX_API_KEY env var.
|
|
134
|
+
base_url: Override the API base URL. Defaults to the Sentryix cloud.
|
|
135
|
+
timeout: Request timeout in seconds. Default 30.
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
guard = Sentryix(api_key="sv_...")
|
|
139
|
+
result = guard.verify(agent_id="bot", action="read catalog page")
|
|
140
|
+
if result.allow:
|
|
141
|
+
proceed()
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(
|
|
145
|
+
self,
|
|
146
|
+
api_key: Optional[str] = None,
|
|
147
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
148
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
149
|
+
):
|
|
150
|
+
self.api_key = api_key or os.environ.get("SENTRYIX_API_KEY", "")
|
|
151
|
+
self.base_url = base_url.rstrip("/")
|
|
152
|
+
self.timeout = timeout
|
|
153
|
+
|
|
154
|
+
if not self.api_key:
|
|
155
|
+
raise AuthError(
|
|
156
|
+
"No API key provided. Pass api_key='sv_...' or set "
|
|
157
|
+
"the SENTRYIX_API_KEY environment variable."
|
|
158
|
+
)
|
|
159
|
+
if not self.api_key.startswith("sv_"):
|
|
160
|
+
raise AuthError(
|
|
161
|
+
f"Invalid API key format '{self.api_key[:8]}...'. "
|
|
162
|
+
"Sentryix keys start with 'sv_'. Get a key at "
|
|
163
|
+
f"{self.base_url}/dashboard"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# ── Public API ─────────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
def verify(
|
|
169
|
+
self,
|
|
170
|
+
action: str,
|
|
171
|
+
agent_id: str = "default-agent",
|
|
172
|
+
context: str = "",
|
|
173
|
+
) -> VerifyResult:
|
|
174
|
+
"""
|
|
175
|
+
Classify an agent action before execution.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
action: The action your AI agent is about to take.
|
|
179
|
+
Be specific: "Delete records from users table where active=false"
|
|
180
|
+
is better than "delete some data".
|
|
181
|
+
agent_id: Identifier for the agent making this call.
|
|
182
|
+
Used for BREAK_LOOP rate limiting per agent.
|
|
183
|
+
context: Optional context explaining why the action is happening.
|
|
184
|
+
Helps the AI layer make a more accurate decision.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
VerifyResult with .allow, .risk_level, .reason, etc.
|
|
188
|
+
|
|
189
|
+
Raises:
|
|
190
|
+
AuthError Invalid or missing API key.
|
|
191
|
+
RateLimitError Monthly call limit reached.
|
|
192
|
+
BreakLoopError Agent rate limit (BREAK_LOOP) triggered.
|
|
193
|
+
SentryixError Any other API or network error.
|
|
194
|
+
|
|
195
|
+
Example:
|
|
196
|
+
result = guard.verify(
|
|
197
|
+
action="Export all user emails to CSV",
|
|
198
|
+
agent_id="export-bot",
|
|
199
|
+
context="Admin requested data export"
|
|
200
|
+
)
|
|
201
|
+
if not result.allow:
|
|
202
|
+
raise PermissionError(f"Blocked: {result.reason}")
|
|
203
|
+
"""
|
|
204
|
+
if not action or not action.strip():
|
|
205
|
+
raise SentryixError("action cannot be empty")
|
|
206
|
+
|
|
207
|
+
payload = {
|
|
208
|
+
"action": action.strip(),
|
|
209
|
+
"agent_id": agent_id,
|
|
210
|
+
"context": context,
|
|
211
|
+
"api_key": self.api_key,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
data = self._post("/api/classify", payload)
|
|
215
|
+
return self._parse_result(data)
|
|
216
|
+
|
|
217
|
+
# ── Internal ───────────────────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
def _post(self, path: str, payload: dict) -> dict:
|
|
220
|
+
url = self.base_url + path
|
|
221
|
+
body = json.dumps(payload).encode("utf-8")
|
|
222
|
+
headers = {
|
|
223
|
+
"Content-Type": "application/json",
|
|
224
|
+
"User-Agent": f"sentryix-python/{__version__}",
|
|
225
|
+
"X-Sentryix-Origin": self.base_url,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
req = urllib.request.Request(url, data=body, headers=headers, method="POST")
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
232
|
+
raw = resp.read().decode("utf-8")
|
|
233
|
+
return json.loads(raw)
|
|
234
|
+
except urllib.error.HTTPError as e:
|
|
235
|
+
raw = e.read().decode("utf-8")
|
|
236
|
+
try:
|
|
237
|
+
err_data = json.loads(raw)
|
|
238
|
+
except Exception:
|
|
239
|
+
err_data = {"error": raw}
|
|
240
|
+
|
|
241
|
+
msg = err_data.get("error", f"HTTP {e.code}")
|
|
242
|
+
|
|
243
|
+
if e.code == 401 or "invalid key" in msg.lower() or "not found" in msg.lower():
|
|
244
|
+
raise AuthError(msg, status_code=e.code) from e
|
|
245
|
+
if e.code == 429 or "limit" in msg.lower():
|
|
246
|
+
if "BREAK_LOOP" in msg:
|
|
247
|
+
raise BreakLoopError(msg, status_code=e.code) from e
|
|
248
|
+
raise RateLimitError(msg, status_code=e.code) from e
|
|
249
|
+
raise SentryixError(msg, status_code=e.code) from e
|
|
250
|
+
|
|
251
|
+
except urllib.error.URLError as e:
|
|
252
|
+
raise SentryixError(
|
|
253
|
+
f"Network error connecting to Sentryix API: {e.reason}. "
|
|
254
|
+
"Check your internet connection."
|
|
255
|
+
) from e
|
|
256
|
+
|
|
257
|
+
except Exception as e:
|
|
258
|
+
raise SentryixError(f"Unexpected error: {e}") from e
|
|
259
|
+
|
|
260
|
+
def _parse_result(self, data: dict) -> VerifyResult:
|
|
261
|
+
# Handle LIMIT_REACHED error from the API
|
|
262
|
+
err = data.get("error", "")
|
|
263
|
+
if err:
|
|
264
|
+
if "BREAK_LOOP" in err:
|
|
265
|
+
resets_in = int(err.split("Resumes in ")[-1].replace("s.", "")) if "Resumes in" in err else 0
|
|
266
|
+
raise BreakLoopError(err, resets_in=resets_in)
|
|
267
|
+
if "limit" in err.lower() or "LIMIT_REACHED" in err:
|
|
268
|
+
raise RateLimitError(err)
|
|
269
|
+
if "invalid key" in err.lower() or "not found" in err.lower() or "disabled" in err.lower():
|
|
270
|
+
raise AuthError(err)
|
|
271
|
+
raise SentryixError(err)
|
|
272
|
+
|
|
273
|
+
return VerifyResult(
|
|
274
|
+
allow = bool(data.get("allow", True)),
|
|
275
|
+
risk_level = data.get("riskLevel", "LOW"),
|
|
276
|
+
requires_human = bool(data.get("requiresHuman", False)),
|
|
277
|
+
reason = data.get("reason", ""),
|
|
278
|
+
confidence = int(data.get("confidence", 0)),
|
|
279
|
+
eu_article = data.get("euArticle", ""),
|
|
280
|
+
eu_title = data.get("euTitle", ""),
|
|
281
|
+
agent_id = data.get("agentId", ""),
|
|
282
|
+
action = data.get("action", ""),
|
|
283
|
+
processing_mode = data.get("processingMode", "sync"),
|
|
284
|
+
pii_redacted = bool(data.get("piiRedacted", False)),
|
|
285
|
+
pii_types = data.get("piiTypes", []),
|
|
286
|
+
credit_cost = int(data.get("creditCost", 0)),
|
|
287
|
+
usage = data.get("usage", {}),
|
|
288
|
+
raw = data,
|
|
289
|
+
)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sentryix
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Verify AI agent actions before execution. EU AI Act compliant.
|
|
5
|
+
Home-page: https://sentinelvault-deploy.vercel.app
|
|
6
|
+
Author: Sentryix
|
|
7
|
+
Author-email: sales@sentryix.ai
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://sentinelvault-deploy.vercel.app
|
|
10
|
+
Project-URL: Documentation, https://sentinelvault-deploy.vercel.app/dashboard
|
|
11
|
+
Keywords: ai,agent,governance,security,llm,eu-ai-act,compliance
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
|
|
26
|
+
# sentryix
|
|
27
|
+
|
|
28
|
+
**Verify AI agent actions before execution. EU AI Act compliant.**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install sentryix
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Zero dependencies. Works with Python 3.8+.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from sentryix import Sentryix
|
|
42
|
+
|
|
43
|
+
guard = Sentryix(api_key="sv_your_key_here")
|
|
44
|
+
# Or set SENTRYIX_API_KEY environment variable
|
|
45
|
+
|
|
46
|
+
result = guard.verify(
|
|
47
|
+
agent_id="billing-agent",
|
|
48
|
+
action="Delete all inactive user records older than 90 days",
|
|
49
|
+
context="Scheduled cleanup job, triggered by cron"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if result.allow:
|
|
53
|
+
execute_action()
|
|
54
|
+
else:
|
|
55
|
+
notify_human(result.reason)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## What You Get Back
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
result.allow # True / False
|
|
64
|
+
result.risk_level # "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
|
|
65
|
+
result.requires_human # True if human approval required
|
|
66
|
+
result.reason # "Mass deletion operation — irreversible data loss risk."
|
|
67
|
+
result.confidence # 97
|
|
68
|
+
result.eu_article # "Article 9"
|
|
69
|
+
result.eu_title # "Risk Management System"
|
|
70
|
+
result.pii_redacted # True if PII was detected and redacted
|
|
71
|
+
result.processing_mode # "sync" (AI verified) or "async" (rule engine)
|
|
72
|
+
result.usage # {"callsUsed": 42, "callsLimit": 500, "tier": "free"}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## LangChain Integration
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from sentryix import Sentryix, SentryixError
|
|
81
|
+
guard = Sentryix()
|
|
82
|
+
|
|
83
|
+
class SentryixGuard:
|
|
84
|
+
def __call__(self, action: str) -> bool:
|
|
85
|
+
result = guard.verify(agent_id="langchain-agent", action=action)
|
|
86
|
+
if not result.allow:
|
|
87
|
+
raise PermissionError(f"[{result.risk_level}] {result.reason}")
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
# Add to your LangChain agent before any tool execution
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CrewAI Integration
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from crewai import Agent, Task
|
|
97
|
+
from sentryix import Sentryix
|
|
98
|
+
|
|
99
|
+
guard = Sentryix()
|
|
100
|
+
|
|
101
|
+
def governed_execute(agent_name: str, action: str):
|
|
102
|
+
result = guard.verify(agent_id=agent_name, action=action)
|
|
103
|
+
if not result.allow:
|
|
104
|
+
return f"BLOCKED [{result.risk_level}]: {result.reason}"
|
|
105
|
+
return execute(action)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Error Handling
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from sentryix import Sentryix, AuthError, RateLimitError, BreakLoopError
|
|
114
|
+
|
|
115
|
+
guard = Sentryix(api_key="sv_...")
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
result = guard.verify(agent_id="bot", action=user_action)
|
|
119
|
+
except AuthError:
|
|
120
|
+
# Invalid or expired API key
|
|
121
|
+
pass
|
|
122
|
+
except RateLimitError as e:
|
|
123
|
+
# Monthly limit reached — e.tier, e.limit
|
|
124
|
+
pass
|
|
125
|
+
except BreakLoopError as e:
|
|
126
|
+
# Agent fired too many calls — e.resets_in seconds
|
|
127
|
+
time.sleep(e.resets_in)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Environment Variable
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
export SENTRYIX_API_KEY="sv_your_key_here"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
guard = Sentryix() # picks up SENTRYIX_API_KEY automatically
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## EU AI Act Compliance
|
|
145
|
+
|
|
146
|
+
Every verification is logged with the applicable EU AI Act article:
|
|
147
|
+
|
|
148
|
+
| Risk Level | Article | Requirement |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| LOW | Article 12 | Record-keeping |
|
|
151
|
+
| MEDIUM | Article 13 | Transparency |
|
|
152
|
+
| HIGH | Article 14 | Human Oversight |
|
|
153
|
+
| CRITICAL | Article 9/10 | Risk Management / Data Governance |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Get an API Key
|
|
158
|
+
|
|
159
|
+
Free tier: 500 verifications/month, no credit card.
|
|
160
|
+
|
|
161
|
+
→ [sentinelvault-deploy.vercel.app/dashboard](https://sentinelvault-deploy.vercel.app/dashboard)
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sentryix
|
sentryix-1.0.0/setup.cfg
ADDED
sentryix-1.0.0/setup.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
4
|
+
long_description = f.read()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name="sentryix",
|
|
8
|
+
version="1.0.0",
|
|
9
|
+
author="Sentryix",
|
|
10
|
+
author_email="sales@sentryix.ai",
|
|
11
|
+
description="Verify AI agent actions before execution. EU AI Act compliant.",
|
|
12
|
+
long_description=long_description,
|
|
13
|
+
long_description_content_type="text/markdown",
|
|
14
|
+
url="https://sentinelvault-deploy.vercel.app",
|
|
15
|
+
project_urls={
|
|
16
|
+
"Documentation": "https://sentinelvault-deploy.vercel.app/dashboard",
|
|
17
|
+
"Source": "https://github.com/sentryix/sentryix-python",
|
|
18
|
+
"Tracker": "https://github.com/sentryix/sentryix-python/issues",
|
|
19
|
+
},
|
|
20
|
+
packages=find_packages(),
|
|
21
|
+
python_requires=">=3.8",
|
|
22
|
+
install_requires=[], # zero dependencies — stdlib only
|
|
23
|
+
classifiers=[
|
|
24
|
+
"Development Status :: 4 - Beta",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Topic :: Security",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"License :: OSI Approved :: MIT License",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.8",
|
|
31
|
+
"Programming Language :: Python :: 3.9",
|
|
32
|
+
"Programming Language :: Python :: 3.10",
|
|
33
|
+
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
35
|
+
],
|
|
36
|
+
keywords=[
|
|
37
|
+
"ai", "agent", "governance", "security", "llm",
|
|
38
|
+
"eu-ai-act", "compliance", "guardrails", "prompt-injection"
|
|
39
|
+
],
|
|
40
|
+
)
|