hexgate 0.2.2__tar.gz → 0.2.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.
- {hexgate-0.2.2 → hexgate-0.2.3}/PKG-INFO +31 -18
- {hexgate-0.2.2 → hexgate-0.2.3}/README.md +29 -16
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/google/runner.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/langchain/agent.py +2 -2
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/langchain/wrapper.py +2 -2
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/openai/runner.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/pydantic_ai/agent.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/pydantic_ai/wrapper.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/loader.py +4 -4
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/_common.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/hexgate.py +2 -2
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/main.py +2 -2
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/serve.py +3 -3
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cloud/__init__.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cloud/attenuate.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cloud/biscuit.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cloud/client.py +7 -7
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/rego.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/wasm_engine.py +3 -3
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/streaming/__init__.py +1 -1
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate.egg-info/PKG-INFO +31 -18
- {hexgate-0.2.2 → hexgate-0.2.3}/pyproject.toml +2 -2
- {hexgate-0.2.2 → hexgate-0.2.3}/LICENSE +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/google/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/google/tools.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/google/wrapper.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/langchain/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/langchain/tools.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/openai/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/openai/tools.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/openai/wrapper.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/pydantic_ai/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/adapters/pydantic_ai/tools.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/builtin/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/builtin/researcher/agent.yaml +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/builtin/researcher/policy.yaml +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/builtin/researcher/system.md +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/factory.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/models.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/agents/prompts/agent_system.md +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/audit.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/bootstrap.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/chat.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/policy/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/policy/main.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/google.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/langchain.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/manifest.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/models.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/openai.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/pydantic_ai.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/register/register.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/cli/state.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/config/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/config/settings.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/runtime/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/runtime/command_policy.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/runtime/context.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/runtime/sandbox_runtime.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/runtime/srt.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/runtime/workspace.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/binding.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/bundle.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/constraints.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/decision.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/enforcer.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/errors.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/file_scope.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/models.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/policy.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/policy_set.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/rego_wasm.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/signing.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/security/source.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/streaming/events.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/streaming/normalize.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/bash.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/decorators.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/fetch.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/files/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/files/_common.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/files/edit_file.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/files/glob.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/files/grep.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/files/read_file.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/files/write_file.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/refund.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tools/websearch.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tracing/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/tracing/langfuse.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/utils/__init__.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate/utils/retry.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate.egg-info/SOURCES.txt +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate.egg-info/dependency_links.txt +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate.egg-info/entry_points.txt +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate.egg-info/requires.txt +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/hexgate.egg-info/top_level.txt +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/setup.cfg +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/tests/test_bootstrap.py +0 -0
- {hexgate-0.2.2 → hexgate-0.2.3}/tests/test_demo.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexgate
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: Hexgate — authorization infrastructure for AI agents (agent runtime + cloud client).
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.13
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -39,29 +39,38 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
<div align="center">
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
<img src="./icon.svg" alt="Hexgate" width="96" height="96" />
|
|
43
|
+
|
|
44
|
+
# Hexgate
|
|
43
45
|
|
|
44
46
|
**Authorization infrastructure for AI agents.**
|
|
45
47
|
Policy enforcement, signed policy bundles, per-request user scope, audit trail — for OpenAI Agents, LangChain, Google ADK, Pydantic AI, or a native runtime.
|
|
46
48
|
|
|
49
|
+
[**Website**](https://hexgate.ai) · [Docs](https://docs.hexgate.ai) · [PyPI](https://pypi.org/project/hexgate/) · [Discussions](https://github.com/HexamindOrganisation/hexgate/discussions)
|
|
50
|
+
|
|
47
51
|
[](https://pypi.org/project/hexgate/)
|
|
48
|
-
[](https://pypi.org/project/hexgate/)
|
|
49
52
|
[](https://github.com/HexamindOrganisation/hexgate/actions/workflows/tests.yml)
|
|
50
53
|
[](https://pypi.org/project/hexgate/)
|
|
51
54
|
[](LICENSE)
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
<br />
|
|
57
|
+
|
|
58
|
+
<img src="./assets/hero.png" alt="Control what your agents do — not just what they say. Policy decisions streaming live from the PolicyEnforcer." />
|
|
59
|
+
|
|
60
|
+
<br />
|
|
61
|
+
|
|
62
|
+
[Quick Start](#-quick-start--local-cli) · [Two paths](#-which-path-do-i-pick) · [Framework adapters](#-framework-agent-wrapping) · [Policy bundles](#-policy-bundles--compile-sign-enforce-wasm) · [User scope](#-user-scope--roles) · [Platform](#-hexgate-platform)
|
|
54
63
|
|
|
55
64
|
</div>
|
|
56
65
|
|
|
57
66
|
---
|
|
58
67
|
|
|
59
|
-
## What is
|
|
68
|
+
## What is Hexgate?
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
Hexgate is two things that move together:
|
|
62
71
|
|
|
63
72
|
- **`hexgate` — the SDK.** A Python runtime that gates every tool call through a typed `Decision` (allow / deny / approval-required), wraps your existing OpenAI / LangChain / Google ADK / Pydantic AI agent without rewriting it, and threads per-request user identity through tracing + audit.
|
|
64
|
-
- **The
|
|
73
|
+
- **The Hexgate platform** *(optional)* — a FastAPI control plane + React dashboard for editing policy in a browser, minting per-project tokens, watching live decisions stream from a serving agent, and shipping signed WASM policy bundles to production.
|
|
65
74
|
|
|
66
75
|
You can use the SDK with nothing else (single-process REPL, YAML on disk). Or plug in the platform when you want auditable decisions in ClickHouse, a shared Playground UI, and live policy edits.
|
|
67
76
|
|
|
@@ -79,7 +88,7 @@ You can use the SDK with nothing else (single-process REPL, YAML on disk). Or pl
|
|
|
79
88
|
┌────────────────┐ ┌──────────────────┐ ┌────────────────┐
|
|
80
89
|
│ Local policy │ │ Signed WASM │ │ Audit log │
|
|
81
90
|
│ (YAML / dir, │ │ bundle from │ │ (ClickHouse │
|
|
82
|
-
│ hot reload) │ │
|
|
91
|
+
│ hot reload) │ │ Hexgate cloud │ │ via REST) │
|
|
83
92
|
└────────────────┘ └──────────────────┘ └────────────────┘
|
|
84
93
|
```
|
|
85
94
|
|
|
@@ -103,7 +112,7 @@ You can use the SDK with nothing else (single-process REPL, YAML on disk). Or pl
|
|
|
103
112
|
- [Environment](#-environment)
|
|
104
113
|
- [Tests & dev tooling](#-tests--dev-tooling)
|
|
105
114
|
- [CLI reference](#-cli-reference)
|
|
106
|
-
- [
|
|
115
|
+
- [Hexgate platform](#-hexgate-platform)
|
|
107
116
|
- [User scope + roles](#-user-scope--roles)
|
|
108
117
|
- [Stream results](#-stream-results)
|
|
109
118
|
|
|
@@ -177,7 +186,7 @@ Both commands accept either a plain agent id (`--agent researcher`) or a uvicorn
|
|
|
177
186
|
|
|
178
187
|
## 🚀 Quick Start — Platform
|
|
179
188
|
|
|
180
|
-
To run the full
|
|
189
|
+
To run the full Hexgate control plane locally (FastAPI backend + dashboard + your local agent serving over WebSocket), you need **three terminals**. The Makefile has a target that prints the recipe:
|
|
181
190
|
|
|
182
191
|
```bash
|
|
183
192
|
make demo-platform # prints the 3-terminal recipe below
|
|
@@ -660,7 +669,7 @@ What happens under the hood:
|
|
|
660
669
|
|
|
661
670
|
Working scripts in `examples/`:
|
|
662
671
|
|
|
663
|
-
- `examples/customer_bot.py` — canonical
|
|
672
|
+
- `examples/customer_bot.py` — canonical Hexgate path: `create_agent(...)` + the dashboard register/serve loop end-to-end.
|
|
664
673
|
- `examples/openai_demo.py` — `HexgateRunner` (OpenAI Agents SDK) end-to-end.
|
|
665
674
|
- `examples/google_demo.py` — `HexgateRunner` (Google ADK) end-to-end with `InMemorySessionService`.
|
|
666
675
|
- `examples/pydantic_ai_demo.py` — `wrap_pydantic_agent` (Pydantic AI) end-to-end.
|
|
@@ -795,7 +804,7 @@ That means the same agent code can stay simple in development, while deployment
|
|
|
795
804
|
|
|
796
805
|
## 🧩 Policy Bundles — Compile, Sign, Enforce (WASM)
|
|
797
806
|
|
|
798
|
-
|
|
807
|
+
Hexgate has **two policy enforcement engines** that return identical decisions (there's a parity test suite that proves it):
|
|
799
808
|
|
|
800
809
|
- **pydantic** (default) — evaluates constraints in-process. Zero setup; this is what every example above uses.
|
|
801
810
|
- **WASM** — compiles `policy.yaml` → Rego → a WebAssembly module evaluated via `wasmtime`. This is the path production ships: one compiled artifact, byte-for-byte reproducible, cryptographically signed by the platform.
|
|
@@ -1131,7 +1140,7 @@ hexgate chat --use examples/research_agents.py --agent update_researcher --appro
|
|
|
1131
1140
|
|
|
1132
1141
|
### `hexgate register` — push a manifest to the platform
|
|
1133
1142
|
|
|
1134
|
-
Register a code-defined agent's manifest with the
|
|
1143
|
+
Register a code-defined agent's manifest with the Hexgate platform. `--agent`
|
|
1135
1144
|
takes a Python import path of the form `module.path:attribute`, the same shape
|
|
1136
1145
|
as ASGI/WSGI entrypoints. The CLI imports the module, grabs the agent object,
|
|
1137
1146
|
and POSTs its manifest to `${HEXGATE_API_URL}/v1/agents` using
|
|
@@ -1170,7 +1179,7 @@ system prompt directly off the object. No flags needed.
|
|
|
1170
1179
|
`--system-prompt` accepts either a literal string or a path to a `.md` /
|
|
1171
1180
|
`.txt` / `.jinja` file (read as text at register time).
|
|
1172
1181
|
|
|
1173
|
-
Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph,
|
|
1182
|
+
Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph, Hexgate agents.
|
|
1174
1183
|
|
|
1175
1184
|
### `hexgate serve` — bridge a local agent to the platform's relay
|
|
1176
1185
|
|
|
@@ -1207,7 +1216,7 @@ print(manifest.model_dump())
|
|
|
1207
1216
|
```
|
|
1208
1217
|
|
|
1209
1218
|
`create_manifest` dispatches on the framework of `agent`. The supported
|
|
1210
|
-
types are the same set `hexgate register` accepts:
|
|
1219
|
+
types are the same set `hexgate register` accepts: Hexgate, OpenAI Agents
|
|
1211
1220
|
SDK, Google ADK, Pydantic AI, and LangChain/LangGraph compiled graphs.
|
|
1212
1221
|
For LangGraph you must pass `tools=` explicitly, and may pass `model=` /
|
|
1213
1222
|
`system_prompt=`, since compiled graphs don't expose those fields after
|
|
@@ -1217,7 +1226,7 @@ The return value is an `AgentManifest` (a Pydantic model, also re-exported
|
|
|
1217
1226
|
from `hexgate` for type annotations) — the same schema the platform
|
|
1218
1227
|
stores and the dashboard renders.
|
|
1219
1228
|
|
|
1220
|
-
## 🌐
|
|
1229
|
+
## 🌐 Hexgate Platform
|
|
1221
1230
|
|
|
1222
1231
|
The `platform/` directory contains an optional control plane that hosts agent definitions, dev tokens, and a live debug surface. The SDK works fully without it (`load_local_agent`, `load_builtin_agent` keep their existing semantics) — but with it you get:
|
|
1223
1232
|
|
|
@@ -1336,7 +1345,7 @@ the name from the loaded agent's `.name` attribute — no env var needed.
|
|
|
1336
1345
|
|
|
1337
1346
|
## 👤 User Scope + Roles
|
|
1338
1347
|
|
|
1339
|
-
Real backends serve many users, and different users get different capabilities.
|
|
1348
|
+
Real backends serve many users, and different users get different capabilities. Hexgate splits that into two pieces:
|
|
1340
1349
|
|
|
1341
1350
|
- **`User`** — the per-request scope. Marks "this invocation acts on behalf of alice, in role X." Async context manager; pushes a fact-bearing Biscuit through the agent runtime.
|
|
1342
1351
|
- **Role policies** — one `policy.yaml` per role, optionally inheriting from a base mixin. The runtime picks the right one at call time based on the active `User.role`.
|
|
@@ -1486,3 +1495,7 @@ async for event in stream_agent(agent, handler, "latest AI breakthroughs"):
|
|
|
1486
1495
|
- assistant text deltas
|
|
1487
1496
|
- tool lifecycle
|
|
1488
1497
|
- final run completion
|
|
1498
|
+
|
|
1499
|
+
---
|
|
1500
|
+
|
|
1501
|
+
If Hexgate looks useful, [give it a ⭐ on GitHub](https://github.com/HexamindOrganisation/hexgate) — it helps more than you'd think. Built by [Hexamind](https://hexgate.ai).
|
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="./icon.svg" alt="Hexgate" width="96" height="96" />
|
|
4
|
+
|
|
5
|
+
# Hexgate
|
|
4
6
|
|
|
5
7
|
**Authorization infrastructure for AI agents.**
|
|
6
8
|
Policy enforcement, signed policy bundles, per-request user scope, audit trail — for OpenAI Agents, LangChain, Google ADK, Pydantic AI, or a native runtime.
|
|
7
9
|
|
|
10
|
+
[**Website**](https://hexgate.ai) · [Docs](https://docs.hexgate.ai) · [PyPI](https://pypi.org/project/hexgate/) · [Discussions](https://github.com/HexamindOrganisation/hexgate/discussions)
|
|
11
|
+
|
|
8
12
|
[](https://pypi.org/project/hexgate/)
|
|
9
|
-
[](https://pypi.org/project/hexgate/)
|
|
10
13
|
[](https://github.com/HexamindOrganisation/hexgate/actions/workflows/tests.yml)
|
|
11
14
|
[](https://pypi.org/project/hexgate/)
|
|
12
15
|
[](LICENSE)
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
<br />
|
|
18
|
+
|
|
19
|
+
<img src="./assets/hero.png" alt="Control what your agents do — not just what they say. Policy decisions streaming live from the PolicyEnforcer." />
|
|
20
|
+
|
|
21
|
+
<br />
|
|
22
|
+
|
|
23
|
+
[Quick Start](#-quick-start--local-cli) · [Two paths](#-which-path-do-i-pick) · [Framework adapters](#-framework-agent-wrapping) · [Policy bundles](#-policy-bundles--compile-sign-enforce-wasm) · [User scope](#-user-scope--roles) · [Platform](#-hexgate-platform)
|
|
15
24
|
|
|
16
25
|
</div>
|
|
17
26
|
|
|
18
27
|
---
|
|
19
28
|
|
|
20
|
-
## What is
|
|
29
|
+
## What is Hexgate?
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
Hexgate is two things that move together:
|
|
23
32
|
|
|
24
33
|
- **`hexgate` — the SDK.** A Python runtime that gates every tool call through a typed `Decision` (allow / deny / approval-required), wraps your existing OpenAI / LangChain / Google ADK / Pydantic AI agent without rewriting it, and threads per-request user identity through tracing + audit.
|
|
25
|
-
- **The
|
|
34
|
+
- **The Hexgate platform** *(optional)* — a FastAPI control plane + React dashboard for editing policy in a browser, minting per-project tokens, watching live decisions stream from a serving agent, and shipping signed WASM policy bundles to production.
|
|
26
35
|
|
|
27
36
|
You can use the SDK with nothing else (single-process REPL, YAML on disk). Or plug in the platform when you want auditable decisions in ClickHouse, a shared Playground UI, and live policy edits.
|
|
28
37
|
|
|
@@ -40,7 +49,7 @@ You can use the SDK with nothing else (single-process REPL, YAML on disk). Or pl
|
|
|
40
49
|
┌────────────────┐ ┌──────────────────┐ ┌────────────────┐
|
|
41
50
|
│ Local policy │ │ Signed WASM │ │ Audit log │
|
|
42
51
|
│ (YAML / dir, │ │ bundle from │ │ (ClickHouse │
|
|
43
|
-
│ hot reload) │ │
|
|
52
|
+
│ hot reload) │ │ Hexgate cloud │ │ via REST) │
|
|
44
53
|
└────────────────┘ └──────────────────┘ └────────────────┘
|
|
45
54
|
```
|
|
46
55
|
|
|
@@ -64,7 +73,7 @@ You can use the SDK with nothing else (single-process REPL, YAML on disk). Or pl
|
|
|
64
73
|
- [Environment](#-environment)
|
|
65
74
|
- [Tests & dev tooling](#-tests--dev-tooling)
|
|
66
75
|
- [CLI reference](#-cli-reference)
|
|
67
|
-
- [
|
|
76
|
+
- [Hexgate platform](#-hexgate-platform)
|
|
68
77
|
- [User scope + roles](#-user-scope--roles)
|
|
69
78
|
- [Stream results](#-stream-results)
|
|
70
79
|
|
|
@@ -138,7 +147,7 @@ Both commands accept either a plain agent id (`--agent researcher`) or a uvicorn
|
|
|
138
147
|
|
|
139
148
|
## 🚀 Quick Start — Platform
|
|
140
149
|
|
|
141
|
-
To run the full
|
|
150
|
+
To run the full Hexgate control plane locally (FastAPI backend + dashboard + your local agent serving over WebSocket), you need **three terminals**. The Makefile has a target that prints the recipe:
|
|
142
151
|
|
|
143
152
|
```bash
|
|
144
153
|
make demo-platform # prints the 3-terminal recipe below
|
|
@@ -621,7 +630,7 @@ What happens under the hood:
|
|
|
621
630
|
|
|
622
631
|
Working scripts in `examples/`:
|
|
623
632
|
|
|
624
|
-
- `examples/customer_bot.py` — canonical
|
|
633
|
+
- `examples/customer_bot.py` — canonical Hexgate path: `create_agent(...)` + the dashboard register/serve loop end-to-end.
|
|
625
634
|
- `examples/openai_demo.py` — `HexgateRunner` (OpenAI Agents SDK) end-to-end.
|
|
626
635
|
- `examples/google_demo.py` — `HexgateRunner` (Google ADK) end-to-end with `InMemorySessionService`.
|
|
627
636
|
- `examples/pydantic_ai_demo.py` — `wrap_pydantic_agent` (Pydantic AI) end-to-end.
|
|
@@ -756,7 +765,7 @@ That means the same agent code can stay simple in development, while deployment
|
|
|
756
765
|
|
|
757
766
|
## 🧩 Policy Bundles — Compile, Sign, Enforce (WASM)
|
|
758
767
|
|
|
759
|
-
|
|
768
|
+
Hexgate has **two policy enforcement engines** that return identical decisions (there's a parity test suite that proves it):
|
|
760
769
|
|
|
761
770
|
- **pydantic** (default) — evaluates constraints in-process. Zero setup; this is what every example above uses.
|
|
762
771
|
- **WASM** — compiles `policy.yaml` → Rego → a WebAssembly module evaluated via `wasmtime`. This is the path production ships: one compiled artifact, byte-for-byte reproducible, cryptographically signed by the platform.
|
|
@@ -1092,7 +1101,7 @@ hexgate chat --use examples/research_agents.py --agent update_researcher --appro
|
|
|
1092
1101
|
|
|
1093
1102
|
### `hexgate register` — push a manifest to the platform
|
|
1094
1103
|
|
|
1095
|
-
Register a code-defined agent's manifest with the
|
|
1104
|
+
Register a code-defined agent's manifest with the Hexgate platform. `--agent`
|
|
1096
1105
|
takes a Python import path of the form `module.path:attribute`, the same shape
|
|
1097
1106
|
as ASGI/WSGI entrypoints. The CLI imports the module, grabs the agent object,
|
|
1098
1107
|
and POSTs its manifest to `${HEXGATE_API_URL}/v1/agents` using
|
|
@@ -1131,7 +1140,7 @@ system prompt directly off the object. No flags needed.
|
|
|
1131
1140
|
`--system-prompt` accepts either a literal string or a path to a `.md` /
|
|
1132
1141
|
`.txt` / `.jinja` file (read as text at register time).
|
|
1133
1142
|
|
|
1134
|
-
Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph,
|
|
1143
|
+
Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph, Hexgate agents.
|
|
1135
1144
|
|
|
1136
1145
|
### `hexgate serve` — bridge a local agent to the platform's relay
|
|
1137
1146
|
|
|
@@ -1168,7 +1177,7 @@ print(manifest.model_dump())
|
|
|
1168
1177
|
```
|
|
1169
1178
|
|
|
1170
1179
|
`create_manifest` dispatches on the framework of `agent`. The supported
|
|
1171
|
-
types are the same set `hexgate register` accepts:
|
|
1180
|
+
types are the same set `hexgate register` accepts: Hexgate, OpenAI Agents
|
|
1172
1181
|
SDK, Google ADK, Pydantic AI, and LangChain/LangGraph compiled graphs.
|
|
1173
1182
|
For LangGraph you must pass `tools=` explicitly, and may pass `model=` /
|
|
1174
1183
|
`system_prompt=`, since compiled graphs don't expose those fields after
|
|
@@ -1178,7 +1187,7 @@ The return value is an `AgentManifest` (a Pydantic model, also re-exported
|
|
|
1178
1187
|
from `hexgate` for type annotations) — the same schema the platform
|
|
1179
1188
|
stores and the dashboard renders.
|
|
1180
1189
|
|
|
1181
|
-
## 🌐
|
|
1190
|
+
## 🌐 Hexgate Platform
|
|
1182
1191
|
|
|
1183
1192
|
The `platform/` directory contains an optional control plane that hosts agent definitions, dev tokens, and a live debug surface. The SDK works fully without it (`load_local_agent`, `load_builtin_agent` keep their existing semantics) — but with it you get:
|
|
1184
1193
|
|
|
@@ -1297,7 +1306,7 @@ the name from the loaded agent's `.name` attribute — no env var needed.
|
|
|
1297
1306
|
|
|
1298
1307
|
## 👤 User Scope + Roles
|
|
1299
1308
|
|
|
1300
|
-
Real backends serve many users, and different users get different capabilities.
|
|
1309
|
+
Real backends serve many users, and different users get different capabilities. Hexgate splits that into two pieces:
|
|
1301
1310
|
|
|
1302
1311
|
- **`User`** — the per-request scope. Marks "this invocation acts on behalf of alice, in role X." Async context manager; pushes a fact-bearing Biscuit through the agent runtime.
|
|
1303
1312
|
- **Role policies** — one `policy.yaml` per role, optionally inheriting from a base mixin. The runtime picks the right one at call time based on the active `User.role`.
|
|
@@ -1447,3 +1456,7 @@ async for event in stream_agent(agent, handler, "latest AI breakthroughs"):
|
|
|
1447
1456
|
- assistant text deltas
|
|
1448
1457
|
- tool lifecycle
|
|
1449
1458
|
- final run completion
|
|
1459
|
+
|
|
1460
|
+
---
|
|
1461
|
+
|
|
1462
|
+
If Hexgate looks useful, [give it a ⭐ on GitHub](https://github.com/HexamindOrganisation/hexgate) — it helps more than you'd think. Built by [Hexamind](https://hexgate.ai).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Proxy around a pre-built ``CompiledStateGraph`` for
|
|
1
|
+
"""Proxy around a pre-built ``CompiledStateGraph`` for Hexgate-aware calls."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -60,7 +60,7 @@ class HexgateLangchainAgent:
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
def _with_callbacks(self, config: RunnableConfig | None) -> RunnableConfig:
|
|
63
|
-
"""Append the
|
|
63
|
+
"""Append the Hexgate callback handler to ``config['callbacks']``."""
|
|
64
64
|
merged: RunnableConfig = dict(config) if config else {}
|
|
65
65
|
callbacks = list(merged.get("callbacks") or [])
|
|
66
66
|
if self._callback_handler not in callbacks:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""BYO-graph entry point: retrofit a pre-built ``CompiledStateGraph`` with
|
|
2
|
-
|
|
2
|
+
Hexgate policy. Tools are mutated in place so the graph keeps its
|
|
3
3
|
references; the returned :class:`HexgateLangchainAgent` opens a User
|
|
4
4
|
scope + Langfuse propagation per call. For the manifest-driven path,
|
|
5
5
|
use :func:`hexgate.enforce_policy` instead.
|
|
@@ -28,7 +28,7 @@ def wrap_langchain_agent(
|
|
|
28
28
|
tools: list[BaseTool],
|
|
29
29
|
api_key: str | None = None,
|
|
30
30
|
) -> HexgateLangchainAgent:
|
|
31
|
-
"""Wrap a pre-built LangGraph agent with
|
|
31
|
+
"""Wrap a pre-built LangGraph agent with Hexgate policy enforcement.
|
|
32
32
|
|
|
33
33
|
Mutates ``tools`` in place so the graph keeps its references.
|
|
34
34
|
The returned proxy takes ``user`` per invocation; role resolves at
|
|
@@ -32,7 +32,7 @@ from hexgate.security.enforcer import build_enforcer
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
class HexgateRunner:
|
|
35
|
-
"""Runner for OpenAI agents with
|
|
35
|
+
"""Runner for OpenAI agents with Hexgate tool policy and observability."""
|
|
36
36
|
|
|
37
37
|
def __init__(self, api_key: str | None = None):
|
|
38
38
|
self.api_key = api_key or os.getenv("HEXGATE_KEY")
|
|
@@ -49,7 +49,7 @@ def wrap_pydantic_agent(
|
|
|
49
49
|
agent: Agent,
|
|
50
50
|
api_key: str | None = None,
|
|
51
51
|
) -> HexgatePydanticAgent:
|
|
52
|
-
"""Wrap a pydantic_ai agent with
|
|
52
|
+
"""Wrap a pydantic_ai agent with Hexgate policy + observability.
|
|
53
53
|
|
|
54
54
|
Returns a :class:`HexgatePydanticAgent` backed by a clone of the
|
|
55
55
|
caller's ``agent``; the original is not mutated. The proxy takes
|
|
@@ -462,10 +462,10 @@ def load_hexgate_agent(
|
|
|
462
462
|
approval_handler: ApprovalHandler | None = None,
|
|
463
463
|
decision_observer: "DecisionObserver | None" = None,
|
|
464
464
|
) -> tuple[AgentGraph, CallbackHandler]:
|
|
465
|
-
"""Fetch an agent from
|
|
465
|
+
"""Fetch an agent from Hexgate and return it with policy enforcement applied.
|
|
466
466
|
|
|
467
467
|
Mirrors `load_local_agent` but sources the three YAMLs (agent, policy, system)
|
|
468
|
-
from the
|
|
468
|
+
from the Hexgate API instead of disk. Tool resolution and enforcement are
|
|
469
469
|
identical — only the bytes' origin differs.
|
|
470
470
|
|
|
471
471
|
``name`` is required. The Phase-7 env-var fallback chain
|
|
@@ -555,7 +555,7 @@ def load_agent(
|
|
|
555
555
|
approval_handler: ApprovalHandler | None = None,
|
|
556
556
|
decision_observer: "DecisionObserver | None" = None,
|
|
557
557
|
) -> tuple[AgentGraph, CallbackHandler]:
|
|
558
|
-
"""Load an agent from
|
|
558
|
+
"""Load an agent from Hexgate (when HEXGATE_KEY is set), local, or builtin.
|
|
559
559
|
|
|
560
560
|
``name`` is required for every path post-Phase 7 — the
|
|
561
561
|
HEXGATE_AGENT_NAME env-var fallback was removed when ``hexgate
|
|
@@ -584,7 +584,7 @@ def load_agent(
|
|
|
584
584
|
decision_observer=decision_observer,
|
|
585
585
|
)
|
|
586
586
|
if name is None:
|
|
587
|
-
raise ValueError("load_agent() requires a name when not using
|
|
587
|
+
raise ValueError("load_agent() requires a name when not using Hexgate Cloud")
|
|
588
588
|
source = resolve_agent_source(name, base_dir)
|
|
589
589
|
if source == "local":
|
|
590
590
|
return load_local_agent(
|
|
@@ -63,7 +63,7 @@ def build_runtime(
|
|
|
63
63
|
"this loads in serve but not chat" footgun — same spec form
|
|
64
64
|
``hexgate serve`` already accepts.
|
|
65
65
|
|
|
66
|
-
``local_only=True`` keeps the loader off the
|
|
66
|
+
``local_only=True`` keeps the loader off the Hexgate Cloud path even
|
|
67
67
|
when ``HEXGATE_KEY`` is present in the environment — what terminal
|
|
68
68
|
chat uses, since it doesn't need cloud-fetched policy or a serve
|
|
69
69
|
tunnel. ``hexgate serve`` passes ``local_only=False`` so policy edits
|
|
@@ -19,10 +19,10 @@ from hexgate.cli.register.models import (
|
|
|
19
19
|
def create_hexgate_manifest(
|
|
20
20
|
agent: HexgateAgent, *, description: str | None = None
|
|
21
21
|
) -> AgentManifest:
|
|
22
|
-
"""Build an AgentManifest from a
|
|
22
|
+
"""Build an AgentManifest from a Hexgate agent created by `create_agent`."""
|
|
23
23
|
if not agent.name:
|
|
24
24
|
raise ValueError(
|
|
25
|
-
"
|
|
25
|
+
"Hexgate agent has no name — set a name on the HexgateAgent so the "
|
|
26
26
|
"manifest can identify it on the platform."
|
|
27
27
|
)
|
|
28
28
|
return AgentManifest(
|
|
@@ -40,8 +40,8 @@ def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
|
40
40
|
"""
|
|
41
41
|
parser = subparsers.add_parser(
|
|
42
42
|
"register",
|
|
43
|
-
help="Register an agent to the
|
|
44
|
-
description="Register an agent to the
|
|
43
|
+
help="Register an agent to the Hexgate platform.",
|
|
44
|
+
description="Register an agent to the Hexgate platform.",
|
|
45
45
|
)
|
|
46
46
|
parser.add_argument(
|
|
47
47
|
"--agent",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Serve subcommand: bridge a local agent to the
|
|
1
|
+
"""Serve subcommand: bridge a local agent to the Hexgate control plane.
|
|
2
2
|
|
|
3
3
|
Connects to ``ws://{API_URL}/v1/serve`` and authenticates via the
|
|
4
4
|
``bearer.<envelope>`` WebSocket subprotocol — the server derives the
|
|
@@ -251,9 +251,9 @@ def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
|
251
251
|
"""Register the `serve` subcommand on the top-level hexgate CLI."""
|
|
252
252
|
parser = subparsers.add_parser(
|
|
253
253
|
"serve",
|
|
254
|
-
help="Relay a local agent to the
|
|
254
|
+
help="Relay a local agent to the Hexgate dashboard over WebSocket.",
|
|
255
255
|
description=(
|
|
256
|
-
"Serve a local agent to the
|
|
256
|
+
"Serve a local agent to the Hexgate dashboard Playground over "
|
|
257
257
|
"WebSocket. Takes a module:attr spec — the same form as "
|
|
258
258
|
"`hexgate register --agent ...` — and brings the agent up "
|
|
259
259
|
"end-to-end: auto-registers the manifest (idempotent), fetches "
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Transport layer for the
|
|
1
|
+
"""Transport layer for the Hexgate control plane — HTTP client + Biscuit verify.
|
|
2
2
|
|
|
3
3
|
The agent-loader equivalent (`load_hexgate_agent`) lives in `hexgate.agents.loader`
|
|
4
4
|
alongside `load_local_agent` / `load_builtin_agent`; this package only carries the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Attenuate a verified
|
|
1
|
+
"""Attenuate a verified Hexgate token with user-scoped facts and checks.
|
|
2
2
|
|
|
3
3
|
In production the dev's backend calls :func:`attenuate_for_user` on each
|
|
4
4
|
inbound request, taking the project-wide token from ``HEXGATE_KEY`` and
|
|
@@ -71,7 +71,7 @@ def parse_envelope(envelope: str) -> tuple[str, str, str]:
|
|
|
71
71
|
parts = envelope.split("_", 3)
|
|
72
72
|
if len(parts) != 4 or parts[0] != ENVELOPE_PREFIX:
|
|
73
73
|
raise TokenError(
|
|
74
|
-
f"malformed
|
|
74
|
+
f"malformed Hexgate token envelope (expected '{ENVELOPE_PREFIX}_<env>_<project>_<biscuit>')"
|
|
75
75
|
)
|
|
76
76
|
env, project_id, biscuit_b64 = parts[1], parts[2], parts[3]
|
|
77
77
|
return env, project_id, biscuit_b64
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""HTTP client for the
|
|
1
|
+
"""HTTP client for the Hexgate control plane.
|
|
2
2
|
|
|
3
3
|
The client trusts ``HEXGATE_KEY`` only after verifying its Biscuit signature
|
|
4
4
|
against the platform's public key. The public key is resolved in this order:
|
|
@@ -6,7 +6,7 @@ against the platform's public key. The public key is resolved in this order:
|
|
|
6
6
|
1. Explicit ``public_key`` arg passed to ``HexgateConfig``.
|
|
7
7
|
2. ``HEXGATE_PUBLIC_KEY`` env var (base64 url-safe, 32 raw bytes).
|
|
8
8
|
3. Fetched from ``GET /v1/.well-known/keys`` on first use (TOFU for POC;
|
|
9
|
-
embed a build-time constant for hosted
|
|
9
|
+
embed a build-time constant for hosted Hexgate Cloud later).
|
|
10
10
|
|
|
11
11
|
If none of the above produces a verifying key, the client raises with a
|
|
12
12
|
clear error rather than blindly forwarding the bearer token.
|
|
@@ -42,7 +42,7 @@ TOKEN_PREFIX = "fty_"
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class HexgateError(RuntimeError):
|
|
45
|
-
"""Raised for any
|
|
45
|
+
"""Raised for any Hexgate API interaction failure.
|
|
46
46
|
|
|
47
47
|
``status`` is the HTTP status code, or ``None`` for transport errors.
|
|
48
48
|
"""
|
|
@@ -54,7 +54,7 @@ class HexgateError(RuntimeError):
|
|
|
54
54
|
|
|
55
55
|
@dataclass
|
|
56
56
|
class HexgateConfig:
|
|
57
|
-
"""Resolved configuration for a
|
|
57
|
+
"""Resolved configuration for a Hexgate client.
|
|
58
58
|
|
|
59
59
|
``project_id`` is best-effort only — used by display surfaces
|
|
60
60
|
(log lines, langchain tags) but never threaded through API URLs.
|
|
@@ -332,14 +332,14 @@ class HexgateClient:
|
|
|
332
332
|
return None, exc.headers.get("ETag") or if_none_match
|
|
333
333
|
detail = exc.read().decode("utf-8", errors="replace")
|
|
334
334
|
raise HexgateError(
|
|
335
|
-
f"
|
|
335
|
+
f"Hexgate API error {exc.code} calling {url}: {detail[:200]}",
|
|
336
336
|
status=exc.code,
|
|
337
337
|
) from exc
|
|
338
338
|
except urllib.error.URLError as exc:
|
|
339
339
|
raise HexgateError(
|
|
340
|
-
f"
|
|
340
|
+
f"Hexgate API unreachable at {url}: {exc.reason}"
|
|
341
341
|
) from exc
|
|
342
342
|
try:
|
|
343
343
|
return json.loads(payload), etag
|
|
344
344
|
except json.JSONDecodeError as exc:
|
|
345
|
-
raise HexgateError(f"
|
|
345
|
+
raise HexgateError(f"Hexgate API returned non-JSON from {url}") from exc
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Compile a
|
|
1
|
+
"""Compile a Hexgate ``policy.yaml`` document into a Rego policy module.
|
|
2
2
|
|
|
3
3
|
The compiler is a pure-Python transformation: parsed YAML payload →
|
|
4
4
|
``PolicySet`` (which flattens ``inherits:`` and drops ``is_mixin:`` entries)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Evaluate a
|
|
1
|
+
"""Evaluate a Hexgate policy bundle's ``policy.wasm`` via wasmtime-py.
|
|
2
2
|
|
|
3
3
|
This module is the runtime counterpart to :mod:`hexgate.security.rego_wasm`:
|
|
4
4
|
that one *compiles* YAML → Rego → WASM, this one *evaluates* WASM →
|
|
@@ -107,7 +107,7 @@ DEFAULT_ENTRYPOINT = "hexgate/policy/decision"
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
class WasmPolicy:
|
|
110
|
-
"""A loaded, evaluable
|
|
110
|
+
"""A loaded, evaluable Hexgate policy WASM bundle.
|
|
111
111
|
|
|
112
112
|
Construct with ``WasmPolicy.from_bytes(wasm)`` or
|
|
113
113
|
``WasmPolicy.from_bundle_path(dir)``. Each instance owns its own
|
|
@@ -349,7 +349,7 @@ def _make_builtin_stubs(store: wasmtime.Store) -> dict[int, wasmtime.Func]:
|
|
|
349
349
|
builtin_id = args[0] if args else -1
|
|
350
350
|
raise WasmEvalError(
|
|
351
351
|
f"policy invoked unsupported Rego builtin "
|
|
352
|
-
f"(builtin{_arity}, id={builtin_id}); the
|
|
352
|
+
f"(builtin{_arity}, id={builtin_id}); the Hexgate "
|
|
353
353
|
"constraint grammar does not expose builtins."
|
|
354
354
|
)
|
|
355
355
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexgate
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: Hexgate — authorization infrastructure for AI agents (agent runtime + cloud client).
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.13
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -39,29 +39,38 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
<div align="center">
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
<img src="./icon.svg" alt="Hexgate" width="96" height="96" />
|
|
43
|
+
|
|
44
|
+
# Hexgate
|
|
43
45
|
|
|
44
46
|
**Authorization infrastructure for AI agents.**
|
|
45
47
|
Policy enforcement, signed policy bundles, per-request user scope, audit trail — for OpenAI Agents, LangChain, Google ADK, Pydantic AI, or a native runtime.
|
|
46
48
|
|
|
49
|
+
[**Website**](https://hexgate.ai) · [Docs](https://docs.hexgate.ai) · [PyPI](https://pypi.org/project/hexgate/) · [Discussions](https://github.com/HexamindOrganisation/hexgate/discussions)
|
|
50
|
+
|
|
47
51
|
[](https://pypi.org/project/hexgate/)
|
|
48
|
-
[](https://pypi.org/project/hexgate/)
|
|
49
52
|
[](https://github.com/HexamindOrganisation/hexgate/actions/workflows/tests.yml)
|
|
50
53
|
[](https://pypi.org/project/hexgate/)
|
|
51
54
|
[](LICENSE)
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
<br />
|
|
57
|
+
|
|
58
|
+
<img src="./assets/hero.png" alt="Control what your agents do — not just what they say. Policy decisions streaming live from the PolicyEnforcer." />
|
|
59
|
+
|
|
60
|
+
<br />
|
|
61
|
+
|
|
62
|
+
[Quick Start](#-quick-start--local-cli) · [Two paths](#-which-path-do-i-pick) · [Framework adapters](#-framework-agent-wrapping) · [Policy bundles](#-policy-bundles--compile-sign-enforce-wasm) · [User scope](#-user-scope--roles) · [Platform](#-hexgate-platform)
|
|
54
63
|
|
|
55
64
|
</div>
|
|
56
65
|
|
|
57
66
|
---
|
|
58
67
|
|
|
59
|
-
## What is
|
|
68
|
+
## What is Hexgate?
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
Hexgate is two things that move together:
|
|
62
71
|
|
|
63
72
|
- **`hexgate` — the SDK.** A Python runtime that gates every tool call through a typed `Decision` (allow / deny / approval-required), wraps your existing OpenAI / LangChain / Google ADK / Pydantic AI agent without rewriting it, and threads per-request user identity through tracing + audit.
|
|
64
|
-
- **The
|
|
73
|
+
- **The Hexgate platform** *(optional)* — a FastAPI control plane + React dashboard for editing policy in a browser, minting per-project tokens, watching live decisions stream from a serving agent, and shipping signed WASM policy bundles to production.
|
|
65
74
|
|
|
66
75
|
You can use the SDK with nothing else (single-process REPL, YAML on disk). Or plug in the platform when you want auditable decisions in ClickHouse, a shared Playground UI, and live policy edits.
|
|
67
76
|
|
|
@@ -79,7 +88,7 @@ You can use the SDK with nothing else (single-process REPL, YAML on disk). Or pl
|
|
|
79
88
|
┌────────────────┐ ┌──────────────────┐ ┌────────────────┐
|
|
80
89
|
│ Local policy │ │ Signed WASM │ │ Audit log │
|
|
81
90
|
│ (YAML / dir, │ │ bundle from │ │ (ClickHouse │
|
|
82
|
-
│ hot reload) │ │
|
|
91
|
+
│ hot reload) │ │ Hexgate cloud │ │ via REST) │
|
|
83
92
|
└────────────────┘ └──────────────────┘ └────────────────┘
|
|
84
93
|
```
|
|
85
94
|
|
|
@@ -103,7 +112,7 @@ You can use the SDK with nothing else (single-process REPL, YAML on disk). Or pl
|
|
|
103
112
|
- [Environment](#-environment)
|
|
104
113
|
- [Tests & dev tooling](#-tests--dev-tooling)
|
|
105
114
|
- [CLI reference](#-cli-reference)
|
|
106
|
-
- [
|
|
115
|
+
- [Hexgate platform](#-hexgate-platform)
|
|
107
116
|
- [User scope + roles](#-user-scope--roles)
|
|
108
117
|
- [Stream results](#-stream-results)
|
|
109
118
|
|
|
@@ -177,7 +186,7 @@ Both commands accept either a plain agent id (`--agent researcher`) or a uvicorn
|
|
|
177
186
|
|
|
178
187
|
## 🚀 Quick Start — Platform
|
|
179
188
|
|
|
180
|
-
To run the full
|
|
189
|
+
To run the full Hexgate control plane locally (FastAPI backend + dashboard + your local agent serving over WebSocket), you need **three terminals**. The Makefile has a target that prints the recipe:
|
|
181
190
|
|
|
182
191
|
```bash
|
|
183
192
|
make demo-platform # prints the 3-terminal recipe below
|
|
@@ -660,7 +669,7 @@ What happens under the hood:
|
|
|
660
669
|
|
|
661
670
|
Working scripts in `examples/`:
|
|
662
671
|
|
|
663
|
-
- `examples/customer_bot.py` — canonical
|
|
672
|
+
- `examples/customer_bot.py` — canonical Hexgate path: `create_agent(...)` + the dashboard register/serve loop end-to-end.
|
|
664
673
|
- `examples/openai_demo.py` — `HexgateRunner` (OpenAI Agents SDK) end-to-end.
|
|
665
674
|
- `examples/google_demo.py` — `HexgateRunner` (Google ADK) end-to-end with `InMemorySessionService`.
|
|
666
675
|
- `examples/pydantic_ai_demo.py` — `wrap_pydantic_agent` (Pydantic AI) end-to-end.
|
|
@@ -795,7 +804,7 @@ That means the same agent code can stay simple in development, while deployment
|
|
|
795
804
|
|
|
796
805
|
## 🧩 Policy Bundles — Compile, Sign, Enforce (WASM)
|
|
797
806
|
|
|
798
|
-
|
|
807
|
+
Hexgate has **two policy enforcement engines** that return identical decisions (there's a parity test suite that proves it):
|
|
799
808
|
|
|
800
809
|
- **pydantic** (default) — evaluates constraints in-process. Zero setup; this is what every example above uses.
|
|
801
810
|
- **WASM** — compiles `policy.yaml` → Rego → a WebAssembly module evaluated via `wasmtime`. This is the path production ships: one compiled artifact, byte-for-byte reproducible, cryptographically signed by the platform.
|
|
@@ -1131,7 +1140,7 @@ hexgate chat --use examples/research_agents.py --agent update_researcher --appro
|
|
|
1131
1140
|
|
|
1132
1141
|
### `hexgate register` — push a manifest to the platform
|
|
1133
1142
|
|
|
1134
|
-
Register a code-defined agent's manifest with the
|
|
1143
|
+
Register a code-defined agent's manifest with the Hexgate platform. `--agent`
|
|
1135
1144
|
takes a Python import path of the form `module.path:attribute`, the same shape
|
|
1136
1145
|
as ASGI/WSGI entrypoints. The CLI imports the module, grabs the agent object,
|
|
1137
1146
|
and POSTs its manifest to `${HEXGATE_API_URL}/v1/agents` using
|
|
@@ -1170,7 +1179,7 @@ system prompt directly off the object. No flags needed.
|
|
|
1170
1179
|
`--system-prompt` accepts either a literal string or a path to a `.md` /
|
|
1171
1180
|
`.txt` / `.jinja` file (read as text at register time).
|
|
1172
1181
|
|
|
1173
|
-
Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph,
|
|
1182
|
+
Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph, Hexgate agents.
|
|
1174
1183
|
|
|
1175
1184
|
### `hexgate serve` — bridge a local agent to the platform's relay
|
|
1176
1185
|
|
|
@@ -1207,7 +1216,7 @@ print(manifest.model_dump())
|
|
|
1207
1216
|
```
|
|
1208
1217
|
|
|
1209
1218
|
`create_manifest` dispatches on the framework of `agent`. The supported
|
|
1210
|
-
types are the same set `hexgate register` accepts:
|
|
1219
|
+
types are the same set `hexgate register` accepts: Hexgate, OpenAI Agents
|
|
1211
1220
|
SDK, Google ADK, Pydantic AI, and LangChain/LangGraph compiled graphs.
|
|
1212
1221
|
For LangGraph you must pass `tools=` explicitly, and may pass `model=` /
|
|
1213
1222
|
`system_prompt=`, since compiled graphs don't expose those fields after
|
|
@@ -1217,7 +1226,7 @@ The return value is an `AgentManifest` (a Pydantic model, also re-exported
|
|
|
1217
1226
|
from `hexgate` for type annotations) — the same schema the platform
|
|
1218
1227
|
stores and the dashboard renders.
|
|
1219
1228
|
|
|
1220
|
-
## 🌐
|
|
1229
|
+
## 🌐 Hexgate Platform
|
|
1221
1230
|
|
|
1222
1231
|
The `platform/` directory contains an optional control plane that hosts agent definitions, dev tokens, and a live debug surface. The SDK works fully without it (`load_local_agent`, `load_builtin_agent` keep their existing semantics) — but with it you get:
|
|
1223
1232
|
|
|
@@ -1336,7 +1345,7 @@ the name from the loaded agent's `.name` attribute — no env var needed.
|
|
|
1336
1345
|
|
|
1337
1346
|
## 👤 User Scope + Roles
|
|
1338
1347
|
|
|
1339
|
-
Real backends serve many users, and different users get different capabilities.
|
|
1348
|
+
Real backends serve many users, and different users get different capabilities. Hexgate splits that into two pieces:
|
|
1340
1349
|
|
|
1341
1350
|
- **`User`** — the per-request scope. Marks "this invocation acts on behalf of alice, in role X." Async context manager; pushes a fact-bearing Biscuit through the agent runtime.
|
|
1342
1351
|
- **Role policies** — one `policy.yaml` per role, optionally inheriting from a base mixin. The runtime picks the right one at call time based on the active `User.role`.
|
|
@@ -1486,3 +1495,7 @@ async for event in stream_agent(agent, handler, "latest AI breakthroughs"):
|
|
|
1486
1495
|
- assistant text deltas
|
|
1487
1496
|
- tool lifecycle
|
|
1488
1497
|
- final run completion
|
|
1498
|
+
|
|
1499
|
+
---
|
|
1500
|
+
|
|
1501
|
+
If Hexgate looks useful, [give it a ⭐ on GitHub](https://github.com/HexamindOrganisation/hexgate) — it helps more than you'd think. Built by [Hexamind](https://hexgate.ai).
|
|
@@ -9,8 +9,8 @@ build-backend = "setuptools.build_meta"
|
|
|
9
9
|
# 0.2.0 (the original package name was taken on PyPI by a 2014
|
|
10
10
|
# abandoned project; the team consolidated on `hexgate` for everything).
|
|
11
11
|
name = "hexgate"
|
|
12
|
-
version = "0.2.
|
|
13
|
-
description = "
|
|
12
|
+
version = "0.2.3"
|
|
13
|
+
description = "Hexgate — authorization infrastructure for AI agents (agent runtime + cloud client)."
|
|
14
14
|
readme = "README.md"
|
|
15
15
|
license = "MIT"
|
|
16
16
|
license-files = ["LICENSE"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|