Metadata-Version: 2.5
Name: hardn-runtime
Version: 0.1.0
Summary: Hardn runtime protection for Python: label agent traffic, serve honeytokens, screen prompt injection, report to Hardn.
Project-URL: Homepage, https://hardn.si
Project-URL: Documentation, https://hardn.si/developers#runtime
Author: Infonex Pty Ltd
License: Proprietary
Keywords: hardn,honeytoken,middleware,prompt-injection,security
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# Hardn runtime protection (Python)

A small, **defensive** middleware you run inside your own app. It:

- **labels automated-agent traffic** from request headers and path (`score_agent`),
- **serves honeytokens** — decoy values and paths a real user never touches (`Honeytokens`),
- **screens text for prompt-injection** patterns before it reaches an LLM (`scan_prompt`),
- **reports** these signals to Hardn (`Telemetry`) so you see them on your dashboard.

It never modifies your data and only calls out to Hardn's telemetry endpoint. Telemetry is fail-open: a
network problem drops a batch of signals, it never breaks a request. Client IPs are hashed before they leave
your process.

```bash
pip install hardn-runtime
```

## Flask example

```python
from flask import Flask, request, abort
from hardn_runtime import Telemetry, Honeytokens, score_agent, scan_prompt

app = Flask(__name__)
tel = Telemetry(api_key="hk_live_…", source="web-1")
honey = Honeytokens()

@app.before_request
def guard():
    sig = score_agent(user_agent=request.headers.get("User-Agent"), path=request.path,
                      accept=request.headers.get("Accept"))
    if sig.is_agent:
        tel.record("agent_detected", ip=request.remote_addr, path=request.path, score=sig.score,
                   detail={"reasons": sig.reasons})
    if honey.is_decoy_path(request.path):
        tel.record("honeytoken_hit", ip=request.remote_addr, path=request.path, score=100)
        abort(404)  # behave like any other missing page

@app.post("/chat")
def chat():
    result = scan_prompt(request.json.get("message", ""))
    if result.flagged:
        tel.record("injection_blocked", score=result.score, detail={"tactics": result.tactics})
        # strip / review / reject as your policy requires
    ...
```

## FastAPI / Starlette

Use the same calls from an `http` middleware or dependency; `score_agent` and `scan_prompt` are plain
functions, and `Telemetry.record` is safe to call from async code (it only buffers and occasionally POSTs).

## What gets sent

Each signal is `{kind, source, ip_hash, path, score, detail}` where `kind` is `agent_detected`,
`honeytoken_hit`, `injection_blocked` or `heartbeat`. Call `tel.flush()` on shutdown to send the last batch.
