Metadata-Version: 2.4
Name: ollama-openttt
Version: 0.1.0
Summary: TTTPS Proof-of-Time reverse proxy for Ollama: cryptographic audit-trail timestamps for local model responses via the public KPP Provenance API
License: MIT
Project-URL: Homepage, https://github.com/Helm-Protocol/OpenTTT
Project-URL: Repository, https://github.com/Helm-Protocol/OpenTTT
Project-URL: Issues, https://github.com/Helm-Protocol/OpenTTT/issues
Keywords: ollama,proof-of-time,provenance,temporal-attestation,audit-trail,llm-observability,reverse-proxy
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.24
Requires-Dist: fastapi>=0.100
Requires-Dist: uvicorn>=0.23

# ollama-openttt

TTTPS Proof-of-Time reverse proxy for [Ollama](https://github.com/ollama/ollama).
Sits in front of `ollama serve` and attaches a cryptographic audit-trail timestamp
(a signed Proof-of-Time receipt) to every `/api/generate` and `/api/chat` response,
sealed against the public self-serve [KPP Provenance API](https://kpp.kenosian.com).

This attaches a cryptographic audit-trail timestamp and integrity hash. It does
**not** certify legal or regulatory compliance (EU AI Act, FDA, and so on).
Treat it as an audit-trail timestamp, not a compliance claim.

## Install

```bash
pip install ollama-openttt
```

## Usage

Mint a free key with `POST https://kpp.kenosian.com/v1/keys`, then start the proxy
next to your running Ollama daemon:

```bash
export KPP_API_KEY=...

ollama serve &            # upstream, port 11434
ollama-openttt --port 11435
```

`python -m ollama_openttt` does the same thing.

Point any Ollama client at the proxy port instead of the daemon port. Requests and
responses are otherwise identical, so existing clients need no other change:

```bash
curl http://127.0.0.1:11435/api/chat -d '{
  "model": "qwen2.5:0.5b",
  "messages": [{"role": "user", "content": "hi"}],
  "stream": false
}'
```

The response carries an extra top-level key:

```json
{
  "model": "qwen2.5:0.5b",
  "message": {"role": "assistant", "content": "Hello! How can I help you today?"},
  "done": true,
  "tttps_receipt": {
    "status": "ok",
    "content_hash": "sha256:...",
    "receipt_id": "...",
    "receipt": "...",
    "time": "...",
    "time_source": "...",
    "verify_url": "https://kpp.kenosian.com/v1/verify?receipt_id=...",
    "overhead_ms": "<round trip spent on the anchor call>"
  }
}
```

Non-streaming responses also expose `X-TTTPS-Status` and `X-TTTPS-Receipt` headers.
A receipt can be re-checked at any time via `POST https://kpp.kenosian.com/v1/verify`
with `{"receipt_id": ...}`.

## Streaming

Ollama streams NDJSON by default. Every delta line is forwarded through unchanged
and immediately, and only the final `"done": true` line gets the `tttps_receipt`
key added, computed over the accumulated text. The anchor round trip is paid once,
after generation has already finished, so token-by-token delivery is never delayed,
and any parser that only reads the known keys is unaffected by the extra one.

## Configuration

| Variable | Default | Meaning |
| --- | --- | --- |
| `KPP_API_KEY` | empty | Provenance key from `POST /v1/keys` |
| `KPP_BASE` | `https://kpp.kenosian.com` | Provenance API base URL |
| `KPP_TIMEOUT_S` | `1.0` | Fail-open deadline for the anchor call |
| `OLLAMA_UPSTREAM` | `http://127.0.0.1:11434` | Ollama daemon base URL |
| `OLLAMA_TIMEOUT_S` | `120.0` | Deadline for the upstream Ollama call |

CLI flags: `--host`, `--port`, `--upstream`, `--log-level`.
`GET /health` reports whether the upstream daemon is reachable.

## Fail-open

A missing key, an unreachable or slow Provenance API, or any unexpected error
degrades the receipt to `{"status": "degraded", "reason": ...}`. The underlying
Ollama response is never blocked or altered.

## Why a proxy

Ollama has no plugin or middleware extension point: its HTTP router is built with a
fixed pair of middlewares and is not extensible from outside the binary. A reverse
proxy is therefore the way to attach anything to an Ollama response, which is the
pattern other Ollama proxies use as well.

## License

MIT
