saferelay 3.5.3__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,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: saferelay
3
+ Version: 3.5.3
4
+ Summary: Zero-trust local DLP for AI-era workflows — 40+ threat patterns across 8+ countries
5
+ Home-page: https://saferelay.ai
6
+ Author: LogicGrid AI, LLC
7
+ Author-email: support@logicgrid.ai
8
+ License: Proprietary
9
+ Keywords: ai-safety api-keys cli dlp pii redaction security zero-trust saferelay
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Security
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ Provides-Extra: redis
19
+ Requires-Dist: redis>=4.0; extra == "redis"
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: provides-extra
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ # SafePaste Enterprise CLI
33
+
34
+ > Zero-trust DLP for Linux pipelines — 35+ threat patterns across 8+ countries.
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/saferelay-enterprise)](https://pypi.org/project/saferelay-enterprise/)
37
+ [![Docker Hub](https://img.shields.io/badge/Docker-logicgridai%2Fsaferelay-blue)](https://hub.docker.com/r/logicgridai/saferelay)
38
+ [![Chrome Web Store](https://img.shields.io/badge/Chrome-Web%20Store-blue)](https://saferelay.ai)
39
+
40
+ SafePaste redacts sensitive data in your Linux pipelines before it reaches AI tools, log aggregators, or external services.
41
+ BEFORE AFTER (SafePaste)
42
+ ───────────────────────────────── ─────────────────────────────────
43
+ OPENAI_API_KEY=sk-proj-abc...xyz
44
+ AWS_ACCESS_KEY_ID=AKIA1234ABCD
45
+ Authorization: Bearer eyJhb... Authorization: Bearer [BEARER_1]
46
+ Server: [IP_1] Server: [DEVSEC_1]
47
+ SSN: [US_SSN_1] SSN: [US_SSN_1]
48
+
49
+ ## Get a License
50
+
51
+ | Tier | Price | Get it |
52
+ |------|-------|--------|
53
+ | Free | $0 | [Chrome Web Store](https://chromewebstore.google.com/detail/saferelay-enterprise/odeoilooelkodahbbdokbollgahdcaag) |
54
+ | Pro | $7.99/mo or $59/yr | [saferelay.ai/#pricing](https://saferelay.ai/#pricing) |
55
+ | SafeRelay Suite | $99 one-time | [saferelay.ai/saferelay](https://saferelay.ai/saferelay) |
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ pip install saferelay-enterprise
61
+
62
+ # With Redis support (enterprise distributed vault)
63
+ pip install saferelay-enterprise[redis]
64
+ ```
65
+
66
+ Requires Python 3.9+. No external dependencies for the base install.
67
+
68
+ ## Usage
69
+
70
+ ```bash
71
+ # Free tier — mask IPs and API keys
72
+ cat /var/log/app.log | saferelay --mask
73
+
74
+ # Pro tier — full vault with unmask
75
+ docker logs my-app | saferelay --mask > clean.log
76
+ cat ai_response.txt | saferelay --unmask
77
+
78
+ # Activate Pro
79
+ saferelay --unlock "YOUR-LICENSE-KEY"
80
+
81
+ # Status
82
+ saferelay --status
83
+ ```
84
+
85
+ ## What gets redacted
86
+
87
+ | Pattern | Free | Pro |
88
+ |---------|------|-----|
89
+ | IPv4 addresses | ✓ | ✓ |
90
+ | API keys (OpenAI, Anthropic, AWS, GitHub, Slack, Gemini) | ✓ | ✓ |
91
+ | Bitcoin / Ethereum addresses | ✓ | ✓ |
92
+ | PEM private keys | ✓ | ✓ |
93
+ | .env file values | ✓ | ✓ |
94
+ | MAC addresses | — | ✓ |
95
+ | Credit cards (Luhn-validated) | — | ✓ |
96
+ | US SSN | — | ✓ |
97
+ | EU IBAN | — | ✓ |
98
+ | UK NINO | — | ✓ |
99
+ | Nigeria NIN / Bank / Phone | — | ✓ |
100
+ | Canada SIN | — | ✓ |
101
+ | India Aadhaar / PAN | — | ✓ |
102
+ | South Africa ID | — | ✓ |
103
+ | Australia TFN | — | ✓ |
104
+ | Brazil CPF | — | ✓ |
105
+ | Singapore NRIC | — | ✓ |
106
+ | Germany Tax ID | — | ✓ |
107
+ | Seed phrases (12/24 word) | — | ✓ |
108
+ | ETH private keys | — | ✓ |
109
+ | Custom NDA keywords | — | ✓ |
110
+
111
+ ## Docker
112
+
113
+ ```bash
114
+ docker pull logicgridai/saferelay:latest
115
+
116
+ cat /var/log/app.log | docker run --rm -i logicgridai/saferelay --mask
117
+ ```
118
+
119
+ ## Kubernetes sidecar
120
+
121
+ ```yaml
122
+ containers:
123
+ - name: saferelay
124
+ image: logicgridai/saferelay:latest
125
+ env:
126
+ - name: SAFEPASTE_LICENSE_KEY
127
+ valueFrom:
128
+ secretKeyRef:
129
+ name: saferelay-secret
130
+ key: license-key
131
+ ```
132
+
133
+ ## Pricing
134
+
135
+ | Tier | Price | Features |
136
+ |------|-------|---------|
137
+ | Free | $0 | IP + API key redaction |
138
+ | Pro | $7.99/month or $59/year | 35+ patterns, full vault |
139
+ | SafeRelay Suite | $99 one-time | SafePaste + SpeakPaste + Boomerang Snip |
140
+
141
+ → [Get a license at saferelay.ai](https://saferelay.ai)
142
+
143
+ ## Privacy
144
+
145
+ Clipboard and log content never leaves your machine. License activation sends only a hashed device fingerprint to `api.saferelay.ai` — no log data, ever.
146
+
147
+ Full privacy policy: [saferelay.ai/privacy](https://saferelay.ai/privacy)
148
+
149
+ **Built by [LogicGrid AI, LLC](https://logicgrid.ai)** — support@logicgrid.ai
@@ -0,0 +1,118 @@
1
+ # SafePaste Enterprise CLI
2
+
3
+ > Zero-trust DLP for Linux pipelines — 35+ threat patterns across 8+ countries.
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/saferelay-enterprise)](https://pypi.org/project/saferelay-enterprise/)
6
+ [![Docker Hub](https://img.shields.io/badge/Docker-logicgridai%2Fsaferelay-blue)](https://hub.docker.com/r/logicgridai/saferelay)
7
+ [![Chrome Web Store](https://img.shields.io/badge/Chrome-Web%20Store-blue)](https://saferelay.ai)
8
+
9
+ SafePaste redacts sensitive data in your Linux pipelines before it reaches AI tools, log aggregators, or external services.
10
+ BEFORE AFTER (SafePaste)
11
+ ───────────────────────────────── ─────────────────────────────────
12
+ OPENAI_API_KEY=sk-proj-abc...xyz
13
+ AWS_ACCESS_KEY_ID=AKIA1234ABCD
14
+ Authorization: Bearer eyJhb... Authorization: Bearer [BEARER_1]
15
+ Server: [IP_1] Server: [DEVSEC_1]
16
+ SSN: [US_SSN_1] SSN: [US_SSN_1]
17
+
18
+ ## Get a License
19
+
20
+ | Tier | Price | Get it |
21
+ |------|-------|--------|
22
+ | Free | $0 | [Chrome Web Store](https://chromewebstore.google.com/detail/saferelay-enterprise/odeoilooelkodahbbdokbollgahdcaag) |
23
+ | Pro | $7.99/mo or $59/yr | [saferelay.ai/#pricing](https://saferelay.ai/#pricing) |
24
+ | SafeRelay Suite | $99 one-time | [saferelay.ai/saferelay](https://saferelay.ai/saferelay) |
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ pip install saferelay-enterprise
30
+
31
+ # With Redis support (enterprise distributed vault)
32
+ pip install saferelay-enterprise[redis]
33
+ ```
34
+
35
+ Requires Python 3.9+. No external dependencies for the base install.
36
+
37
+ ## Usage
38
+
39
+ ```bash
40
+ # Free tier — mask IPs and API keys
41
+ cat /var/log/app.log | saferelay --mask
42
+
43
+ # Pro tier — full vault with unmask
44
+ docker logs my-app | saferelay --mask > clean.log
45
+ cat ai_response.txt | saferelay --unmask
46
+
47
+ # Activate Pro
48
+ saferelay --unlock "YOUR-LICENSE-KEY"
49
+
50
+ # Status
51
+ saferelay --status
52
+ ```
53
+
54
+ ## What gets redacted
55
+
56
+ | Pattern | Free | Pro |
57
+ |---------|------|-----|
58
+ | IPv4 addresses | ✓ | ✓ |
59
+ | API keys (OpenAI, Anthropic, AWS, GitHub, Slack, Gemini) | ✓ | ✓ |
60
+ | Bitcoin / Ethereum addresses | ✓ | ✓ |
61
+ | PEM private keys | ✓ | ✓ |
62
+ | .env file values | ✓ | ✓ |
63
+ | MAC addresses | — | ✓ |
64
+ | Credit cards (Luhn-validated) | — | ✓ |
65
+ | US SSN | — | ✓ |
66
+ | EU IBAN | — | ✓ |
67
+ | UK NINO | — | ✓ |
68
+ | Nigeria NIN / Bank / Phone | — | ✓ |
69
+ | Canada SIN | — | ✓ |
70
+ | India Aadhaar / PAN | — | ✓ |
71
+ | South Africa ID | — | ✓ |
72
+ | Australia TFN | — | ✓ |
73
+ | Brazil CPF | — | ✓ |
74
+ | Singapore NRIC | — | ✓ |
75
+ | Germany Tax ID | — | ✓ |
76
+ | Seed phrases (12/24 word) | — | ✓ |
77
+ | ETH private keys | — | ✓ |
78
+ | Custom NDA keywords | — | ✓ |
79
+
80
+ ## Docker
81
+
82
+ ```bash
83
+ docker pull logicgridai/saferelay:latest
84
+
85
+ cat /var/log/app.log | docker run --rm -i logicgridai/saferelay --mask
86
+ ```
87
+
88
+ ## Kubernetes sidecar
89
+
90
+ ```yaml
91
+ containers:
92
+ - name: saferelay
93
+ image: logicgridai/saferelay:latest
94
+ env:
95
+ - name: SAFEPASTE_LICENSE_KEY
96
+ valueFrom:
97
+ secretKeyRef:
98
+ name: saferelay-secret
99
+ key: license-key
100
+ ```
101
+
102
+ ## Pricing
103
+
104
+ | Tier | Price | Features |
105
+ |------|-------|---------|
106
+ | Free | $0 | IP + API key redaction |
107
+ | Pro | $7.99/month or $59/year | 35+ patterns, full vault |
108
+ | SafeRelay Suite | $99 one-time | SafePaste + SpeakPaste + Boomerang Snip |
109
+
110
+ → [Get a license at saferelay.ai](https://saferelay.ai)
111
+
112
+ ## Privacy
113
+
114
+ Clipboard and log content never leaves your machine. License activation sends only a hashed device fingerprint to `api.saferelay.ai` — no log data, ever.
115
+
116
+ Full privacy policy: [saferelay.ai/privacy](https://saferelay.ai/privacy)
117
+
118
+ **Built by [LogicGrid AI, LLC](https://logicgrid.ai)** — support@logicgrid.ai
@@ -0,0 +1,4 @@
1
+ """SafeRelay Enterprise CLI — Zero-trust DLP for Linux pipelines."""
2
+ from .__main__ import main, __version__
3
+ from .client import SafeRelayClient
4
+ __all__ = ["main", "__version__", "SafeRelayClient"]
@@ -0,0 +1,418 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ saferelay — SafeRelay Enterprise CLI
4
+ Zero-trust local DLP for AI-era workflows.
5
+ 35+ threat patterns across 8+ countries.
6
+ """
7
+ __version__ = "3.5.3"
8
+ from __future__ import annotations
9
+ from __future__ import annotations
10
+ import argparse
11
+ import json
12
+ import os
13
+ import re
14
+ import sys
15
+ import urllib.error
16
+ import urllib.parse
17
+ import urllib.request
18
+ from pathlib import Path
19
+ from typing import Callable
20
+ try:
21
+ import redis # type: ignore[import-untyped]
22
+ HAS_REDIS = True
23
+ except ImportError:
24
+ HAS_REDIS = False
25
+
26
+ VAULT_PATH = Path.home() / ".safepaste" / "vault.json"
27
+ CONFIG_PATH = Path.home() / ".safepaste" / "config.json"
28
+ REDIS_ENV_VAR = "SAFEPASTE_REDIS_URL"
29
+ REDIS_HASH_KEY = "safepaste:vault"
30
+
31
+ # ── License validation (SafePaste Worker) ───────────────────────────────────
32
+ LICENSE_VERIFY_URL = "https://logicgrid-commerce-worker.admin-thequanthub.workers.dev/license/validate"
33
+
34
+ DEFAULT_CONFIG: dict = {
35
+ "is_licensed": False,
36
+ "license_key": "",
37
+ "license_tier": "",
38
+ "devsec_mode": True,
39
+ "fintech_shield": True,
40
+ "corporate_shield": True,
41
+ "web3_shield": True,
42
+ "civic_shield": True,
43
+ "custom_keywords": [],
44
+ }
45
+
46
+ # ── Helpers ─────────────────────────────────────────────────────────────────
47
+ def _load_config() -> dict:
48
+ if CONFIG_PATH.exists():
49
+ try:
50
+ return {**DEFAULT_CONFIG, **json.loads(CONFIG_PATH.read_text())}
51
+ except Exception:
52
+ pass
53
+ return dict(DEFAULT_CONFIG)
54
+
55
+ def _save_config(cfg: dict) -> None:
56
+ CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
57
+ CONFIG_PATH.write_text(json.dumps(cfg, indent=2))
58
+
59
+ def _get_device_id() -> str:
60
+ import platform, hashlib
61
+ raw = "|".join([platform.node(), platform.machine(), platform.system()])
62
+ return hashlib.sha256(raw.encode()).hexdigest()[:32]
63
+
64
+ # ── License verification ─────────────────────────────────────────────────────
65
+ def verify_license(license_key: str, timeout: float = 30.0) -> tuple[bool, str]:
66
+ """POST to SafePaste worker; returns (valid, tier)."""
67
+ key = license_key.strip()
68
+ if not key:
69
+ return False, ""
70
+ device_id = _get_device_id()
71
+ payload = json.dumps({"license_key": key, "device_id": device_id}).encode()
72
+ req = urllib.request.Request(
73
+ LICENSE_VERIFY_URL,
74
+ data=payload,
75
+ headers={"Content-Type": "application/json", "User-Agent": f"safepaste-cli/{__version__}"},
76
+ method="POST",
77
+ )
78
+ try:
79
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
80
+ data = json.loads(resp.read().decode())
81
+ if data.get("success") is True or data.get("valid") is True:
82
+ tier = str(data.get("tier", "pro")).lower().strip()
83
+ return True, tier
84
+ return False, ""
85
+ except Exception as exc:
86
+ print(f"safepaste: license check failed — {exc}", file=sys.stderr)
87
+ return False, ""
88
+
89
+ # ── Vault ────────────────────────────────────────────────────────────────────
90
+ def _get_redis() -> "redis.Redis | None":
91
+ url = os.environ.get(REDIS_ENV_VAR, "")
92
+ if not url or not HAS_REDIS:
93
+ return None
94
+ try:
95
+ r = redis.Redis.from_url(url, socket_timeout=2)
96
+ r.ping()
97
+ return r
98
+ except Exception:
99
+ return None
100
+
101
+ def _load_vault() -> dict:
102
+ r = _get_redis()
103
+ if r:
104
+ raw = r.hgetall(REDIS_HASH_KEY)
105
+ return {k.decode(): v.decode() for k, v in raw.items()} if raw else {}
106
+ if VAULT_PATH.exists():
107
+ try:
108
+ return json.loads(VAULT_PATH.read_text())
109
+ except Exception:
110
+ pass
111
+ return {}
112
+
113
+ def _save_vault(vault: dict) -> None:
114
+ r = _get_redis()
115
+ if r:
116
+ if vault:
117
+ r.hset(REDIS_HASH_KEY, mapping=vault)
118
+ else:
119
+ r.delete(REDIS_HASH_KEY)
120
+ return
121
+ VAULT_PATH.parent.mkdir(parents=True, exist_ok=True)
122
+ VAULT_PATH.write_text(json.dumps(vault, indent=2))
123
+
124
+ # ── Patterns (41 total) ──────────────────────────────────────────────────────
125
+
126
+ # DevSec Shield
127
+ RE_IPV4 = re.compile(r"\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b")
128
+ RE_API_KEY = re.compile(r"\b(?:sk|pk|rk|key|token|secret|api[-_]?key)[-_]?[a-zA-Z0-9]{16,64}\b", re.IGNORECASE)
129
+ RE_AWS_KEY = re.compile(r"\bAKIA[0-9A-Z]{16}\b")
130
+ RE_GITHUB_TOKEN = re.compile(r"\bgh[pousr]_[A-Za-z0-9_]{36,255}\b")
131
+ RE_SLACK_TOKEN = re.compile(r"\bxox[bpaso]-[0-9A-Za-z\-]{10,72}\b")
132
+ RE_OPENAI_KEY = re.compile(r"\bsk-[A-Za-z0-9]{32,64}\b")
133
+ RE_BEARER = re.compile(r"(?i)\bBearer\s+[A-Za-z0-9\-._~+/]{20,512}\b")
134
+ RE_MAC_ADDR = re.compile(r"\b(?:[0-9A-Fa-f]{2}[:\-]){5}[0-9A-Fa-f]{2}\b")
135
+ RE_ENV_VALUE = re.compile(r"(?m)^[A-Z][A-Z0-9_]{2,}=\S{8,}$")
136
+ RE_INTERNAL_URL = re.compile(r"\bhttps?://(?:localhost|127\.0\.0\.1|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[01])\.\d+\.\d+)[^\s]*\b")
137
+
138
+ # FinTech Shield
139
+ RE_US_SSN = re.compile(r"\b(?!000|666|9\d{2})\d{3}[- ]?(?!00)\d{2}[- ]?(?!0000)\d{4}\b")
140
+ RE_EU_IBAN = re.compile(r"\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b")
141
+ RE_SWIFT_BIC = re.compile(r"\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\b")
142
+ RE_CREDIT_CARD = re.compile(r"\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12})\b")
143
+ RE_UK_NINO = re.compile(r"\b[A-CEGHJ-PR-TW-Z]{2}\d{6}[A-D]\b", re.IGNORECASE)
144
+ RE_CA_SIN = re.compile(r"\b\d{3}[- ]?\d{3}[- ]?\d{3}\b")
145
+ RE_IN_AADHAAR = re.compile(r"\b[2-9]\d{3}[- ]?\d{4}[- ]?\d{4}\b")
146
+ RE_IN_PAN = re.compile(r"\b[A-Z]{5}\d{4}[A-Z]\b")
147
+ RE_NG_NIN = re.compile(r"\b\d{11}\b")
148
+ RE_ZA_ID = re.compile(r"\b\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{4}[01]\d{2}\b")
149
+ RE_AU_TFN = re.compile(r"\b\d{3}[- ]?\d{3}[- ]?\d{3}\b")
150
+ RE_BR_CPF = re.compile(r"\b\d{3}\.?\d{3}\.?\d{3}-?\d{2}\b")
151
+ RE_SG_NRIC = re.compile(r"\b[STFG]\d{7}[A-Z]\b", re.IGNORECASE)
152
+ RE_PASSPORT = re.compile(r"\b[A-Z]{1,2}[0-9]{6,9}\b")
153
+
154
+ # Corporate Identity Shield
155
+ RE_DUNS = re.compile(r"\b\d{2}-\d{3}-\d{4}\b|\b\d{9}\b")
156
+ RE_EIN = re.compile(r"\b\d{2}-\d{7}\b")
157
+ RE_VAT_EU = re.compile(r"\b(ATU\d{8}|BE0\d{9}|DE\d{9}|FR[A-Z0-9]{2}\d{9}|GB\d{9}|IT\d{11}|NL\d{9}B\d{2}|ES[A-Z0-9]\d{7}[A-Z0-9]|PL\d{10}|SE\d{12})\b")
158
+ RE_VENDOR_ID = re.compile(r"(?i)\b(?:VEND|PO|TENDER|CONTRACT|RFQ|INV)-[A-Z0-9]{4,20}\b")
159
+ RE_INVOICE = re.compile(r"(?i)\b(?:Invoice|INV|PO)[- ]?(?:No|#|Number)?[- ]?([A-Z]{0,4}\d{4,12})\b")
160
+
161
+ # Web3 Shield
162
+ RE_ETH_WALLET = re.compile(r"\b0x[a-fA-F0-9]{40}\b")
163
+ RE_BTC_WIF = re.compile(r"\b[5KL][1-9A-HJ-NP-Za-km-z]{50,51}\b")
164
+ RE_SOL_ADDR = re.compile(r"\b[1-9A-HJ-NP-Za-km-z]{32,44}\b")
165
+ RE_SEED_PHRASE = re.compile(r"\b(?:[a-z]{3,8}\s+){11}[a-z]{3,8}\b|\b(?:[a-z]{3,8}\s+){23}[a-z]{3,8}\b")
166
+ RE_GEMINI_KEY = re.compile(r"\baccount-[A-Za-z0-9]{20,40}\b")
167
+
168
+ # Mission & Civic Shield
169
+ RE_GPS_COORDS = re.compile(r"\b-?[0-9]{1,3}\.[0-9]{4,}\s*,\s*-?[0-9]{1,3}\.[0-9]{4,}\b")
170
+ RE_UNHCR_ID = re.compile(r"\b[A-Z]{3}-\d{2}-\d{6,8}C?\d?\b")
171
+ RE_BENEFICIARY = re.compile(r"(?i)\b(?:BEN|DONOR|CASE|HH|PROG)-[A-Z0-9]{4,16}\b")
172
+
173
+ # Context-aware validators
174
+ _DUNS_CTX = re.compile(r"(?i)(DUNS|D-U-N-S|D&B|Dun\s*&\s*Bradstreet|business\s*credit)")
175
+ _EIN_CTX = re.compile(r"(?i)(EIN|Tax\s*ID|TIN|employer\s*identification|federal\s*tax)")
176
+
177
+ def _duns_validator(m: re.Match, text: str) -> bool:
178
+ start = max(0, m.start() - 80)
179
+ end = min(len(text), m.end() + 80)
180
+ return bool(_DUNS_CTX.search(text[start:end]))
181
+
182
+ def _ein_validator(m: re.Match, text: str) -> bool:
183
+ start = max(0, m.start() - 50)
184
+ end = min(len(text), m.end() + 50)
185
+ return bool(_EIN_CTX.search(text[start:end]))
186
+
187
+ # ── Threat table ─────────────────────────────────────────────────────────────
188
+ # (priority, label, pattern, shield, validator)
189
+ # shield: "free" | "devsec" | "fintech" | "corporate" | "web3" | "civic"
190
+ ThreatRow = tuple[int, str, re.Pattern, str, Callable]
191
+
192
+ def _build_threats(cfg: dict, is_pro: bool) -> list[ThreatRow]:
193
+ devsec = is_pro and cfg.get("devsec_mode", True)
194
+ fintech = is_pro and cfg.get("fintech_shield", True)
195
+ corporate = is_pro and cfg.get("corporate_shield", True)
196
+ web3 = is_pro and cfg.get("web3_shield", True)
197
+ civic = is_pro and cfg.get("civic_shield", True)
198
+
199
+ rows: list[ThreatRow] = [
200
+ # Free tier
201
+ (10, "IPV4", RE_IPV4, "free", lambda m, t: True),
202
+ (11, "DEVSEC", RE_API_KEY, "free", lambda m, t: True),
203
+ (12, "DEVSEC", RE_AWS_KEY, "free", lambda m, t: True),
204
+ (13, "DEVSEC", RE_OPENAI_KEY, "free", lambda m, t: True),
205
+ ]
206
+ if devsec:
207
+ rows += [
208
+ (20, "DEVSEC", RE_GITHUB_TOKEN, "devsec", lambda m, t: True),
209
+ (21, "DEVSEC", RE_SLACK_TOKEN, "devsec", lambda m, t: True),
210
+ (22, "DEVSEC", RE_BEARER, "devsec", lambda m, t: True),
211
+ (23, "MAC_ADDR", RE_MAC_ADDR, "devsec", lambda m, t: True),
212
+ (24, "ENV_VALUE", RE_ENV_VALUE, "devsec", lambda m, t: True),
213
+ (25, "INT_URL", RE_INTERNAL_URL, "devsec", lambda m, t: True),
214
+ ]
215
+ if fintech:
216
+ rows += [
217
+ (30, "US_SSN", RE_US_SSN, "fintech", lambda m, t: True),
218
+ (31, "EU_IBAN", RE_EU_IBAN, "fintech", lambda m, t: True),
219
+ (32, "SWIFT_BIC", RE_SWIFT_BIC, "fintech", lambda m, t: True),
220
+ (33, "CC", RE_CREDIT_CARD,"fintech", lambda m, t: True),
221
+ (34, "UK_NINO", RE_UK_NINO, "fintech", lambda m, t: True),
222
+ (35, "CA_SIN", RE_CA_SIN, "fintech", lambda m, t: True),
223
+ (36, "IN_AADHAAR",RE_IN_AADHAAR, "fintech", lambda m, t: True),
224
+ (37, "IN_PAN", RE_IN_PAN, "fintech", lambda m, t: True),
225
+ (38, "NG_NIN", RE_NG_NIN, "fintech", lambda m, t: True),
226
+ (39, "ZA_ID", RE_ZA_ID, "fintech", lambda m, t: True),
227
+ (40, "AU_TFN", RE_AU_TFN, "fintech", lambda m, t: True),
228
+ (41, "BR_CPF", RE_BR_CPF, "fintech", lambda m, t: True),
229
+ (42, "SG_NRIC", RE_SG_NRIC, "fintech", lambda m, t: True),
230
+ (43, "PASSPORT", RE_PASSPORT, "fintech", lambda m, t: True),
231
+ ]
232
+ if corporate:
233
+ rows += [
234
+ (50, "DUNS", RE_DUNS, "corporate", _duns_validator),
235
+ (51, "EIN", RE_EIN, "corporate", _ein_validator),
236
+ (52, "VAT_EU", RE_VAT_EU, "corporate", lambda m, t: True),
237
+ (53, "VENDOR_ID", RE_VENDOR_ID, "corporate", lambda m, t: True),
238
+ (54, "INVOICE", RE_INVOICE, "corporate", lambda m, t: True),
239
+ ]
240
+ if web3:
241
+ rows += [
242
+ (60, "ETH_WALLET", RE_ETH_WALLET, "web3", lambda m, t: True),
243
+ (61, "BTC_WIF", RE_BTC_WIF, "web3", lambda m, t: True),
244
+ (62, "SOL_ADDR", RE_SOL_ADDR, "web3", lambda m, t: True),
245
+ (63, "SEED_PHRASE", RE_SEED_PHRASE, "web3", lambda m, t: True),
246
+ (64, "GEMINI_KEY", RE_GEMINI_KEY, "web3", lambda m, t: True),
247
+ ]
248
+ if civic:
249
+ rows += [
250
+ (70, "GPS_COORDS", RE_GPS_COORDS, "civic", lambda m, t: True),
251
+ (71, "UNHCR_ID", RE_UNHCR_ID, "civic", lambda m, t: True),
252
+ (72, "BENEFICIARY", RE_BENEFICIARY, "civic", lambda m, t: True),
253
+ ]
254
+ return sorted(rows, key=lambda r: r[0])
255
+
256
+ # ── Redaction engine ─────────────────────────────────────────────────────────
257
+ def redact(text: str, threats: list[ThreatRow], vault: dict,
258
+ custom_keywords: list[str]) -> tuple[str, dict]:
259
+ counters: dict[str, int] = {}
260
+ result = text
261
+
262
+ # Custom NDA keywords first
263
+ for kw in custom_keywords:
264
+ escaped = re.escape(kw)
265
+ pat = re.compile(escaped, re.IGNORECASE)
266
+ for m in reversed(list(pat.finditer(result))):
267
+ counters["NDA"] = counters.get("NDA", 0) + 1
268
+ ph = f"[NDA_{counters['NDA']}]"
269
+ vault[ph] = m.group(0)
270
+ result = result[:m.start()] + ph + result[m.end():]
271
+
272
+ # Shield patterns
273
+ matches: list[tuple[int, int, str, str]] = []
274
+ for _, label, pattern, shield, validator in threats:
275
+ for m in pattern.finditer(result):
276
+ if validator(m, result):
277
+ matches.append((m.start(), m.end(), label, m.group(0)))
278
+
279
+ # Sort by position descending to replace without offset issues
280
+ matches.sort(key=lambda x: x[0], reverse=True)
281
+ seen_spans: set[tuple[int,int]] = set()
282
+ for start, end, label, value in matches:
283
+ if any(s <= start < e or s < end <= e for s, e in seen_spans):
284
+ continue
285
+ seen_spans.add((start, end))
286
+ counters[label] = counters.get(label, 0) + 1
287
+ ph = f"[{label}_{counters[label]}]"
288
+ vault[ph] = value
289
+ result = result[:start] + ph + result[end:]
290
+
291
+ return result, vault
292
+
293
+ # ── Main ──────────────────────────────────────────────────────────────────────
294
+ def main() -> None:
295
+ cfg = _load_config()
296
+ is_pro = bool(cfg.get("is_licensed"))
297
+
298
+ p = argparse.ArgumentParser(
299
+ prog="safepaste",
300
+ description=(
301
+ f"SafePaste Enterprise CLI v{__version__} — "
302
+ "41 global PII patterns across 12+ countries. "
303
+ "Zero-trust DLP for AI-assisted workflows. "
304
+ "Free: IP + API key redaction. Pro: all Shield Packs + vault."
305
+ ),
306
+ )
307
+ p.add_argument("input", nargs="?", type=argparse.FileType("r"), default=sys.stdin,
308
+ help="Input file (default: stdin).")
309
+ p.add_argument("--unmask", metavar="PLACEHOLDER",
310
+ help="Reveal a vaulted placeholder, e.g. [US_SSN_1].")
311
+ p.add_argument("--unlock", metavar="LICENSE_KEY",
312
+ help="Verify license key and enable Pro Shield Packs.")
313
+ p.add_argument("--status", action="store_true",
314
+ help="Show current license tier and active shields.")
315
+ p.add_argument("--clear-vault", action="store_true",
316
+ help="Wipe all vaulted values.")
317
+ p.add_argument("--pro", action="store_true",
318
+ help="Force Pro-tier for this run (no license call).")
319
+ p.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
320
+
321
+ # Shield toggles
322
+ p.add_argument("--no-devsec", action="store_true", help="Disable DevSec Shield.")
323
+ p.add_argument("--no-fintech", action="store_true", help="Disable FinTech Shield.")
324
+ p.add_argument("--no-corporate", action="store_true", help="Disable Corporate Identity Shield.")
325
+ p.add_argument("--no-web3", action="store_true", help="Disable Web3 Shield.")
326
+ p.add_argument("--no-civic", action="store_true", help="Disable Mission & Civic Shield.")
327
+ p.add_argument("--mask", action="store_true", help="Alias for default redact mode.")
328
+
329
+ args = p.parse_args()
330
+
331
+ # ── --unlock ──────────────────────────────────────────────────────────────
332
+ if args.unlock:
333
+ key = args.unlock.strip() or os.environ.get("SAFEPASTE_LICENSE_KEY", "").strip()
334
+ if not key:
335
+ print("safepaste: pass LICENSE_KEY or set SAFEPASTE_LICENSE_KEY", file=sys.stderr)
336
+ sys.exit(1)
337
+ print(f"safepaste: verifying license…", file=sys.stderr)
338
+ ok, tier = verify_license(key)
339
+ if ok:
340
+ cfg["is_licensed"] = True
341
+ cfg["license_key"] = key
342
+ cfg["license_tier"] = tier
343
+ _save_config(cfg)
344
+ print(f"safepaste: ✓ License verified — tier={tier}. Pro Shield Packs enabled.")
345
+ else:
346
+ print("safepaste: ✗ Invalid license key. Check your key and try again.", file=sys.stderr)
347
+ sys.exit(1)
348
+ return
349
+
350
+ # ── --status ──────────────────────────────────────────────────────────────
351
+ if args.status:
352
+ on, off = "✓ on", "✗ off"
353
+ tier = cfg.get("license_tier", "free") if is_pro else "free"
354
+ print(f"safepaste v{__version__}")
355
+ print(f" License : {'Pro — ' + tier if is_pro else 'Free'}")
356
+ print(f" DevSec : {on if cfg.get('devsec_mode') else off}")
357
+ print(f" FinTech : {on if is_pro and cfg.get('fintech_shield') else off}")
358
+ print(f" Corporate : {on if is_pro and cfg.get('corporate_shield') else off}")
359
+ print(f" Web3 : {on if is_pro and cfg.get('web3_shield') else off}")
360
+ print(f" Civic : {on if is_pro and cfg.get('civic_shield') else off}")
361
+ kws = cfg.get("custom_keywords", [])
362
+ print(f" NDA terms : {len(kws)} ({', '.join(kws[:3])}{'…' if len(kws) > 3 else ''})")
363
+ return
364
+
365
+ # ── --clear-vault ─────────────────────────────────────────────────────────
366
+ if args.clear_vault:
367
+ _save_vault({})
368
+ print("safepaste: vault cleared.")
369
+ return
370
+
371
+ # ── --unmask ──────────────────────────────────────────────────────────────
372
+ if args.unmask:
373
+ vault = _load_vault()
374
+ ph = args.unmask.strip()
375
+ if not ph.startswith("["):
376
+ ph = f"[{ph}]"
377
+ val = vault.get(ph)
378
+ if val:
379
+ print(val)
380
+ else:
381
+ print(f"safepaste: {ph} not found in vault.", file=sys.stderr)
382
+ sys.exit(1)
383
+ return
384
+
385
+ # ── Apply CLI overrides to config ─────────────────────────────────────────
386
+ if args.pro:
387
+ is_pro = True
388
+ if args.no_devsec:
389
+ cfg["devsec_mode"] = False
390
+ if args.no_fintech:
391
+ cfg["fintech_shield"] = False
392
+ if args.no_corporate:
393
+ cfg["corporate_shield"] = False
394
+ if args.no_web3:
395
+ cfg["web3_shield"] = False
396
+ if args.no_civic:
397
+ cfg["civic_shield"] = False
398
+
399
+ # ── Read input ────────────────────────────────────────────────────────────
400
+ try:
401
+ text = args.input.read()
402
+ except KeyboardInterrupt:
403
+ sys.exit(0)
404
+
405
+ # ── Build threats & redact ────────────────────────────────────────────────
406
+ threats = _build_threats(cfg, is_pro)
407
+ vault = _load_vault() if is_pro else {}
408
+ custom = cfg.get("custom_keywords", []) if is_pro else []
409
+
410
+ redacted, vault = redact(text, threats, vault, custom)
411
+
412
+ if is_pro:
413
+ _save_vault(vault)
414
+
415
+ print(redacted, end="")
416
+
417
+ if __name__ == "__main__":
418
+ main()
@@ -0,0 +1,140 @@
1
+ """
2
+ SafeRelay Python SDK — SafeRelayClient
3
+ pip install saferelay-enterprise
4
+
5
+ Usage:
6
+ from saferelay import SafeRelayClient
7
+ client = SafeRelayClient(mode="zero-trust")
8
+ safe = client.redact("token AKIAIOSFODNN7EXAMPLE at 10.1.2.3")
9
+ print(safe) # "token 🔒[AWS_KEY_1] at 🔒[INTERNAL_IP_1]"
10
+ print(client.last_detections) # {"AWS_KEY": 1, "INTERNAL_IP": 1}
11
+ """
12
+
13
+ from __future__ import annotations
14
+ import re
15
+ from typing import Literal
16
+
17
+ # ---------------------------------------------------------------------------
18
+ # Pattern definitions — mirrors patterns.js and boomerang_snip.py
19
+ # ---------------------------------------------------------------------------
20
+
21
+ _PATTERNS: list[tuple[str, re.Pattern, callable | None]] = [
22
+ # ── Network ────────────────────────────────────────────────────────────
23
+ ("INTERNAL_IP", re.compile(
24
+ r'\b(?!127\.|255\.|0\.0\.0\.0)(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\b'
25
+ ), lambda m: all(0 <= int(x) <= 255 for x in m.split('.'))),
26
+
27
+ # ── AWS ────────────────────────────────────────────────────────────────
28
+ ("AWS_KEY", re.compile(r'\bAKIA[0-9A-Z]{16}\b'), None),
29
+ ("AWS_KEY", re.compile(r'\bASIA[0-9A-Z]{16}\b'), None),
30
+ ("AWS_SECRET", re.compile(
31
+ r'(?:AWS_SECRET_ACCESS_KEY|aws_secret_access_key|SecretAccessKey)'
32
+ r'\s*[=:"\'\s]+["\']?([A-Za-z0-9/+=]{40})["\']?'
33
+ ), None),
34
+ ("AWS_SECRET", re.compile(
35
+ r'(?<![A-Za-z0-9/+])[A-Za-z0-9/+=]{40}(?![A-Za-z0-9/+=])'
36
+ ), lambda m: not re.fullmatch(r'[0-9a-fA-F]{40}', m)),
37
+
38
+ # ── API Keys ───────────────────────────────────────────────────────────
39
+ ("OPENAI_KEY", re.compile(r'\bsk-proj-[a-zA-Z0-9_-]{20,}\b', re.I), None),
40
+ ("OPENAI_KEY", re.compile(r'\bsk-(?!proj-|ant-)[a-zA-Z0-9_-]{16,}\b', re.I), None),
41
+ ("ANTHROPIC_KEY", re.compile(r'\bsk-ant-[a-zA-Z0-9_-]{10,}\b', re.I), None),
42
+ ("GITHUB_TOKEN", re.compile(r'\bghp_[a-zA-Z0-9]{20,}\b'), None),
43
+ ("GITHUB_TOKEN", re.compile(r'\bgithub_pat_[a-zA-Z0-9_]{20,}\b'), None),
44
+ ("GITHUB_TOKEN", re.compile(r'\bgh[osux]_[a-zA-Z0-9]{20,}\b'), None),
45
+ ("SLACK_TOKEN", re.compile(r'\bxox[bap]-[a-zA-Z0-9-]{10,}\b', re.I), None),
46
+ ("SLACK_WEBHOOK", re.compile(
47
+ r'https://hooks\.slack\.com/services/[A-Z0-9]+/[A-Z0-9]+/[A-Za-z0-9]+'
48
+ ), None),
49
+ ("STRIPE_KEY", re.compile(r'\bsk_(?:live|test)_[a-zA-Z0-9]{24,}\b'), None),
50
+ ("DOCKER_TOKEN", re.compile(r'\bdckr_pat_[a-zA-Z0-9_-]{20,}\b'), None),
51
+ ("NPM_TOKEN", re.compile(r'\bnpm_[a-zA-Z0-9]{36,}\b'), None),
52
+ ("TWILIO_KEY", re.compile(r'\bSK[a-f0-9]{32}\b'), None),
53
+ ("SENDGRID_KEY", re.compile(r'\bSG\.[a-zA-Z0-9_-]{22,}\.[a-zA-Z0-9_-]{43,}\b'), None),
54
+ ("GEMINI_KEY", re.compile(r'\bAIza[0-9A-Za-z\-_]{35}\b'), None),
55
+
56
+ # ── Google OAuth ──────────────────────────────────────────────────────
57
+ ("GOOGLE_OAUTH", re.compile(r'\b\d{6,}-[a-z0-9]+\.apps\.googleusercontent\.com\b', re.I), None),
58
+ ("GOOGLE_OAUTH", re.compile(r'\bGOCSPX-[A-Za-z0-9_-]{20,}\b'), None),
59
+ ("GOOGLE_OAUTH", re.compile(r'\b1//[A-Za-z0-9_\-]{10,}\b'), None),
60
+
61
+ # ── Crypto ─────────────────────────────────────────────────────────────
62
+ ("BTC_ADDRESS", re.compile(r'\b1[a-km-zA-HJ-NP-Z1-9]{25,34}\b'), None),
63
+ ("BTC_ADDRESS", re.compile(r'\b3[a-km-zA-HJ-NP-Z1-9]{25,34}\b'), None),
64
+ ("BTC_ADDRESS", re.compile(r'\bbc1[a-z0-9]{6,87}\b', re.I), None),
65
+ ("ETH_ADDRESS", re.compile(r'\b0x[a-fA-F0-9]{40}\b'), None),
66
+ ("SEED_PHRASE", re.compile(r'\b([a-z]+\s){11}[a-z]+\b'), lambda m: len(m.strip().split()) == 12),
67
+ ("SEED_PHRASE", re.compile(r'\b([a-z]+\s){23}[a-z]+\b'), lambda m: len(m.strip().split()) == 24),
68
+ ("PEM_KEY", re.compile(r'-----BEGIN\s(?:RSA\s|EC\s|OPENSSH\s)?PRIVATE KEY-----'), None),
69
+
70
+ # ── PII ────────────────────────────────────────────────────────────────
71
+ ("US_SSN", re.compile(r'\b(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b'), None),
72
+ ("CREDIT_CARD", re.compile(r'\b(?:\d{4}[-\s]?){3}\d{3,4}\b'), None),
73
+ ]
74
+
75
+
76
+ class SafeRelayClient:
77
+ """
78
+ Local, zero-trust DLP client for Python.
79
+
80
+ Modes:
81
+ zero-trust — redacts in place, nothing leaves the process (default)
82
+ audit — redacts and records detections for logging/reporting
83
+
84
+ Example:
85
+ client = SafeRelayClient(mode="zero-trust")
86
+ safe = client.redact(raw_log)
87
+ print(client.last_detections)
88
+ """
89
+
90
+ def __init__(self, mode: Literal["zero-trust", "audit"] = "zero-trust"):
91
+ self.mode = mode
92
+ self._detections: dict[str, int] = {}
93
+
94
+ def redact(self, text: str, emoji: bool = True) -> str:
95
+ """Redact all detected secrets and PII from *text*.
96
+
97
+ Args:
98
+ text: The raw string to scan.
99
+ emoji: If True (default) prefix tokens with 🔒. Set False for
100
+ plain tokens like [AWS_KEY_1] instead of 🔒[AWS_KEY_1].
101
+
102
+ Returns:
103
+ Sanitised string with secrets replaced by labelled tokens.
104
+ """
105
+ if not text:
106
+ return text
107
+
108
+ result = text
109
+ self._detections = {}
110
+ prefix = "🔒" if emoji else ""
111
+
112
+ for label, pattern, validator in _PATTERNS:
113
+ counter = self._detections.get(label, 0)
114
+
115
+ def _replace(m, l=label, v=validator, c=[counter]):
116
+ match_str = m.group(0)
117
+ if v and not v(match_str):
118
+ return match_str
119
+ c[0] += 1
120
+ self._detections[l] = c[0]
121
+ return f"{prefix}[{l}_{c[0]}]"
122
+
123
+ result = pattern.sub(_replace, result)
124
+
125
+ return result
126
+
127
+ def is_safe(self, text: str) -> bool:
128
+ """Return True if no secrets are detected in *text*."""
129
+ probe = self.redact(text, emoji=False)
130
+ return probe == text
131
+
132
+ @property
133
+ def last_detections(self) -> dict[str, int]:
134
+ """Dict of {PATTERN_TYPE: count} from the last redact() call."""
135
+ return dict(self._detections)
136
+
137
+ @property
138
+ def last_detection_count(self) -> int:
139
+ """Total number of secrets found in the last redact() call."""
140
+ return sum(self._detections.values())
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: saferelay
3
+ Version: 3.5.3
4
+ Summary: Zero-trust local DLP for AI-era workflows — 40+ threat patterns across 8+ countries
5
+ Home-page: https://saferelay.ai
6
+ Author: LogicGrid AI, LLC
7
+ Author-email: support@logicgrid.ai
8
+ License: Proprietary
9
+ Keywords: ai-safety api-keys cli dlp pii redaction security zero-trust saferelay
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Security
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ Provides-Extra: redis
19
+ Requires-Dist: redis>=4.0; extra == "redis"
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: provides-extra
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ # SafePaste Enterprise CLI
33
+
34
+ > Zero-trust DLP for Linux pipelines — 35+ threat patterns across 8+ countries.
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/saferelay-enterprise)](https://pypi.org/project/saferelay-enterprise/)
37
+ [![Docker Hub](https://img.shields.io/badge/Docker-logicgridai%2Fsaferelay-blue)](https://hub.docker.com/r/logicgridai/saferelay)
38
+ [![Chrome Web Store](https://img.shields.io/badge/Chrome-Web%20Store-blue)](https://saferelay.ai)
39
+
40
+ SafePaste redacts sensitive data in your Linux pipelines before it reaches AI tools, log aggregators, or external services.
41
+ BEFORE AFTER (SafePaste)
42
+ ───────────────────────────────── ─────────────────────────────────
43
+ OPENAI_API_KEY=sk-proj-abc...xyz
44
+ AWS_ACCESS_KEY_ID=AKIA1234ABCD
45
+ Authorization: Bearer eyJhb... Authorization: Bearer [BEARER_1]
46
+ Server: [IP_1] Server: [DEVSEC_1]
47
+ SSN: [US_SSN_1] SSN: [US_SSN_1]
48
+
49
+ ## Get a License
50
+
51
+ | Tier | Price | Get it |
52
+ |------|-------|--------|
53
+ | Free | $0 | [Chrome Web Store](https://chromewebstore.google.com/detail/saferelay-enterprise/odeoilooelkodahbbdokbollgahdcaag) |
54
+ | Pro | $7.99/mo or $59/yr | [saferelay.ai/#pricing](https://saferelay.ai/#pricing) |
55
+ | SafeRelay Suite | $99 one-time | [saferelay.ai/saferelay](https://saferelay.ai/saferelay) |
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ pip install saferelay-enterprise
61
+
62
+ # With Redis support (enterprise distributed vault)
63
+ pip install saferelay-enterprise[redis]
64
+ ```
65
+
66
+ Requires Python 3.9+. No external dependencies for the base install.
67
+
68
+ ## Usage
69
+
70
+ ```bash
71
+ # Free tier — mask IPs and API keys
72
+ cat /var/log/app.log | saferelay --mask
73
+
74
+ # Pro tier — full vault with unmask
75
+ docker logs my-app | saferelay --mask > clean.log
76
+ cat ai_response.txt | saferelay --unmask
77
+
78
+ # Activate Pro
79
+ saferelay --unlock "YOUR-LICENSE-KEY"
80
+
81
+ # Status
82
+ saferelay --status
83
+ ```
84
+
85
+ ## What gets redacted
86
+
87
+ | Pattern | Free | Pro |
88
+ |---------|------|-----|
89
+ | IPv4 addresses | ✓ | ✓ |
90
+ | API keys (OpenAI, Anthropic, AWS, GitHub, Slack, Gemini) | ✓ | ✓ |
91
+ | Bitcoin / Ethereum addresses | ✓ | ✓ |
92
+ | PEM private keys | ✓ | ✓ |
93
+ | .env file values | ✓ | ✓ |
94
+ | MAC addresses | — | ✓ |
95
+ | Credit cards (Luhn-validated) | — | ✓ |
96
+ | US SSN | — | ✓ |
97
+ | EU IBAN | — | ✓ |
98
+ | UK NINO | — | ✓ |
99
+ | Nigeria NIN / Bank / Phone | — | ✓ |
100
+ | Canada SIN | — | ✓ |
101
+ | India Aadhaar / PAN | — | ✓ |
102
+ | South Africa ID | — | ✓ |
103
+ | Australia TFN | — | ✓ |
104
+ | Brazil CPF | — | ✓ |
105
+ | Singapore NRIC | — | ✓ |
106
+ | Germany Tax ID | — | ✓ |
107
+ | Seed phrases (12/24 word) | — | ✓ |
108
+ | ETH private keys | — | ✓ |
109
+ | Custom NDA keywords | — | ✓ |
110
+
111
+ ## Docker
112
+
113
+ ```bash
114
+ docker pull logicgridai/saferelay:latest
115
+
116
+ cat /var/log/app.log | docker run --rm -i logicgridai/saferelay --mask
117
+ ```
118
+
119
+ ## Kubernetes sidecar
120
+
121
+ ```yaml
122
+ containers:
123
+ - name: saferelay
124
+ image: logicgridai/saferelay:latest
125
+ env:
126
+ - name: SAFEPASTE_LICENSE_KEY
127
+ valueFrom:
128
+ secretKeyRef:
129
+ name: saferelay-secret
130
+ key: license-key
131
+ ```
132
+
133
+ ## Pricing
134
+
135
+ | Tier | Price | Features |
136
+ |------|-------|---------|
137
+ | Free | $0 | IP + API key redaction |
138
+ | Pro | $7.99/month or $59/year | 35+ patterns, full vault |
139
+ | SafeRelay Suite | $99 one-time | SafePaste + SpeakPaste + Boomerang Snip |
140
+
141
+ → [Get a license at saferelay.ai](https://saferelay.ai)
142
+
143
+ ## Privacy
144
+
145
+ Clipboard and log content never leaves your machine. License activation sends only a hashed device fingerprint to `api.saferelay.ai` — no log data, ever.
146
+
147
+ Full privacy policy: [saferelay.ai/privacy](https://saferelay.ai/privacy)
148
+
149
+ **Built by [LogicGrid AI, LLC](https://logicgrid.ai)** — support@logicgrid.ai
@@ -0,0 +1,11 @@
1
+ README.md
2
+ setup.py
3
+ saferelay/__init__.py
4
+ saferelay/__main__.py
5
+ saferelay/client.py
6
+ saferelay.egg-info/PKG-INFO
7
+ saferelay.egg-info/SOURCES.txt
8
+ saferelay.egg-info/dependency_links.txt
9
+ saferelay.egg-info/entry_points.txt
10
+ saferelay.egg-info/requires.txt
11
+ saferelay.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ saferelay = safepaste.__main__:main
@@ -0,0 +1,3 @@
1
+
2
+ [redis]
3
+ redis>=4.0
@@ -0,0 +1 @@
1
+ saferelay
@@ -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
+ setup(
3
+ name="saferelay",
4
+ version="3.5.3",
5
+ description="Zero-trust local DLP for AI-era workflows — 40+ threat patterns across 8+ countries",
6
+ long_description=open("README.md", encoding="utf-8").read(),
7
+ long_description_content_type="text/markdown",
8
+ author="LogicGrid AI, LLC",
9
+ author_email="support@logicgrid.ai",
10
+ url="https://saferelay.ai",
11
+ packages=find_packages(),
12
+ python_requires=">=3.9",
13
+ extras_require={"redis": ["redis>=4.0"]},
14
+ entry_points={"console_scripts": ["saferelay=safepaste.__main__:main"]},
15
+ license="Proprietary",
16
+ keywords="ai-safety api-keys cli dlp pii redaction security zero-trust saferelay",
17
+ classifiers=[
18
+ "Development Status :: 5 - Production/Stable",
19
+ "Environment :: Console",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Topic :: Security",
24
+ ],
25
+ )