Quickstart

Govern your first AI agent in under 2 minutes.

PhronEdge enforces constitutional governance at the agent boundary. Every tool call passes through 7 checkpoints before execution. Every decision is hash-chained and cryptographically provable. Every policy build maps to EU AI Act Articles 9, 14, and 15. Every governed call produces an Article 12 audit event.

Install

Shell
pip install phronedge

Requires Python 3.9 or higher. Works with any framework. No additional dependencies.

Current version: 2.4.6

Get an API key

  1. 1.Go to phronedge.com/brain and sign in
  2. 2.Open API Keys in the sidebar
  3. 3.Click Create Key
  4. 4.Copy the key. It starts with pe_live_ and is shown only once

Set your API key

Shell
export PHRONEDGE_API_KEY=pe_live_your_key_here

Never hardcode API keys. Use environment variables or your secrets manager.

How PhronEdge works

Your data stays in your runtime. PhronEdge validates governance decisions, not business data. Function arguments, return values, and model outputs never leave your infrastructure. The gateway sees the tool name, action, and jurisdiction.

Your policy is cryptographically signed. Every policy is ECDSA P-256 signed with your tenant's private key. The credential lives in your agent runtime. Export it as OPA Rego and run it independently. If PhronEdge is unreachable, your signed credential continues to govern.

Gateway latency under 50ms. No queue. No batch. No cold start. Millions of governed calls per day with the same latency as the first.

196 jurisdictions. 30 controls. 7 checkpoints. ECDSA P-256 signatures. SHA-256 hash-chained audit trail.

Govern your first tool

One decorator. Nothing else.

Python
from phronedge import PhronEdge

pe = PhronEdge()

@pe.govern("lookup_claim")
def lookup_claim(claim_id: str) -> str:
    """Look up an insurance claim by ID."""
    return db.query(claim_id)

# This call now passes through 7 governance checkpoints
# before the function body executes. Under 50ms.
result = lookup_claim("CLM-2026-001")

When lookup_claim is called, PhronEdge intercepts it, validates the agent credential against 7 checkpoints, and either allows or blocks the call. If allowed, the function runs normally. If blocked, the function body never executes.

Per-tool permissions

Each tool call specifies an action and a jurisdiction.

Python
pe = PhronEdge(agent_id="claims-investigator")

# Read-only access in Germany
@pe.govern("claim_lookup", action="read", jurisdiction="DE")
def claim_lookup(claim_id: str) -> dict:
    return claims_db.get(claim_id)

# Write access in Germany
@pe.govern("update_claim", action="write", jurisdiction="DE")
def update_claim(claim_id: str, data: dict) -> dict:
    return claims_db.update(claim_id, data)

Decorator parameters

ParameterTypeDefaultDescription
tool_namestrrequiredMust match a tool in your signed policy
actionstr"execute"read, write, delete, or execute
jurisdictionstrNoneISO alpha-2 code like DE, US, GB
mcpstrNoneMCP server URL for tool discovery
delegateslistNoneAgent IDs this call can delegate to

Jurisdiction enforcement

The allowed jurisdictions for each tool are defined when you sign the policy. The jurisdiction parameter in the decorator declares which jurisdiction this specific call operates in. The gateway checks it against the allowed list.

If your policy defines claim_lookup with jurisdictions: ["DE", "AT", "CH"]:

Python
pe = PhronEdge(agent_id="claims-investigator")

# Allowed: DE is in the policy list
@pe.govern("claim_lookup", action="read", jurisdiction="DE")
def lookup_de(claim_id):
    return claims_db.get(claim_id)

# Allowed: AT is also in the list
@pe.govern("claim_lookup", action="read", jurisdiction="AT")
def lookup_at(claim_id):
    return claims_db.get(claim_id)

# Blocked: CN is not in the list. Function never executes.
@pe.govern("claim_lookup", action="read", jurisdiction="CN")
def lookup_cn(claim_id):
    return claims_db.get(claim_id)

Data classifications

Every tool and every agent declares a data class. The gateway enforces that an agent's clearance matches or exceeds the tool's data class.

CodeLabelRegulatory anchor
PUBPublic APIs, open data. No restrictions.None
INTInternal company data. Not for external sharing.SOC 2, ISO 27001
PIINames, emails, addresses.GDPR Art. 4(1)
PHIPatient records, health data.HIPAA, GDPR Art. 9
SPCBiometrics, credit scores, criminal records.GDPR Art. 9 special categories
FINPayment data, bank accounts, transactions.PCI DSS, PSD2
CONTrade secrets, legal privilege. Need-to-know only.Trade Secrets Directive
RSTClassified, maximum protection. Regulatory approval required.National security frameworks

An agent with INT clearance cannot call a tool classified PII. The gateway blocks the call and cites the relevant regulation.

Multiple agents, one API key

Python
# Agent 1: fraud analyst
pe_fraud = PhronEdge(agent_id="fraud-analyst")

@pe_fraud.govern("transaction_review", action="read", jurisdiction="DE")
def review_transaction(txn_id: str) -> dict:
    return txn_db.get(txn_id)

# Agent 2: KYC verifier. Different tools. Different clearance. Different jurisdictions.
pe_kyc = PhronEdge(agent_id="agt-kyc-orch-v1")

@pe_kyc.govern("id_verify", action="read", jurisdiction="DE")
def verify_identity(doc_id: str) -> dict:
    return idv_service.verify(doc_id)

One API key. Each PhronEdge instance fetches the credential for its specific agent.

What happens on every call

The SDK intercepts before the function body executes. The gateway runs 7 checkpoints in under 50ms:

#CheckpointWhat it does
1Credential ValidatorECDSA P-256 signature, expiry, revocation cache
2PII DetectorScans input for personal data patterns
3Jurisdiction RouterMaps applicable laws, blocks disallowed jurisdictions
4Behavioral BaselineCompares current rate to anchored baseline
5JudgeRBAC plus ABAC plus contextual state. Permission and tier check
6Data ClassifierTags response classification
7Output ConstraintAccess-based restriction and redaction

ALLOW: Function executes, result returned, TOOL_CALL_ALLOWED event anchored to the audit chain.

BLOCK: Function never executes, regulation cited, TOOL_CALL_BLOCKED event anchored to the audit chain.

What a block looks like

When a tool call is blocked, the SDK returns structured data:

Python
from phronedge import PhronEdge

pe = PhronEdge(agent_id="claims-investigator")

@pe.govern("claim_lookup", action="read", jurisdiction="CN")
def claim_lookup_china(claim_id):
    return db.query(claim_id)

result = claim_lookup_china("CLM-001")
# result = {
#     "blocked": True,
#     "reason": "Jurisdiction 'CN' not permitted for tool 'claim_lookup'. Allowed: DE, AT, CH",
#     "checkpoint": "jurisdiction",
#     "regulation": "GDPR Art. 44-49",
#     "retry": True,
#     "message": "Tool call blocked by PhronEdge governance."
# }

The function body never executes. The block is anchored to your audit chain with the regulation that triggered it.

To raise exceptions instead of returning block dicts:

Python
from phronedge import PhronEdge, ToolBlocked

pe = PhronEdge(agent_id="claims-investigator", raise_on_block=True)

@pe.govern("claim_lookup", action="read", jurisdiction="CN")
def claim_lookup_china(claim_id):
    return db.query(claim_id)

try:
    result = claim_lookup_china("CLM-001")
except ToolBlocked as e:
    print(e.reason)       # "Jurisdiction 'CN' not permitted..."
    print(e.checkpoint)   # "jurisdiction"
    print(e.regulation)   # "GDPR Art. 44-49"
    print(e.blocked)      # True
    print(e.retry)        # True

ToolBlocked is the primary exception. GovernanceError is the base class and is also importable for broad catches.

What an allow looks like

Allowed calls return the function result normally. The Observer shows:

FieldValue
EventTOOL_CALL_ALLOWED
Agentclaims-investigator
Toolclaim_lookup
Actionread
CheckpointAll 7 passed
Hasha7f3b2c9...
Prev Hashd1e4f5a6...
RegulationGDPR Art. 5(1)(f), EU AI Act Art. 9

Every event is SHA-256 hashed and chained to the previous event. Tamper one and the chain breaks.

Sign a policy

Before your tools can be governed, you need a signed policy. Three paths, same outcome.

Console (recommended for CISOs and platform teams)

Go to phronedge.com/brain, open Policy Builder, walk through 3 steps:

  1. 1.Organization: HQ jurisdiction, industry, data types, residency, deployment jurisdictions
  2. 2.Agents and Tools: Each agent gets a tier (T0 to T3), data clearance, behavioral baseline, token budget. Each tool gets a data class, minimum tier, jurisdictions, permissions, and rate limits.
  3. 3.Organization Policy: Tenant-wide ceiling (allowed models, global deny patterns, auto-quarantine triggers, escalation rules)

Click Sign and Deploy. The Brain evaluates against all applicable regulatory frameworks for your jurisdiction and industry. If compliant, credentials are issued and events anchored. Open the Architecture view to see your signed policy as JSON, YAML, and OPA Rego.

CLI (recommended for CI/CD)

Shell
phronedge policy build policy.yaml     # preview only, no credentials issued
phronedge policy deploy policy.yaml    # sign and issue credentials
phronedge policy status                # show registered agents and tools

API (for automation)

Python
import os, requests

policy = {
    "organization": {
        "name": "Your Company",
        "jurisdiction": "DE",
        "industry": "IN",
        "data_types": ["PUB", "PII"],
        "data_residency": ["DE"],
        "deployment_jurisdictions": ["DE", "AT", "CH"],
    },
    "agents": [{
        "id": "claims-investigator",
        "purpose": "Process insurance claims",
        "model": "gpt-4o",
        "tier": "T2",
        "role": "standalone",
        "data_classifications": ["PUB", "PII"],
        "tools": ["claim_lookup", "report_generate"],
        "host_jurisdiction": "DE",
        "serving_jurisdictions": ["DE", "AT", "CH"],
    }],
    "tools": [
        {
            "id": "claim_lookup",
            "description": "Search claims by ID",
            "type": "sdk",
            "data_classification": "PII",
            "permissions": ["read"],
            "jurisdictions": ["DE", "AT", "CH"],
            "deny_patterns": ["DROP", "DELETE", "TRUNCATE"],
        },
        {
            "id": "report_generate",
            "description": "Generate compliance reports",
            "type": "sdk",
            "data_classification": "INT",
            "permissions": ["read", "write"],
            "jurisdictions": ["DE"],
        },
    ],
    "deploy": True,
}

r = requests.post(
    "https://api.phronedge.com/api/v1/governance/build",
    headers={
        "X-PhronEdge-Key": os.environ["PHRONEDGE_API_KEY"],
        "Content-Type": "application/json",
    },
    json=policy,
)

result = r.json()
print(result["status"])                           # "compliant"
print(result["signed_artifact"]["controls_met"])  # 30
print(result["credentials_issued"])               # [{"agent_id": "claims-investigator", ...}]

Important: Set "deploy": true to sign and issue credentials. Without it, the policy is signed for review only. Credentials are not persisted. Tool calls will fail with "No phronedge_signature in credential."

Required tool fields

Every tool must declare these fields. Missing fields are rejected at policy build time. No silent defaults.

FieldTypeRequiredDescription
idstryesMust match @pe.govern("id") in your code
descriptionstryesWhat this tool does
typestryessdk, api, or mcp
data_classificationstryesOne of PUB, INT, PII, PHI, SPC, FIN, CON, RST
permissionslistyesActions allowed: read, write, delete, execute
jurisdictionslistyesISO alpha-2 codes where this tool may be called
deny_patternslistnoInput patterns to block (e.g. ["DROP", "DELETE"])
max_per_dayintnoMaximum calls per day
requires_human_approvalboolnoBlock until human approval

A tool with data_classification: "PLI" (typo) is rejected. A tool with no jurisdictions is rejected. Fail-closed by design.

Export your policy as code

After signing, export your policy as OPA Rego, YAML, or JSON:

Shell
phronedge export rego --agent claims-investigator -o policy.rego
phronedge export yaml --agent claims-investigator -o policy.yaml
phronedge export json --agent claims-investigator -o policy.json

Or open Architecture view in the Console and switch between the JSON, YAML, and OPA tabs.

The Rego export is a complete OPA policy bundle with 9 checkpoint rules, denial reasons, and regulatory citations. Drop it into any OPA runtime for independent enforcement outside PhronEdge.

Verify your setup

Shell
phronedge verify --agent claims-investigator

Output:

PhronEdge Verify
==================================================

[+] API key: pe_live_xx******************xxxx
[+] Gateway: https://api.phronedge.com/api/v1

Testing gateway connection...
[+] Gateway reachable

Verifying agent: claims-investigator
[+] Credential valid
    Agent:        claims-investigator
    Tier:         T2
    Jurisdiction: DE
    Tools:        claim_lookup, report_generate
    Signed:       ES256 key=v2

Ready. Agent 'claims-investigator' is governed.

Scan your code

Confirm all tools in your codebase are governed:

Shell
phronedge scan my_agent.py

Output:

PhronEdge Scan: my_agent.py
==================================================

  [+] claim_lookup (as "claim_lookup")    line  12  governed
  [+] report_generate (as "report")       line  18  governed
  [x] send_email                          line  24  NOT governed

Total: 3 tools
  Governed:   2
  Ungoverned: 1

Ungoverned tools execute without governance.
Add @pe.govern("tool_name") to each one.

Use --strict in CI to fail the build on ungoverned tools:

Shell
phronedge scan src/agents/*.py --strict

Independent verification

Every signed policy can be verified by anyone, using only the public key. No PhronEdge involvement required. A regulator can run this script against your credential.

Python
import requests, json
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.exceptions import InvalidSignature

# 1. Fetch your tenant's public keys (no auth required)
tenant_id = "tn_your_tenant_id"
r = requests.get(f"https://api.phronedge.com/.well-known/phronedge/{tenant_id}/keys.json")
keys = r.json()["keys"]

# 2. Pick the key used to sign your credential
key_id = credential["phronedge_signature"]["key_id"]  # e.g. "v2"
active_key = next(k for k in keys if k["kid"] == key_id)
pub_key = serialization.load_pem_public_key(active_key["pem"].encode())

# 3. Reconstruct the signed payload
sig_hex = credential["phronedge_signature"]["value"]
payload = {k: v for k, v in credential.items() if k not in ("phronedge_signature", "anchor_hash", "anchor_tx")}
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")

# 4. Verify
try:
    pub_key.verify(bytes.fromhex(sig_hex), canonical, ec.ECDSA(hashes.SHA256()))
    print("Signature valid. Policy is authentic.")
except InvalidSignature:
    print("Signature invalid. Policy has been tampered with.")

The math proves authenticity. Zero trust in PhronEdge required.

Full runnable example

This script signs a policy, deploys credentials, runs governed tools, tests blocks, and verifies the chain. Copy it, set your API key, run it.

Python
"""
PhronEdge E2E: from zero to governed in one script.
pip install phronedge requests
"""
import os, json, logging, requests

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)-5s %(message)s",
    datefmt="%H:%M:%S",
)
log = logging.getLogger("phronedge.e2e")

API = os.environ.get("PHRONEDGE_GATEWAY_URL", "https://api.phronedge.com/api/v1")
KEY = os.environ["PHRONEDGE_API_KEY"]
H = {"X-PhronEdge-Key": KEY, "Content-Type": "application/json"}

# 1. Sign and deploy policy (credentials issued)
log.info("Signing and deploying policy...")
policy = {
    "organization": {
        "name": "Test Co",
        "jurisdiction": "DE",
        "industry": "IN",
        "data_types": ["PUB", "PII"],
        "data_residency": ["DE"],
        "deployment_jurisdictions": ["DE"],
    },
    "agents": [{
        "id": "test-agent",
        "purpose": "Test agent",
        "model": "gpt-4o",
        "tier": "T2",
        "role": "standalone",
        "data_classifications": ["PUB", "PII"],
        "tools": ["claim_lookup"],
        "host_jurisdiction": "DE",
        "serving_jurisdictions": ["DE"],
    }],
    "tools": [{
        "id": "claim_lookup",
        "description": "Search claims",
        "type": "sdk",
        "data_classification": "PII",
        "permissions": ["read"],
        "jurisdictions": ["DE"],
        "deny_patterns": ["DROP", "DELETE"],
    }],
    "deploy": True,
}
r = requests.post(f"{API}/governance/build", headers=H, json=policy, timeout=30)
log.info("Status: %s", r.json().get("status"))

# 2. Govern and call
from phronedge import PhronEdge, ToolBlocked

pe = PhronEdge(agent_id="test-agent", raise_on_block=True)

@pe.govern("claim_lookup", action="read", jurisdiction="DE")
def claim_lookup(claim_id):
    return json.dumps({"id": claim_id, "status": "OPEN", "amount": 12500})

log.info("ALLOWED: %s", claim_lookup("CLM-001"))

# 3. Test block: wrong jurisdiction
@pe.govern("claim_lookup", action="read", jurisdiction="CN")
def bad_jurisdiction(claim_id):
    return "SHOULD NOT RUN"

try:
    bad_jurisdiction("CLM-001")
except ToolBlocked as e:
    log.info("BLOCKED: %s (checkpoint=%s regulation=%s)", e.reason, e.checkpoint, e.regulation)

# 4. Test block: SQL injection pattern
@pe.govern("claim_lookup", action="read", jurisdiction="DE")
def bad_inject(query):
    return "SHOULD NOT RUN"

try:
    bad_inject("DROP TABLE claims")
except ToolBlocked as e:
    log.info("BLOCKED: %s", e.reason)

log.info("Done. Check phronedge.com/brain for the audit trail.")
Shell
export PHRONEDGE_API_KEY=pe_live_your_key_here
python quickstart_e2e.py

Expected output:

08:30:00 INFO  Signing and deploying policy...
08:30:01 INFO  Status: compliant
08:30:03 INFO  ALLOWED: {"id": "CLM-001", "status": "OPEN", "amount": 12500}
08:30:04 INFO  BLOCKED: Jurisdiction 'CN' not permitted... (checkpoint=jurisdiction regulation=GDPR Art. 44-49)
08:30:05 INFO  BLOCKED: Global deny pattern 'DROP TABLE' matched.
08:30:05 INFO  Done. Check phronedge.com/brain for the audit trail.

Next steps

Next
SDK Reference