Metadata-Version: 2.4
Name: upholdr
Version: 0.1.0
Summary: A governor for risky AI/agent tool calls with a tamper-evident audit trail. Upholdr governs tool calls; it never connects to SaaS systems.
Project-URL: Homepage, https://github.com/slimbiggins007/upholdr
Project-URL: Issues, https://github.com/slimbiggins007/upholdr/issues
Author: Jett Magnuson
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: agents,ai,audit,autonomy,governance,mcp,policy,security
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Systems Administration
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: pyyaml>=6.0
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Description-Content-Type: text/markdown

# Upholdr

**A governor for risky AI/agent tool calls — with a tamper-evident audit trail.**

> **Upholdr governs tool calls; it never connects to SaaS systems.**
> It is the control layer that decides what an agent may do automatically, what
> needs human approval, and what is only logged. It is *not* an integration / iPaaS
> platform — it never ships connectors and never talks to Shopify, Slack, HubSpot,
> and so on. You (or an aggregator like MCP / Composio / Pipedream) bring the
> tools; Upholdr decides what's allowed.

Upholdr gives an agent **graduated autonomy**: an action starts in `report_only`
(logged, never executed), and a *category* of action earns its way up the ladder to
`suggest` and finally `auto` only after it accumulates clean, logged evidence —
**per risk category, never globally.** Novel or high-risk actions are escalated even
inside an otherwise-automated category, and the engine **fails closed** when data is
missing or a tool is unknown. The audit trail is the product — a SQLite ledger
sealed by a tamper-evident hash chain.

## How it compares

- **vs OPA / Cedar** — those are stateless-per-decision policy engines (allow/deny
  now). Upholdr adds the *temporal* layer they don't model: autonomy earned across
  sessions, promotion/demotion evidence, and a reproducible ledger. It can sit
  *alongside* a policy engine, not replace it.
- **vs LangSmith / Langfuse** — those *observe* agent runs; Upholdr *gates* them.
- **vs human-in-the-loop libraries** — those ask forever; Upholdr's point is to ask
  *less* over time, with receipts to justify it.

## What's in the box

- **Earned autonomy, per category.** Actions climb `report_only → suggest → auto`
  on clean, logged evidence; novel or high-risk calls escalate even inside an `auto`
  category (the "auto ceiling"). Reaching `auto` requires *routine* clean evidence —
  elevated categories can earn `suggest` but never auto-execute.
- **Deterministic replay.** `ordered action log + policy → decisions + final ladder
  state`, reproducible run-to-run. The same derivation drives the live path, the
  CLI, and the report — one source of truth.
- **Operator overrides with cooldown.** `reject` / `reverse` demote a category and
  let it re-earn trust over a cooldown instead of locking it forever; overrides are
  recorded in the ledger and feed the same derived ladder.
- **Tamper-evident ledger.** A SHA-256 hash chain over a single append sequence,
  append-only SQLite triggers, and `upholdr verify` — receipts you can check.
- **Pluggable gate-packs, selected by policy.** `access_data` (reversibility,
  exposure, blast-radius, data-confidence, evidence-layer novelty) is the default;
  `pack: refunds` scores refund amount — proof the engine isn't domain-specific.
- **Operator surface.** `upholdr status` / `upholdr categories`, and a static HTML
  `upholdr report` showing promotion-readiness, recent decisions, overrides, and
  ledger-verify status.
- **Live MCP governance.** `Governor.govern()` routes each call at its earned level;
  an SDK-free MCP proxy core forwards a tool call *only* when the verdict is
  `executed_auto`, returning "approval required" for the rest.

## Quickstart

```bash
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"          # add the proxy extra with: pip install -e ".[dev,mcp]"

# Evaluate one action against a policy and append a sealed ledger row:
upholdr evaluate \
  --file examples/access_actions.jsonl \
  --policy examples/policies/access_data.policy.yaml \
  --db ./upholdr.db

# Verify the ledger hash chain (exits non-zero on tampering):
upholdr verify --db ./upholdr.db

# Watch one category climb report_only -> suggest -> auto (deterministic):
upholdr replay examples/ladder_actions.jsonl \
  --policy examples/policies/tiny_ladder.policy.yaml

# Same engine, a different domain — a $9,500 refund escalates even at auto:
upholdr replay examples/refunds/actions.jsonl \
  --policy examples/refunds/policy.yaml

# Render the static operator report:
upholdr report --db ./upholdr.db \
  --policy examples/policies/access_data.policy.yaml \
  --out report.html

pytest -q
```

A known tool resolves to `reported_only` (a new category starts at the bottom of the
ladder); an **unknown tool fails closed** to `escalated`. `upholdr evaluate` writes a
sealed ledger row; `upholdr replay` derives ladder state from the ordered log.

## The tool descriptor registry

Upholdr judges risk from *declared* metadata about each tool — never by calling it.
See [`examples/policies/access_data.policy.yaml`](examples/policies/access_data.policy.yaml):

```yaml
tools:
  hubspot.export_contacts:
    direction: read
    surface: internal
    data_class: customer_pii
    default_risk: high
```

`data_class` is **declared by you, not detected** — mislabeling it is a governance
gap you own. An unregistered tool is treated as worst-case and fails closed. A policy
may select a domain gate-pack with `pack:` (default `access_data`; the refunds recipe
uses `pack: refunds`). The MCP proxy SDK is an optional extra: `pip install "upholdr[mcp]"`.

## License & security

Apache-2.0 ([`LICENSE`](LICENSE)). Security policy: [`SECURITY.md`](SECURITY.md).
The ledger is tamper-*evident*, not tamper-*proof* — see the threat model in
[`docs/threat-model.md`](docs/threat-model.md).
