Histos
Hijacked. Still bounded.
Deterministic authorization for AI agent tool calls — before execution and after return. The model proposes. Your policy decides.
Histos puts an in-process security boundary around the tools an agent can call. It does not guess whether a prompt is malicious. It enforces one narrow loop even when the model is manipulated:
- Authorize the input — tool, arguments, principal and trusted resource facts.
- Execute only within policy — or deny / require confirmation before side effects.
- Constrain the output — project and redact before it returns to the model.
No proxy, model or service is required. The core has zero runtime dependencies, works with sync and async tools, and keeps ordinary policy evaluation in-process. It is deliberately a small Python enforcement layer, not an identity platform, sandbox or fleet-governance suite. Audit, coverage, tool import and drift detection make that boundary reviewable and deployable.
Why this exists
A stronger prompt can reduce the chance of a bad decision. It cannot authorize a payment, prove tenant ownership or prevent a forbidden tool call from executing. Histos treats model input, retrieved documents and tool output as untrusted, then checks the action against policy written before the agent encountered them.
model / conversation / retrieved content / tool output untrusted, variable
────────────────────────────────────────────────────────────────────────────
policy + authenticated principal + trusted resource data trusted boundary
This is enforcement, not prompt-injection detection. Detection asks whether content looks dangerous; Histos asks what the agent is allowed to do regardless. Its schema, RBAC, resource and binding checks are deterministic for their inputs; resource lookups, stateful limits, clocks and human approval remain runtime inputs.
See the boundary hold
The repository contains five runnable demos: a LangChain clinic receptionist, a LangGraph accounts-payable workflow, a framework-free on-call agent, an MCP tool rug pull, and a mediation harness. Each attack is judged from actual datastore effects, not from what the assistant claimed it did.
| scenario | without Histos | with a complete policy |
|---|---|---|
| poisoned clinic note redirects an SMS | patient data sent off-site | recipient rebound to the authenticated patient |
| invoice quietly swaps the supplier IBAN | 14,200 PLN sent to the wrong account | payee denied against trusted supplier data |
| injected runbook requests zero replicas and a production deploy | service damaged and an invented version deployed | arguments and resource state keep production unchanged |
| MCP vendor rewrites a tool description after review | ordinary schema diff is silent | description drift makes the CI command exit 1 |
In the controlled qwen2.5:7b runs, 6 of 11 attacks damaged the competent baseline
and 0 of 11 damaged the fully mediated version. Those model-driven figures were
measured manually at temperature 0; they are evidence from these scenarios, not a
general benchmark. A larger model avoided some baseline attacks, while the policy
bounds remained deterministic. The clinic policy also demonstrates a real product
cost: binding the SMS recipient removes caller-selected delivery. The full methods,
raw distinctions and partial-wiring failure are documented in the
demo report.
Install
pip install "histos[yaml]"
Requires Python 3.12 or newer. The yaml extra adds PyYAML; JSON policies use only
the standard library. To see a hijacked call remain bounded with no model or
infrastructure, clone the repository and run python examples/makeRefund_demo.py.
The adversarial applications are in
demo/.
Protect a tool
from histos import Field, GateDenied, Policy, Principal, Schema, ToolContract
from histos import gate, use_principal
def delete_user(user_id: int):
return {"deleted": user_id}
policy = Policy(
tools={"delete_user": ToolContract(
name="delete_user",
args=Schema({"user_id": Field(type="integer")}),
access="write",
)},
permissions={"admin": frozenset({"delete_user"})},
)
safe_delete = gate(delete_user, policy=policy)
with use_principal(Principal(role="admin", identity="svc-1")):
safe_delete(user_id=42) # allowed
with use_principal(Principal(role="viewer", identity="svc-2")):
try:
safe_delete(user_id=42)
except GateDenied as exc:
print(exc.decision.rule) # rbac
Set the Principal in trusted host code from an authenticated session or workload
identity — never from model output or a tool argument. protect() handles a whole
tool set and reports policy review and coverage; a supplied tool with no contract or
grant is still wrapped and denies by default. See the
complete quickstart
and commented policy.
A production adoption path
Import tool shapes from MCP, OpenAI tools, OpenAPI, JSON Schema or Python signatures.
Author what those schemas cannot know — roles, ownership, trusted bindings,
confirmation and output rules. Run histos review and histos coverage, calibrate in
mode="observe", then enforce with a durable audit sink and drift check in CI.
Worked policies for RAG, refunds, outbound email, MCP and deployments live in the policy gallery.
Read this before production
Histos is defense in depth, not a sandbox and not a replacement for backend authorization.
- Every execution path must receive the wrapped callable. A raw tool retained or registered elsewhere is a bypass; coverage sees only the surface you declare.
- Principal, resource facts, confirmation and policy are trusted host inputs. Histos does not replace backend authorization, sandbox compromised code or undo side effects before a post-call check.
- Histos does not understand intent or stop unsafe workflows composed from separately allowed calls. Limits and built-in approvals are process-local; the default audit sink is memory-only.
- The complete joined argument text is limited to 1 MiB by default and can be raised
with
input_budget=. A field usingpatternis limited to 4,096 characters because it runs through Python's backtracking regex engine; unpatterned text is not.
The exact guarantee, residual object-inspection limits and safe deployment patterns are in SECURITY.md.
Status and documentation
Histos 0.1.0 is an alpha API implementing Histos Policy Format Draft 0.1. The Python engine, policy format, CLI, conformance corpus, LangChain/LangGraph adapters and tool definition import/drift workflow exist today. A hosted control plane, JavaScript runtime and dedicated MCP enforcement product do not.
Apache-2.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file histos-0.1.0.tar.gz.
File metadata
- Download URL: histos-0.1.0.tar.gz
- Upload date:
- Size: 381.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
650d8b01a4a21e7c728951210e0d4d74defa1d904fb7ab81ad204cffe836535a
|
|
| MD5 |
6e4cada0f27f9909d975d8a9aad50a51
|
|
| BLAKE2b-256 |
f65aa55299ab74b3ae4bb92181e20f79f3d0ac6e2ef144a5bdf5d9fa244a7ffe
|
Provenance
The following attestation bundles were made for histos-0.1.0.tar.gz:
Publisher:
release.yml on Szesnasty/histos
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
histos-0.1.0.tar.gz -
Subject digest:
650d8b01a4a21e7c728951210e0d4d74defa1d904fb7ab81ad204cffe836535a - Sigstore transparency entry: 2490945802
- Sigstore integration time:
-
Permalink:
Szesnasty/histos@1fb29ee85b9ef6a51f426b0b68c74975c03f9bae -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Szesnasty
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1fb29ee85b9ef6a51f426b0b68c74975c03f9bae -
Trigger Event:
push
-
Statement type:
File details
Details for the file histos-0.1.0-py3-none-any.whl.
File metadata
- Download URL: histos-0.1.0-py3-none-any.whl
- Upload date:
- Size: 303.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
175f4625a28015febaeb02fdb34604bc3585079ca03d81d3d2a11051196a82c1
|
|
| MD5 |
17817d2dbef0697ef41cd6a7cecaa8a1
|
|
| BLAKE2b-256 |
f7031a8caa42aca5519c1aef2df793a1094dd5bd274c43f9175a6ba09b40b5ec
|
Provenance
The following attestation bundles were made for histos-0.1.0-py3-none-any.whl:
Publisher:
release.yml on Szesnasty/histos
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
histos-0.1.0-py3-none-any.whl -
Subject digest:
175f4625a28015febaeb02fdb34604bc3585079ca03d81d3d2a11051196a82c1 - Sigstore transparency entry: 2490945830
- Sigstore integration time:
-
Permalink:
Szesnasty/histos@1fb29ee85b9ef6a51f426b0b68c74975c03f9bae -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Szesnasty
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1fb29ee85b9ef6a51f426b0b68c74975c03f9bae -
Trigger Event:
push
-
Statement type: