Concepts
This page is the single reference for the core building blocks of meshulash-guard: how the SDK works under the hood, what Actions and Conditions do, the full ScanResult structure, and the exceptions the SDK raises. Scanner reference pages link here so these concepts are defined once.
How It Works
The meshulash-guard SDK is a client library that sends your text to a Meshulash security server for analysis. The server runs NER models, regex patterns, text classifiers, and validators — all the heavy computation happens server-side. The SDK translates your scanner configuration into a format the server understands, sends it over HTTPS, and returns structured results.
This means:
- You need a running security server — Meshulash provides a hosted cloud server, or you can run the server on-premises.
- Scan calls involve a network round-trip — plan for roughly 100–500ms latency depending on text length and scanner count.
- An API key and tenant ID authenticate your requests — obtain these from the Meshulash dashboard.
- The deanonymize step is the one exception —
guard.deanonymize()runs entirely client-side with no server call. See the Deanonymize workflow for details.
Actions
An Action tells the scanner what to do when it detects matching content. You set the action when constructing a scanner.
| Action | What Happens | Result Status |
|---|---|---|
Action.REPLACE |
Detected spans are replaced with [LABEL-HASH] placeholders in processed_text. The original values are stored in result.placeholders for later restoration via deanonymize(). |
"secured" |
Action.BLOCK |
The original text is returned unchanged. No redaction occurs. The scan is marked as blocked. | "blocked" |
Action.LOG |
The original text is returned unchanged. Detection is recorded server-side for audit purposes. | "clean" |
from meshulash_guard import Guard, Action
from meshulash_guard.scanners import PIIScanner, PIILabel
guard = Guard(api_key="your-api-key", tenant_id="your-tenant-id")
# REPLACE: redact PII and continue processing
pii = PIIScanner(labels=[PIILabel.EMAIL_ADDRESS], action=Action.REPLACE)
result = guard.scan_input("Email me at alice@example.com", scanners=[pii])
print(result.status) # "secured"
print(result.processed_text) # "Email me at [EMAIL_ADDRESS-A1B2]"
# BLOCK: reject the text entirely
from meshulash_guard.scanners import ToxicityScanner, ToxicityLabel
toxicity = ToxicityScanner(labels=[ToxicityLabel.TOXICITY], action=Action.BLOCK)
result = guard.scan_input("You are completely useless", scanners=[toxicity])
print(result.status) # "blocked"
print(result.processed_text) # "You are completely useless"
Expected output:
Choosing an action:
- Use
Action.REPLACEwhen you want to continue processing but need PII removed (e.g., before sending to an LLM). - Use
Action.BLOCKwhen the content should be rejected entirely (e.g., toxic prompts, jailbreak attempts). - Use
Action.LOGwhen you want to track content without altering the flow (e.g., monitoring only).
Conditions
A Condition controls when the scanner fires its action. All conditions apply to the detections made by that scanner's labels.
| Condition | When It Triggers |
|---|---|
Condition.ANY |
At least one detection from any of the configured labels. This is the default and covers most use cases. |
Condition.ALL |
Every configured label must produce at least one detection. |
Condition.K_OF |
The total number of detections across all labels must reach the configured threshold k. |
Condition.CONTEXTUAL |
The text classifier (TC) head must trigger and at least one NER or regex detection must also be present. Useful for reducing false positives. |
The most common condition is Condition.ANY — it fires as soon as any single label is detected. You rarely need to set it explicitly because it is the default.
from meshulash_guard import Guard, Action, Condition
from meshulash_guard.scanners import PIIScanner, PIILabel
guard = Guard(api_key="your-api-key", tenant_id="your-tenant-id")
# Only trigger if BOTH email AND phone number are present
pii = PIIScanner(
labels=[PIILabel.EMAIL_ADDRESS, PIILabel.PHONE_NUMBER],
action=Action.REPLACE,
condition=Condition.ALL,
)
result = guard.scan_input(
text="Call me at 555-0123", # Only phone, no email
scanners=[pii],
)
print(result.status) # "clean" — Condition.ALL not met (no email detected)
Expected output:
ScanResult
Every call to guard.scan_input() or guard.scan_output() returns a ScanResult object.
Top-level fields
| Field | Type | Description |
|---|---|---|
status |
str |
Overall outcome: "clean" (nothing triggered), "secured" (content redacted), or "blocked" (content rejected). |
processed_text |
str |
The text after all actions have been applied. For Action.REPLACE, this contains placeholders. For Action.BLOCK and Action.LOG, this is the original text. |
placeholders |
dict[str, str] |
Maps placeholder tokens to the original detected values. Example: {"[EMAIL_ADDRESS-A1B2]": "alice@example.com"}. Empty if no replacements were made. |
level |
int |
Severity of the highest-risk detection, from 0 (none) to 5 (critical). |
scanners |
dict[str, ScannerResult] |
Per-scanner breakdown keyed by internal scanner ID. Iterate .values() to inspect each scanner. |
risk_categories |
list[str] |
High-level risk categories that triggered, e.g. ["PII", "SECRETS"]. |
ScannerResult fields
Each value in result.scanners is a ScannerResult:
| Field | Type | Description |
|---|---|---|
triggered |
bool |
Whether this scanner's condition was met and its action fired. |
score |
float |
Overall confidence score for this scanner's detections (0.0–1.0). |
detections |
list[Detection] |
Individual spans detected by this scanner. |
Detection fields
Each item in scanner_result.detections is a Detection:
| Field | Type | Description |
|---|---|---|
label |
str |
The label that matched, e.g. "EMAIL_ADDRESS". |
text |
str |
The exact text that was detected. |
start |
int |
Start character offset in the original text. |
end |
int |
End character offset (exclusive). |
confidence |
float |
Detection confidence (0.0–1.0). |
action |
Action |
The action applied to this specific detection. |
from meshulash_guard import Guard, Action
from meshulash_guard.scanners import PIIScanner, PIILabel
guard = Guard(api_key="your-api-key", tenant_id="your-tenant-id")
pii = PIIScanner(labels=[PIILabel.EMAIL_ADDRESS, PIILabel.PHONE_NUMBER], action=Action.REPLACE)
result = guard.scan_input(
text="Reach me at bob@example.com or 555-9876",
scanners=[pii],
)
print(result.status) # "secured"
print(result.risk_categories) # ["PII"]
for scanner_result in result.scanners.values():
for d in scanner_result.detections:
print(f"{d.label}: '{d.text}' at [{d.start}:{d.end}] confidence={d.confidence:.2f}")
Expected output:
secured
['PII']
EMAIL_ADDRESS: 'bob@example.com' at [12:27] confidence=0.99
PHONE_NUMBER: '555-9876' at [31:39] confidence=0.96
Exceptions
meshulash-guard uses a structured exception hierarchy so you can handle specific failure modes precisely.
| Exception | When Raised |
|---|---|
MeshulashError |
Base class for all SDK exceptions. Catch this to handle any SDK error. |
AuthError |
API key is invalid, expired, or missing. Check your credentials. |
ServerError |
The Meshulash server returned a 5xx response. Has a .status_code attribute. |
ConnectionError |
The SDK could not reach the server (DNS failure, timeout, network unreachable). |
ValidationError |
The request payload failed server-side validation. Usually indicates a misconfigured scanner. |
from meshulash_guard import Guard, Action, MeshulashError, AuthError, ServerError, ConnectionError, ValidationError
from meshulash_guard.scanners import PIIScanner, PIILabel
guard = Guard(api_key="your-api-key", tenant_id="your-tenant-id")
pii = PIIScanner(labels=[PIILabel.EMAIL_ADDRESS], action=Action.REPLACE)
try:
result = guard.scan_input("Contact alice@example.com", scanners=[pii])
print(result.status)
except AuthError:
print("Invalid or expired API key — check your credentials")
except ServerError as e:
print(f"Server error {e.status_code} — retry or contact support")
except ConnectionError:
print("Could not reach the Meshulash server — check your network")
except ValidationError as e:
print(f"Bad request — check scanner configuration: {e}")
except MeshulashError as e:
print(f"Unexpected SDK error: {e}")
Expected output (when credentials are valid):