desicon-seal 1.0.2__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,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: desicon-seal
3
+ Version: 1.0.2
4
+ Summary: Zero-latency App-Layer WAF, Automated SRE Logging, and AI Rescue Engine.
5
+ Home-page: https://github.com/Desicon-AI/seal-python
6
+ Author: Seal Enterprise Security
7
+ Author-email: hello@circle-sure.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: requests>=2.25.1
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: requires-dist
21
+ Dynamic: requires-python
22
+ Dynamic: summary
23
+
24
+ # Seal Python SDK Setup Documentation
25
+
26
+ ## 1. Quick Start
27
+
28
+ ### Step 1: Install the SDK
29
+ ```bash
30
+ pip install desicon-seal
31
+ ```
32
+
33
+ ### Step 2: Initialize Seal & Add ASGI Middleware
34
+ For modern Python frameworks (FastAPI, Starlette, Django 3.0+), you can easily add the Edge-equivalent WAF to your application using the `SealASGIMiddleware`.
35
+
36
+ ```python
37
+ from fastapi import FastAPI
38
+ from seal import seal
39
+
40
+ app = FastAPI()
41
+
42
+ # 1. Initialize Telemetry & SRE
43
+ seal.init(
44
+ api_key="grp_live_xxxx", # From your dashboard
45
+ app_name="My FastAPI App",
46
+ environment="production"
47
+ )
48
+
49
+ # 2. Add the WAF Middleware to protect your API
50
+ app.add_middleware(seal.asgi_middleware())
51
+ ```
52
+
53
+ ## 2. Configuration Options
54
+ | Python Parameter | Node.js / Browser Parameter | Type | Description |
55
+ | --- | --- | --- | --- |
56
+ | `api_key` | `apiKey` | `string` | Your project API key from the dashboard. |
57
+ | `app_name` | `appName` | `string` | A human-readable name for your application. |
58
+ | `environment` | `environment` | `string` | Current deployment environment: 'production', 'staging', or 'development'. |
59
+ | `rescue_engine` | `rescueEngine` | `boolean` | Set to true to enable automated AI hotfix generation and patching. |
60
+ | `level` | `level` | `string` | fatal | error | warning |
61
+
62
+ ## 3. The Sensitivity Dialer
63
+ The Sensitivity Dialer controls how much noise Grumpy generates. Set it in two places:
64
+ 1. **SDK**: Set level in grumpy.init().
65
+ 2. **Dashboard**: Override per-project in Settings. Server enforces it.
66
+
67
+ ### Levels:
68
+ - **fatal** (Low): Only unhandled crashes.
69
+ ```js
70
+ grumpy.init({ level: "fatal" });
71
+ ```
72
+ - **error** (Medium): Caught exceptions + fatal.
73
+ ```js
74
+ grumpy.init({ level: "error" });
75
+ ```
76
+ - **warning** (High): Everything. Good luck.
77
+ ```js
78
+ grumpy.init({ level: "warning" });
79
+ ```
80
+
81
+ ## 4. Grumpy Rescue Engine
82
+ When the Rescue Engine is enabled (via Project Settings), Grumpy doesn't just send you an alert. It instructs your connected AI to generate a functional, sandboxed JavaScript fix that addresses the logical error. The fix is minified and encrypted before leaving the server—only your SDK (holding the same API key) can decrypt and execute it.
83
+
84
+ Each fix is uniquely identified (grp_fix_xxxx) and location-aware: the same error type at two different code locations gets two separate fixes. If a fix fails to apply, the SDK automatically reports the failure, the bad fix is scrubbed server-side, and a fresh fix is generated on the next occurrence. No stale fixes, no fix-the-fix loops.
85
+
86
+ - 1. Enable 'Grumpy Rescue Engine' in your Project settings.
87
+ - 2. Ensure you have an AI Provider configured in settings.
88
+ - 3. When an unhandled error occurs, Grumpy generates an encrypted rescue fix.
89
+ - 4. The SDK decrypts and applies this fix in memory—invisible to network inspectors, DevTools, and MITM proxies.
90
+
91
+ ### Stack Compatibility & Frameworks
92
+ The Rescue Engine dynamically patches JavaScript bugs in the browser, but it is fully compatible with ANY backend stack.
93
+
94
+ **Server-Rendered Apps:** Even if your backend is Python or PHP, your users still interact with HTML/JS in the browser. Drop the Grumpy JS SDK into your Jinja2, Django, or Blade templates. The Rescue Engine will catch and hotfix frontend JavaScript bugs seamlessly.
95
+
96
+ ```html
97
+ <!DOCTYPE html>
98
+ <html>
99
+ <head>
100
+ <!-- Drop Grumpy before your other scripts -->
101
+ <script src="https://cdn.jsdelivr.net/gh/Desicon-AI/grumpy-cdn@main/grumpy.js"></script>
102
+ <script>
103
+ Grumpy.init({
104
+ apiKey: "{{ GRUMPY_API_KEY }}", // Passed from backend
105
+ appName: "My Web App",
106
+ rescueEngine: true
107
+ });
108
+ </script>
109
+ </head>
110
+ <body>
111
+ {% block content %}{% endblock %}
112
+ </body>
113
+ </html>
114
+ ```
115
+
116
+ ## 5. AI Personas
117
+ Grumpy speaks in three voices. Choose the one that fits your team culture. Personas are set at the Organization level and apply to all projects.
118
+
119
+ ### Grumpy (Sarcastic & Mean) — All Plans
120
+ The default. Grumpy roasts your code, insults your variable names, and grudgingly tells you how to fix it. Perfect for teams with thick skin and a sense of humor.
121
+
122
+ > Example: "You passed a string to parseInt and expected magic? Cast your variables, you absolute menace. Here's the fix, don't make me say it twice."
123
+
124
+ ### Friendly (Supportive & Positive) — Pro / Team
125
+ A warm, encouraging senior dev who wants you to know it's going to be okay. Uses emojis. Celebrates small wins. Great for junior-heavy teams or if HR keeps filing complaints.
126
+
127
+ > Example: "Hey! 👋 Looks like a small type mismatch slipped through — happens to everyone! Here's a quick fix. You're doing great! 🎉"
128
+
129
+ ### Professional (Strategic & Objective) — Pro / Team
130
+ An enterprise-grade SRE delivering strictly factual root cause analysis and remediation steps. No jokes, no fluff. Designed for large organizations, regulated industries, and teams that need clean audit trails.
131
+
132
+ > Example: "Root Cause: Type coercion failure in paymentAPI.submit(). Remediation: Validate input type before invocation. Patch recommended."
133
+
134
+ ### How to configure
135
+ All future error analyses across every project in your organization will use the new voice.
136
+ ```text
137
+ 1. Dashboard → Settings → Organization Persona
138
+ 2. Select your preferred persona and click Save
139
+ ```
140
+
141
+ ## 7. Integrations
142
+
143
+ ### Bring Your Own AI (BYOAI)
144
+ Grumpy works out of the box for free using our massive database of hand-crafted roasts. But if you want AI to suggest actual code fixes, connect any AI provider (OpenAI, Anthropic, Groq, Ollama). We never markup token usage. If your API goes down, Grumpy seamlessly falls back to standard mode.
145
+ ```text
146
+ Dashboard → Settings → Select Provider → Paste Key
147
+ ```
148
+
149
+ ### Slack Webhooks
150
+ Grumpy sends rich, formatted alerts directly to your Slack channels with full stack traces, AI analysis, and severity badges.
151
+ ```text
152
+ 1. Dashboard → Settings → Connect Slack
153
+ 2. Slack → Channel → Settings → Integrations → Add Grumpy
154
+ ```
155
+
156
+ ### Discord Webhooks
157
+ Same rich alerts, same brutal honesty — delivered to your Discord server. Perfect for indie devs and open source projects.
158
+ ```text
159
+ Dashboard → Settings → Connect Discord (automatic)
160
+ ```
161
+
162
+ ## 8. The 'Not My Fault' Engine
163
+ Our 'Not My Fault' engine analyzes stack traces. If a crash originated from an external package, we will notify the maintainer of the issue so they can work on it. But we are smart about it. If we notice that the error was caused by missing parameters or incorrect usage by the developer, rather than sending the maintainer a ticket, we notify the developer of the correct use.
164
+
165
+ ### For Maintainers
166
+ Sign up for a free Grumpy account to claim and track your public npm/pip packages. You only need a paid subscription if you want to use Grumpy to monitor your own applications.
167
+
168
+ ### For Developers
169
+ Enable this feature within your project settings so that genuine errors from external packages are automatically sent to the maintainers.
170
+ ```text
171
+ Dashboard → Settings → Enable 'Not My Fault'
172
+ ```
173
+
174
+ ## 9. Sandbox Testing
175
+ Use your Sandbox project to test Grumpy's ingestion and AI analysis without triggering webhooks or skewing your live analytics. Sandbox API keys always start with sandbox_ and can be found in your Dashboard. You can also test everything interactively in the Playground.
176
+
177
+ ```python
178
+ from grumpy_ai import grumpy
179
+
180
+ grumpy.init(
181
+ api_key="sandbox_YOUR_KEY",
182
+ app_name="Test App",
183
+ environment="development",
184
+ endpoint="https://sealengine.desicon.ai/api/v1/sandbox/ingest"
185
+ )
186
+ ```
187
+
@@ -0,0 +1,164 @@
1
+ # Seal Python SDK Setup Documentation
2
+
3
+ ## 1. Quick Start
4
+
5
+ ### Step 1: Install the SDK
6
+ ```bash
7
+ pip install desicon-seal
8
+ ```
9
+
10
+ ### Step 2: Initialize Seal & Add ASGI Middleware
11
+ For modern Python frameworks (FastAPI, Starlette, Django 3.0+), you can easily add the Edge-equivalent WAF to your application using the `SealASGIMiddleware`.
12
+
13
+ ```python
14
+ from fastapi import FastAPI
15
+ from seal import seal
16
+
17
+ app = FastAPI()
18
+
19
+ # 1. Initialize Telemetry & SRE
20
+ seal.init(
21
+ api_key="grp_live_xxxx", # From your dashboard
22
+ app_name="My FastAPI App",
23
+ environment="production"
24
+ )
25
+
26
+ # 2. Add the WAF Middleware to protect your API
27
+ app.add_middleware(seal.asgi_middleware())
28
+ ```
29
+
30
+ ## 2. Configuration Options
31
+ | Python Parameter | Node.js / Browser Parameter | Type | Description |
32
+ | --- | --- | --- | --- |
33
+ | `api_key` | `apiKey` | `string` | Your project API key from the dashboard. |
34
+ | `app_name` | `appName` | `string` | A human-readable name for your application. |
35
+ | `environment` | `environment` | `string` | Current deployment environment: 'production', 'staging', or 'development'. |
36
+ | `rescue_engine` | `rescueEngine` | `boolean` | Set to true to enable automated AI hotfix generation and patching. |
37
+ | `level` | `level` | `string` | fatal | error | warning |
38
+
39
+ ## 3. The Sensitivity Dialer
40
+ The Sensitivity Dialer controls how much noise Grumpy generates. Set it in two places:
41
+ 1. **SDK**: Set level in grumpy.init().
42
+ 2. **Dashboard**: Override per-project in Settings. Server enforces it.
43
+
44
+ ### Levels:
45
+ - **fatal** (Low): Only unhandled crashes.
46
+ ```js
47
+ grumpy.init({ level: "fatal" });
48
+ ```
49
+ - **error** (Medium): Caught exceptions + fatal.
50
+ ```js
51
+ grumpy.init({ level: "error" });
52
+ ```
53
+ - **warning** (High): Everything. Good luck.
54
+ ```js
55
+ grumpy.init({ level: "warning" });
56
+ ```
57
+
58
+ ## 4. Grumpy Rescue Engine
59
+ When the Rescue Engine is enabled (via Project Settings), Grumpy doesn't just send you an alert. It instructs your connected AI to generate a functional, sandboxed JavaScript fix that addresses the logical error. The fix is minified and encrypted before leaving the server—only your SDK (holding the same API key) can decrypt and execute it.
60
+
61
+ Each fix is uniquely identified (grp_fix_xxxx) and location-aware: the same error type at two different code locations gets two separate fixes. If a fix fails to apply, the SDK automatically reports the failure, the bad fix is scrubbed server-side, and a fresh fix is generated on the next occurrence. No stale fixes, no fix-the-fix loops.
62
+
63
+ - 1. Enable 'Grumpy Rescue Engine' in your Project settings.
64
+ - 2. Ensure you have an AI Provider configured in settings.
65
+ - 3. When an unhandled error occurs, Grumpy generates an encrypted rescue fix.
66
+ - 4. The SDK decrypts and applies this fix in memory—invisible to network inspectors, DevTools, and MITM proxies.
67
+
68
+ ### Stack Compatibility & Frameworks
69
+ The Rescue Engine dynamically patches JavaScript bugs in the browser, but it is fully compatible with ANY backend stack.
70
+
71
+ **Server-Rendered Apps:** Even if your backend is Python or PHP, your users still interact with HTML/JS in the browser. Drop the Grumpy JS SDK into your Jinja2, Django, or Blade templates. The Rescue Engine will catch and hotfix frontend JavaScript bugs seamlessly.
72
+
73
+ ```html
74
+ <!DOCTYPE html>
75
+ <html>
76
+ <head>
77
+ <!-- Drop Grumpy before your other scripts -->
78
+ <script src="https://cdn.jsdelivr.net/gh/Desicon-AI/grumpy-cdn@main/grumpy.js"></script>
79
+ <script>
80
+ Grumpy.init({
81
+ apiKey: "{{ GRUMPY_API_KEY }}", // Passed from backend
82
+ appName: "My Web App",
83
+ rescueEngine: true
84
+ });
85
+ </script>
86
+ </head>
87
+ <body>
88
+ {% block content %}{% endblock %}
89
+ </body>
90
+ </html>
91
+ ```
92
+
93
+ ## 5. AI Personas
94
+ Grumpy speaks in three voices. Choose the one that fits your team culture. Personas are set at the Organization level and apply to all projects.
95
+
96
+ ### Grumpy (Sarcastic & Mean) — All Plans
97
+ The default. Grumpy roasts your code, insults your variable names, and grudgingly tells you how to fix it. Perfect for teams with thick skin and a sense of humor.
98
+
99
+ > Example: "You passed a string to parseInt and expected magic? Cast your variables, you absolute menace. Here's the fix, don't make me say it twice."
100
+
101
+ ### Friendly (Supportive & Positive) — Pro / Team
102
+ A warm, encouraging senior dev who wants you to know it's going to be okay. Uses emojis. Celebrates small wins. Great for junior-heavy teams or if HR keeps filing complaints.
103
+
104
+ > Example: "Hey! 👋 Looks like a small type mismatch slipped through — happens to everyone! Here's a quick fix. You're doing great! 🎉"
105
+
106
+ ### Professional (Strategic & Objective) — Pro / Team
107
+ An enterprise-grade SRE delivering strictly factual root cause analysis and remediation steps. No jokes, no fluff. Designed for large organizations, regulated industries, and teams that need clean audit trails.
108
+
109
+ > Example: "Root Cause: Type coercion failure in paymentAPI.submit(). Remediation: Validate input type before invocation. Patch recommended."
110
+
111
+ ### How to configure
112
+ All future error analyses across every project in your organization will use the new voice.
113
+ ```text
114
+ 1. Dashboard → Settings → Organization Persona
115
+ 2. Select your preferred persona and click Save
116
+ ```
117
+
118
+ ## 7. Integrations
119
+
120
+ ### Bring Your Own AI (BYOAI)
121
+ Grumpy works out of the box for free using our massive database of hand-crafted roasts. But if you want AI to suggest actual code fixes, connect any AI provider (OpenAI, Anthropic, Groq, Ollama). We never markup token usage. If your API goes down, Grumpy seamlessly falls back to standard mode.
122
+ ```text
123
+ Dashboard → Settings → Select Provider → Paste Key
124
+ ```
125
+
126
+ ### Slack Webhooks
127
+ Grumpy sends rich, formatted alerts directly to your Slack channels with full stack traces, AI analysis, and severity badges.
128
+ ```text
129
+ 1. Dashboard → Settings → Connect Slack
130
+ 2. Slack → Channel → Settings → Integrations → Add Grumpy
131
+ ```
132
+
133
+ ### Discord Webhooks
134
+ Same rich alerts, same brutal honesty — delivered to your Discord server. Perfect for indie devs and open source projects.
135
+ ```text
136
+ Dashboard → Settings → Connect Discord (automatic)
137
+ ```
138
+
139
+ ## 8. The 'Not My Fault' Engine
140
+ Our 'Not My Fault' engine analyzes stack traces. If a crash originated from an external package, we will notify the maintainer of the issue so they can work on it. But we are smart about it. If we notice that the error was caused by missing parameters or incorrect usage by the developer, rather than sending the maintainer a ticket, we notify the developer of the correct use.
141
+
142
+ ### For Maintainers
143
+ Sign up for a free Grumpy account to claim and track your public npm/pip packages. You only need a paid subscription if you want to use Grumpy to monitor your own applications.
144
+
145
+ ### For Developers
146
+ Enable this feature within your project settings so that genuine errors from external packages are automatically sent to the maintainers.
147
+ ```text
148
+ Dashboard → Settings → Enable 'Not My Fault'
149
+ ```
150
+
151
+ ## 9. Sandbox Testing
152
+ Use your Sandbox project to test Grumpy's ingestion and AI analysis without triggering webhooks or skewing your live analytics. Sandbox API keys always start with sandbox_ and can be found in your Dashboard. You can also test everything interactively in the Playground.
153
+
154
+ ```python
155
+ from grumpy_ai import grumpy
156
+
157
+ grumpy.init(
158
+ api_key="sandbox_YOUR_KEY",
159
+ app_name="Test App",
160
+ environment="development",
161
+ endpoint="https://sealengine.desicon.ai/api/v1/sandbox/ingest"
162
+ )
163
+ ```
164
+
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: desicon-seal
3
+ Version: 1.0.2
4
+ Summary: Zero-latency App-Layer WAF, Automated SRE Logging, and AI Rescue Engine.
5
+ Home-page: https://github.com/Desicon-AI/seal-python
6
+ Author: Seal Enterprise Security
7
+ Author-email: hello@circle-sure.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: requests>=2.25.1
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: requires-dist
21
+ Dynamic: requires-python
22
+ Dynamic: summary
23
+
24
+ # Seal Python SDK Setup Documentation
25
+
26
+ ## 1. Quick Start
27
+
28
+ ### Step 1: Install the SDK
29
+ ```bash
30
+ pip install desicon-seal
31
+ ```
32
+
33
+ ### Step 2: Initialize Seal & Add ASGI Middleware
34
+ For modern Python frameworks (FastAPI, Starlette, Django 3.0+), you can easily add the Edge-equivalent WAF to your application using the `SealASGIMiddleware`.
35
+
36
+ ```python
37
+ from fastapi import FastAPI
38
+ from seal import seal
39
+
40
+ app = FastAPI()
41
+
42
+ # 1. Initialize Telemetry & SRE
43
+ seal.init(
44
+ api_key="grp_live_xxxx", # From your dashboard
45
+ app_name="My FastAPI App",
46
+ environment="production"
47
+ )
48
+
49
+ # 2. Add the WAF Middleware to protect your API
50
+ app.add_middleware(seal.asgi_middleware())
51
+ ```
52
+
53
+ ## 2. Configuration Options
54
+ | Python Parameter | Node.js / Browser Parameter | Type | Description |
55
+ | --- | --- | --- | --- |
56
+ | `api_key` | `apiKey` | `string` | Your project API key from the dashboard. |
57
+ | `app_name` | `appName` | `string` | A human-readable name for your application. |
58
+ | `environment` | `environment` | `string` | Current deployment environment: 'production', 'staging', or 'development'. |
59
+ | `rescue_engine` | `rescueEngine` | `boolean` | Set to true to enable automated AI hotfix generation and patching. |
60
+ | `level` | `level` | `string` | fatal | error | warning |
61
+
62
+ ## 3. The Sensitivity Dialer
63
+ The Sensitivity Dialer controls how much noise Grumpy generates. Set it in two places:
64
+ 1. **SDK**: Set level in grumpy.init().
65
+ 2. **Dashboard**: Override per-project in Settings. Server enforces it.
66
+
67
+ ### Levels:
68
+ - **fatal** (Low): Only unhandled crashes.
69
+ ```js
70
+ grumpy.init({ level: "fatal" });
71
+ ```
72
+ - **error** (Medium): Caught exceptions + fatal.
73
+ ```js
74
+ grumpy.init({ level: "error" });
75
+ ```
76
+ - **warning** (High): Everything. Good luck.
77
+ ```js
78
+ grumpy.init({ level: "warning" });
79
+ ```
80
+
81
+ ## 4. Grumpy Rescue Engine
82
+ When the Rescue Engine is enabled (via Project Settings), Grumpy doesn't just send you an alert. It instructs your connected AI to generate a functional, sandboxed JavaScript fix that addresses the logical error. The fix is minified and encrypted before leaving the server—only your SDK (holding the same API key) can decrypt and execute it.
83
+
84
+ Each fix is uniquely identified (grp_fix_xxxx) and location-aware: the same error type at two different code locations gets two separate fixes. If a fix fails to apply, the SDK automatically reports the failure, the bad fix is scrubbed server-side, and a fresh fix is generated on the next occurrence. No stale fixes, no fix-the-fix loops.
85
+
86
+ - 1. Enable 'Grumpy Rescue Engine' in your Project settings.
87
+ - 2. Ensure you have an AI Provider configured in settings.
88
+ - 3. When an unhandled error occurs, Grumpy generates an encrypted rescue fix.
89
+ - 4. The SDK decrypts and applies this fix in memory—invisible to network inspectors, DevTools, and MITM proxies.
90
+
91
+ ### Stack Compatibility & Frameworks
92
+ The Rescue Engine dynamically patches JavaScript bugs in the browser, but it is fully compatible with ANY backend stack.
93
+
94
+ **Server-Rendered Apps:** Even if your backend is Python or PHP, your users still interact with HTML/JS in the browser. Drop the Grumpy JS SDK into your Jinja2, Django, or Blade templates. The Rescue Engine will catch and hotfix frontend JavaScript bugs seamlessly.
95
+
96
+ ```html
97
+ <!DOCTYPE html>
98
+ <html>
99
+ <head>
100
+ <!-- Drop Grumpy before your other scripts -->
101
+ <script src="https://cdn.jsdelivr.net/gh/Desicon-AI/grumpy-cdn@main/grumpy.js"></script>
102
+ <script>
103
+ Grumpy.init({
104
+ apiKey: "{{ GRUMPY_API_KEY }}", // Passed from backend
105
+ appName: "My Web App",
106
+ rescueEngine: true
107
+ });
108
+ </script>
109
+ </head>
110
+ <body>
111
+ {% block content %}{% endblock %}
112
+ </body>
113
+ </html>
114
+ ```
115
+
116
+ ## 5. AI Personas
117
+ Grumpy speaks in three voices. Choose the one that fits your team culture. Personas are set at the Organization level and apply to all projects.
118
+
119
+ ### Grumpy (Sarcastic & Mean) — All Plans
120
+ The default. Grumpy roasts your code, insults your variable names, and grudgingly tells you how to fix it. Perfect for teams with thick skin and a sense of humor.
121
+
122
+ > Example: "You passed a string to parseInt and expected magic? Cast your variables, you absolute menace. Here's the fix, don't make me say it twice."
123
+
124
+ ### Friendly (Supportive & Positive) — Pro / Team
125
+ A warm, encouraging senior dev who wants you to know it's going to be okay. Uses emojis. Celebrates small wins. Great for junior-heavy teams or if HR keeps filing complaints.
126
+
127
+ > Example: "Hey! 👋 Looks like a small type mismatch slipped through — happens to everyone! Here's a quick fix. You're doing great! 🎉"
128
+
129
+ ### Professional (Strategic & Objective) — Pro / Team
130
+ An enterprise-grade SRE delivering strictly factual root cause analysis and remediation steps. No jokes, no fluff. Designed for large organizations, regulated industries, and teams that need clean audit trails.
131
+
132
+ > Example: "Root Cause: Type coercion failure in paymentAPI.submit(). Remediation: Validate input type before invocation. Patch recommended."
133
+
134
+ ### How to configure
135
+ All future error analyses across every project in your organization will use the new voice.
136
+ ```text
137
+ 1. Dashboard → Settings → Organization Persona
138
+ 2. Select your preferred persona and click Save
139
+ ```
140
+
141
+ ## 7. Integrations
142
+
143
+ ### Bring Your Own AI (BYOAI)
144
+ Grumpy works out of the box for free using our massive database of hand-crafted roasts. But if you want AI to suggest actual code fixes, connect any AI provider (OpenAI, Anthropic, Groq, Ollama). We never markup token usage. If your API goes down, Grumpy seamlessly falls back to standard mode.
145
+ ```text
146
+ Dashboard → Settings → Select Provider → Paste Key
147
+ ```
148
+
149
+ ### Slack Webhooks
150
+ Grumpy sends rich, formatted alerts directly to your Slack channels with full stack traces, AI analysis, and severity badges.
151
+ ```text
152
+ 1. Dashboard → Settings → Connect Slack
153
+ 2. Slack → Channel → Settings → Integrations → Add Grumpy
154
+ ```
155
+
156
+ ### Discord Webhooks
157
+ Same rich alerts, same brutal honesty — delivered to your Discord server. Perfect for indie devs and open source projects.
158
+ ```text
159
+ Dashboard → Settings → Connect Discord (automatic)
160
+ ```
161
+
162
+ ## 8. The 'Not My Fault' Engine
163
+ Our 'Not My Fault' engine analyzes stack traces. If a crash originated from an external package, we will notify the maintainer of the issue so they can work on it. But we are smart about it. If we notice that the error was caused by missing parameters or incorrect usage by the developer, rather than sending the maintainer a ticket, we notify the developer of the correct use.
164
+
165
+ ### For Maintainers
166
+ Sign up for a free Grumpy account to claim and track your public npm/pip packages. You only need a paid subscription if you want to use Grumpy to monitor your own applications.
167
+
168
+ ### For Developers
169
+ Enable this feature within your project settings so that genuine errors from external packages are automatically sent to the maintainers.
170
+ ```text
171
+ Dashboard → Settings → Enable 'Not My Fault'
172
+ ```
173
+
174
+ ## 9. Sandbox Testing
175
+ Use your Sandbox project to test Grumpy's ingestion and AI analysis without triggering webhooks or skewing your live analytics. Sandbox API keys always start with sandbox_ and can be found in your Dashboard. You can also test everything interactively in the Playground.
176
+
177
+ ```python
178
+ from grumpy_ai import grumpy
179
+
180
+ grumpy.init(
181
+ api_key="sandbox_YOUR_KEY",
182
+ app_name="Test App",
183
+ environment="development",
184
+ endpoint="https://sealengine.desicon.ai/api/v1/sandbox/ingest"
185
+ )
186
+ ```
187
+
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ desicon_seal.egg-info/PKG-INFO
5
+ desicon_seal.egg-info/SOURCES.txt
6
+ desicon_seal.egg-info/dependency_links.txt
7
+ desicon_seal.egg-info/requires.txt
8
+ desicon_seal.egg-info/top_level.txt
9
+ seal/__init__.py
10
+ seal/client.py
@@ -0,0 +1 @@
1
+ requests>=2.25.1
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,3 @@
1
+ from .client import seal
2
+
3
+ __all__ = ["seal"]
@@ -0,0 +1,318 @@
1
+ import sys
2
+ import os
3
+ import traceback
4
+ import requests
5
+ import threading
6
+ import time
7
+ import hmac
8
+ import hashlib
9
+ import json
10
+
11
+ class SealClient:
12
+ def __init__(self):
13
+ self.api_key = None
14
+ self.signing_secret = None
15
+ self.environment = "development"
16
+ self.app_name = "unknown_app"
17
+ self._original_excepthook = None
18
+ self.ingest_url = "https://sealengine.desicon.ai/api/v1/ingest"
19
+ self.start_time = int(time.time() * 1000)
20
+
21
+ def init(self, api_key: str, app_name: str, environment: str = "production", ingest_url: str = None, waf: dict = None, signing_secret: str = None):
22
+ self.api_key = api_key
23
+ self.signing_secret = signing_secret
24
+ self.app_name = app_name
25
+ self.environment = environment
26
+ if ingest_url:
27
+ self.ingest_url = ingest_url
28
+
29
+ self.waf_config = {
30
+ "geoBlocking": { "blockedCountries": [], "action": "report" },
31
+ "maliciousScanners": { "action": "drop" },
32
+ "methodTampering": { "action": "report" },
33
+ "payloadOverflow": { "maxPayloadSize": 5242880, "action": "report" },
34
+ "pathTraversal": { "action": "drop" }
35
+ }
36
+ if waf:
37
+ # Deep update
38
+ for k, v in waf.items():
39
+ if k in self.waf_config and isinstance(v, dict):
40
+ self.waf_config[k].update(v)
41
+ else:
42
+ self.waf_config[k] = v
43
+
44
+ # Override the global exception hook
45
+ self._original_excepthook = sys.excepthook
46
+ sys.excepthook = self._seal_excepthook
47
+
48
+ # Ping the backend to auto-resolve old errors on deployment/startup
49
+ try:
50
+ import requests
51
+ headers = self._get_headers()
52
+ ping_url = self.ingest_url + "/ping" if not self.ingest_url.endswith("/") else self.ingest_url + "ping"
53
+ requests.post(ping_url, headers=headers, timeout=3)
54
+
55
+ # Start the Dead Man's Switch heartbeat
56
+ self._start_heartbeat()
57
+ except Exception as e:
58
+ # Silently fail so we don't break the host app if the network is down
59
+ pass
60
+
61
+ print(f"Seal.ai initialized for {self.app_name} ({self.environment}). We are watching you.")
62
+
63
+ def _get_headers(self, payload_str=None):
64
+ headers = {"X-API-Key": self.api_key}
65
+ if payload_str and self.signing_secret:
66
+ timestamp = str(int(time.time()))
67
+ msg = f"{timestamp}.{payload_str}".encode('utf-8')
68
+ signature = hmac.new(self.signing_secret.encode('utf-8'), msg, hashlib.sha256).hexdigest()
69
+ headers["X-Seal-Timestamp"] = timestamp
70
+ headers["X-Seal-Signature"] = signature
71
+ elif self.signing_secret:
72
+ timestamp = str(int(time.time()))
73
+ msg = f"{timestamp}.".encode('utf-8')
74
+ signature = hmac.new(self.signing_secret.encode('utf-8'), msg, hashlib.sha256).hexdigest()
75
+ headers["X-Seal-Timestamp"] = timestamp
76
+ headers["X-Seal-Signature"] = signature
77
+ return headers
78
+
79
+ def _start_heartbeat(self):
80
+ def heartbeat_loop():
81
+ payload = {
82
+ "app_name": self.app_name,
83
+ "environment": self.environment,
84
+ "started_at": self.start_time
85
+ }
86
+ headers = self._get_headers(json.dumps(payload))
87
+
88
+ heartbeat_url = self.ingest_url + "/heartbeat" if not self.ingest_url.endswith("/") else self.ingest_url + "heartbeat"
89
+
90
+ # Initial ping
91
+ try:
92
+ requests.post(heartbeat_url, json=payload, headers=headers, timeout=5)
93
+ except Exception:
94
+ pass
95
+
96
+ while True:
97
+ time.sleep(60)
98
+ try:
99
+ requests.post(heartbeat_url, json=payload, headers=headers, timeout=5)
100
+ except Exception:
101
+ pass # Silently fail
102
+
103
+ thread = threading.Thread(target=heartbeat_loop, daemon=True)
104
+ thread.start()
105
+
106
+ def _extract_code_context(self, tb):
107
+ """Walks the stack trace to find the last file and extracts the surrounding lines."""
108
+ try:
109
+ # Extract the raw traceback
110
+ extracted = traceback.extract_tb(tb)
111
+ if not extracted:
112
+ return "No traceback available."
113
+
114
+ # Find the last frame that is actually in our project code
115
+ last_frame = extracted[-1]
116
+ filename = last_frame.filename
117
+ lineno = last_frame.lineno
118
+
119
+ if not os.path.exists(filename):
120
+ return f"Could not locate {filename} on disk."
121
+
122
+ with open(filename, 'r', encoding='utf-8') as f:
123
+ lines = f.readlines()
124
+
125
+ # Grab 5 lines before and 5 lines after the error
126
+ start_idx = max(0, lineno - 6)
127
+ end_idx = min(len(lines), lineno + 5)
128
+
129
+ context = ""
130
+ for i in range(start_idx, end_idx):
131
+ prefix = ">> " if i == (lineno - 1) else " "
132
+ context += f"{prefix}{i + 1}: {lines[i]}"
133
+
134
+ return context
135
+ except Exception as e:
136
+ return f"Failed to extract context: {str(e)}"
137
+
138
+ def _seal_excepthook(self, exc_type, exc_value, exc_traceback):
139
+ tb_str = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
140
+ context_str = self._extract_code_context(exc_traceback)
141
+
142
+ payload = {
143
+ "app_name": self.app_name,
144
+ "error_type": exc_type.__name__,
145
+ "error_message": str(exc_value),
146
+ "stack_trace": tb_str,
147
+ "code_context": context_str,
148
+ "environment": self.environment
149
+ }
150
+ headers = self._get_headers(json.dumps(payload))
151
+
152
+ try:
153
+ print(f"\n[Seal.ai] Catching {exc_type.__name__}... shipping to SRE engine.")
154
+ resp = requests.post(self.ingest_url, json=payload, headers=headers, timeout=5)
155
+ if resp.status_code == 200:
156
+ data = resp.json()
157
+ if data.get("status") == "deduplicated":
158
+ print(f"[Seal.ai] Deduped (seen {data.get('count')} times).")
159
+ else:
160
+ print(f"\n🔔 SEAL'S ANALYSIS:\n{data.get('analysis')}\n")
161
+ except Exception as e:
162
+ print(f"[Seal.ai] Failed to contact server: {e}")
163
+
164
+ # Let it crash normally so we don't break the actual application
165
+ if self._original_excepthook:
166
+ self._original_excepthook(exc_type, exc_value, exc_traceback)
167
+
168
+ def _report_threat(self, threat_type, ip, scope, details=None):
169
+ if not self.api_key:
170
+ return
171
+
172
+ def _send():
173
+ headers = dict(scope.get("headers", []))
174
+ headers_str = {k.decode("utf-8", "ignore"): v.decode("utf-8", "ignore") for k, v in headers.items()}
175
+
176
+ payload = {
177
+ "app_name": self.app_name,
178
+ "environment": self.environment,
179
+ "ip_address": ip,
180
+ "threat_type": threat_type,
181
+ "context": {
182
+ "method": scope.get("method", ""),
183
+ "url": scope.get("path", ""),
184
+ "headers": headers_str,
185
+ }
186
+ }
187
+ if details:
188
+ payload["context"].update(details)
189
+
190
+ threat_url = self.ingest_url + "/threat" if not self.ingest_url.endswith("/") else self.ingest_url + "threat"
191
+
192
+ try:
193
+ import requests
194
+ headers = self._get_headers(json.dumps(payload))
195
+ requests.post(threat_url, json=payload, headers=headers, timeout=3)
196
+ except Exception:
197
+ pass
198
+
199
+ threading.Thread(target=_send, daemon=True).start()
200
+
201
+ def asgi_middleware(self):
202
+ return SealASGIMiddleware
203
+
204
+ class SealASGIMiddleware:
205
+ def __init__(self, app):
206
+ self.app = app
207
+ self.seal = seal
208
+ self.auth_failures = {} # IP -> [timestamps]
209
+ import re
210
+ self.HONEYPOTS = ['/wp-admin', '/wp-login.php', '/.env', '/config.php', '/.git/config']
211
+ self.SQLI_REGEX = re.compile(r"(?:\b(ALTER|CREATE|DELETE|DROP|EXEC(UTE){0,1}|INSERT( +INTO){0,1}|MERGE|SELECT|UPDATE|UNION( +ALL){0,1})\b)|(?:'|%27).*?(?:OR|AND).*?(?:'|%27)|(?:--)", re.IGNORECASE)
212
+ self.XSS_REGEX = re.compile(r"(?:<|%3C)script[\s\S]*?(?:>|%3E)|(?:<|%3C)[\s\S]*?(?:on[a-z]+\s*=)(?:>|%3E)", re.IGNORECASE)
213
+ self.TRAVERSAL_REGEX = re.compile(r"(?:\.\.\/|\.\.\\|%2e%2e%2f|%2e%2e%5c)", re.IGNORECASE)
214
+ self.SCANNER_REGEX = re.compile(r"(sqlmap|nikto|masscan|zmap|nmap|python-requests|curl|wget)", re.IGNORECASE)
215
+ self.ALLOWED_METHODS = [b'GET', b'POST', b'PUT', b'PATCH', b'DELETE', b'OPTIONS', b'HEAD']
216
+
217
+ async def _send_rejection(self, send, status_code, body_message):
218
+ await send({
219
+ "type": "http.response.start",
220
+ "status": status_code,
221
+ "headers": [(b"content-type", b"text/plain")]
222
+ })
223
+ await send({
224
+ "type": "http.response.body",
225
+ "body": body_message.encode("utf-8")
226
+ })
227
+
228
+ async def __call__(self, scope, receive, send):
229
+ if scope["type"] != "http":
230
+ return await self.app(scope, receive, send)
231
+
232
+ path = scope.get("path", "")
233
+ query = scope.get("query_string", b"").decode("utf-8", "ignore")
234
+ full_url = f"{path}?{query}" if query else path
235
+ method = scope.get("method", b"").encode("utf-8") if isinstance(scope.get("method"), str) else scope.get("method", b"")
236
+
237
+ client_ip = "unknown"
238
+ headers = dict(scope.get("headers", []))
239
+ if b"x-forwarded-for" in headers:
240
+ client_ip = headers[b"x-forwarded-for"].decode("utf-8", "ignore").split(",")[0].strip()
241
+ elif scope.get("client"):
242
+ client_ip = scope["client"][0]
243
+
244
+ ua = headers.get(b"user-agent", b"").decode("utf-8", "ignore")
245
+ cf_country = headers.get(b"cf-ipcountry", headers.get(b"x-vercel-ip-country", b"")).decode("utf-8", "ignore")
246
+ content_length = int(headers.get(b"content-length", b"0").decode("utf-8", "ignore") or 0)
247
+
248
+ waf_cfg = getattr(self.seal, 'waf_config', {})
249
+
250
+ # Geo-Blocking
251
+ if waf_cfg.get("geoBlocking", {}).get("blockedCountries") and cf_country in waf_cfg["geoBlocking"]["blockedCountries"]:
252
+ self.seal._report_threat("GEO_BLOCKED", client_ip, scope, {"country": cf_country})
253
+ if waf_cfg["geoBlocking"].get("action") == "drop":
254
+ return await self._send_rejection(send, 403, "Access Denied from your Region")
255
+
256
+ # Method Tampering
257
+ if waf_cfg.get("methodTampering", {}).get("action") == "drop" and method not in self.ALLOWED_METHODS:
258
+ self.seal._report_threat("METHOD_TAMPERING", client_ip, scope, {"method": method.decode("utf-8", "ignore")})
259
+ return await self._send_rejection(send, 405, "Method Not Allowed")
260
+
261
+ # Malicious Scanners
262
+ if waf_cfg.get("maliciousScanners", {}).get("action") == "drop" and self.SCANNER_REGEX.search(ua):
263
+ self.seal._report_threat("MALICIOUS_SCANNER", client_ip, scope, {"user_agent": ua})
264
+ return await self._send_rejection(send, 403, "Forbidden Scanner")
265
+
266
+ # Payload Overflow
267
+ max_payload = waf_cfg.get("payloadOverflow", {}).get("maxPayloadSize", 5242880)
268
+ if waf_cfg.get("payloadOverflow", {}).get("action") == "drop" and content_length > max_payload:
269
+ self.seal._report_threat("PAYLOAD_OVERFLOW", client_ip, scope, {"content_length": content_length})
270
+ return await self._send_rejection(send, 413, "Payload Too Large")
271
+
272
+ # Path Traversal
273
+ if waf_cfg.get("pathTraversal", {}).get("action") == "drop" and self.TRAVERSAL_REGEX.search(full_url):
274
+ self.seal._report_threat("PATH_TRAVERSAL", client_ip, scope)
275
+ return await self._send_rejection(send, 403, "Forbidden Path")
276
+
277
+ # 1. Honeypot check
278
+ if path in self.HONEYPOTS:
279
+ self.seal._report_threat("HONEYPOT_ACCESS", client_ip, scope)
280
+
281
+ # 2. WAF Legacy URL Check
282
+ is_threat = False
283
+ threat_type = ""
284
+ if self.SQLI_REGEX.search(full_url):
285
+ is_threat, threat_type = True, "SQL_INJECTION"
286
+ elif self.XSS_REGEX.search(full_url):
287
+ is_threat, threat_type = True, "XSS_ATTACK"
288
+
289
+ if is_threat:
290
+ self.seal._report_threat(threat_type, client_ip, scope)
291
+
292
+ # 3. 401/403 Sliding Window Tracker
293
+ async def send_wrapper(message):
294
+ if message["type"] == "http.response.start":
295
+ status = message.get("status")
296
+ if status in (401, 403):
297
+ now = time.time()
298
+ hits = self.auth_failures.get(client_ip, [])
299
+ hits.append(now)
300
+ recent_hits = [h for h in hits if h > now - 60]
301
+ self.auth_failures[client_ip] = recent_hits
302
+
303
+ if len(recent_hits) >= 10:
304
+ last_reported = self.auth_failures.get(f"reported_{client_ip}", 0)
305
+ if now - last_reported > 60:
306
+ self.seal._report_threat(
307
+ "BRUTE_FORCE_ATTACK",
308
+ client_ip,
309
+ scope,
310
+ {"status_code": status, "attempts": len(recent_hits)}
311
+ )
312
+ self.auth_failures[f"reported_{client_ip}"] = now
313
+ await send(message)
314
+
315
+ await self.app(scope, receive, send_wrapper)
316
+
317
+ # Global singleton
318
+ seal = SealClient()
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,25 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="desicon-seal",
8
+ version="1.0.2",
9
+ author="Seal Enterprise Security",
10
+ author_email="hello@circle-sure.com",
11
+ description="Zero-latency App-Layer WAF, Automated SRE Logging, and AI Rescue Engine.",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/Desicon-AI/seal-python",
15
+ packages=find_packages(),
16
+ classifiers=[
17
+ "Programming Language :: Python :: 3",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ ],
21
+ python_requires='>=3.7',
22
+ install_requires=[
23
+ "requests>=2.25.1",
24
+ ],
25
+ )