# ODIN HEL Policy Engine

Lightweight, embeddable **policy engine** for governing AI egress (URLs/hosts/methods) with:
- **Profiles**: `strict`, `medium`, `open`
- **Allowlist**: per-tenant overrides
- **Optional Rego (OPA)** evaluation when `rego_path` provided and the `opa` binary is available

Install:
```bash
pip install odin-hel
```


## Quick Start
```python
from odin_hel import PolicyEngine, Decision

# Create a PolicyEngine with the default 'medium' profile
engine = PolicyEngine(profile="medium")

# Prepare a context for evaluation (simulate an API call)
ctx = {
  "tenant_id": "acme",
  "forward_url": "https://api.openai.com/v1/chat/completions",
  "method": "POST",
  "headers": {"authorization": "Bearer ..."},
  "payload_cid": "sha256:...",
  "attrs": {"user": "alice"}
}

# Evaluate the context
decision = engine.evaluate(ctx)
print(decision)
assert decision.allow

# Use a strict profile (deny by default)
engine_strict = PolicyEngine(profile="strict")
decision_strict = engine_strict.evaluate(ctx)
print(decision_strict)

# Use a custom allowlist
engine_custom = PolicyEngine(profile="strict", allowlist=["api.openai.com"])
decision_custom = engine_custom.evaluate(ctx)
print(decision_custom)
```

---

## FAQ: Common Errors

**Q: I get `ModuleNotFoundError: No module named 'odin_hel'` when running tests.**
A: Make sure your `PYTHONPATH` includes the `src/` directory, e.g.:
```bash
export PYTHONPATH=./data/odin-hel/src
pytest
```
Or on Windows PowerShell:
```powershell
$env:PYTHONPATH = 'data/odin-hel/src'; python -m pytest
```

**Q: Why does my context get rejected with `reason='no_host'`?**
A: The context must include either a `host` or a valid `forward_url` field. If both are missing or invalid, the engine cannot determine the target host.

**Q: How do I add a custom allowlist?**
A: Pass the `allowlist` argument to `PolicyEngine`, e.g. `PolicyEngine(profile="strict", allowlist=["api.example.com"])`.

### Profiles

- **strict**: deny by default, only explicit allowlist permits
- **medium**: curated defaults for common AI API hosts + allowlist
- **open**: allow all (useful for local/dev)

### Rego (OPA) optional

If you pass `rego_path="policies/egress.rego"` and an `opa` binary is on PATH, the engine will evaluate `data.odin.allow` against the provided context and return that decision. If OPA or the policy is unavailable, the engine falls back to the profile/allowlist result (fail-closed when explicitly configured).

---

## VS Code + Copilot

**Recommended tasks**:
- Run tests: `pytest`
- Add or refine allowlist defaults under `profiles.py`

**Copilot prompts**:

> *“Extend domain matching to support wildcards like `*.example.com` and add unit tests.”*

> *“Add reason codes and structured details when a host is rejected (e.g., policy=profile_denied, match=none).”*

> *“Implement a sample Rego policy and a smoke-test that is skipped when `opa` is not installed.”*

---

## Publishing

Use the included GitHub Actions workflow to publish on tag push (set `PYPI_API_TOKEN` in repo secrets).

---

## License

Apache-2.0
