Metadata-Version: 2.4
Name: allela
Version: 0.1.2
Summary: Operational Flight Recorder for AI — tamper-evident decision attestation, independently timestamped (RFC 3161, DigiCert TSA)
License-Expression: Apache-2.0
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=41.0
Requires-Dist: asn1crypto>=1.5
Requires-Dist: rfc8785>=0.1.2
Provides-Extra: api
Requires-Dist: fastapi[standard]>=0.110; extra == "api"
Dynamic: license-file

# Allela SDK

Tamper-evident attestation for AI decisions, timestamped with an RFC 3161
token from DigiCert's timestamp authority.

## Quickstart

Create a project folder and a virtual environment, then install Allela.

macOS / Linux:

```bash
mkdir allela-quickstart && cd allela-quickstart
python3 -m venv .venv
source .venv/bin/activate
pip install allela
```

Windows (PowerShell):

```powershell
mkdir allela-quickstart; cd allela-quickstart
py -m venv .venv
.venv\Scripts\Activate.ps1
pip install allela
```

> **Windows note:** if `Activate.ps1` is blocked ("running scripts is
> disabled on this system"), run this once, then activate again:
>
> ```powershell
> Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
> ```

Save this as `quickstart.py`:

```python
import allela

allela.configure(
    tenant_id="quickstart",
    model_provider="anthropic",
    model_id="claude-sonnet-4-6",
    model_version="20250929",
)

prompt = "Applicant: income $60,000, requesting $10,000 loan."
answer = "APPROVED: Low risk profile."

# 1. Sign the record and get an RFC 3161 timestamp from DigiCert.
event_id = allela.attest(prompt, answer)

# 2. Look up what was stored.
event = allela.get_event(event_id)
print("event:    ", event_id)
print("status:   ", event["status"])  # "success" = signed and timestamped

# 3. Verify the record against the original input and output.
result = allela.reconcile(event_id, raw_input=prompt, raw_output=answer)
print("verified: ", result["path"])   # FULL_INTEGRITY_VERIFICATION
print("timestamp:", result["timestamp_imprint_matches"])  # True
```

Run it:

```bash
python quickstart.py
```

Expected output (your event ID will differ):

```text
event:     evt_...
status:    success
verified:  FULL_INTEGRITY_VERIFICATION
timestamp: True
```

The first run creates three files in the current folder:

- `allela_private.key`: your Ed25519 signing key. Keep it secret and
  never commit it.
- `allela_public.key`: the matching public key used to verify records.
- `allela_ledger.db`: the local, append-only record ledger.

Add `*.key` and `*.db` to your `.gitignore`. Timestamping needs outbound
HTTP access to `timestamp.digicert.com`; without it, records are still
signed and stored, with status `signed_no_tsa`.

## Using it in your app

Wrap your existing AI call with `@allela.trace` to attest every call:

```python
import allela

allela.configure(
    tenant_id="acme_corp",
    model_provider="anthropic",
    model_id="claude-sonnet-4-6",
    model_version="20250929",
)

@allela.trace(system_prompt="You are a credit risk assistant.")
def make_credit_decision(applicant_profile: str) -> str:
    return call_your_llm(applicant_profile)  # your existing AI call, unchanged

result = make_credit_decision("Applicant: income $60,000, requesting $10,000 loan.")
# `result` is returned immediately — attestation happens on a background
# thread and does not add latency to your function call.

# Before your process exits, flush any in-flight attestations:
allela.shutdown()
```

## Verifying a decision later

```python
result = allela.reconcile(
    event_id="evt_...",
    raw_input="Applicant: income $60,000, requesting $10,000 loan.",
    raw_output="APPROVED: Low risk profile.",
)
print(result["path"])
```

`result["path"]` is one of:

- `FULL_INTEGRITY_VERIFICATION`: the stored record is intact and your data matches it.
- `TAMPER_DETECTED`: the stored record is intact, but your data doesn't match it (`mismatched_fields` says which).
- `DATA_UNAVAILABLE_AT_SOURCE`: the stored record is intact; no data was supplied to compare.
- `LEDGER_RECORD_INVALID`: the stored record failed its signature or root-hash check (`failed_checks` says which), so it can't be trusted and nothing was compared.
- `NOT_FOUND`: no event with that ID.

`result["timestamp_imprint_matches"]` only confirms that the stored
timestamp token covers this record's hash. It does not yet verify the
timestamp authority's signature or certificate chain.

## What gets stored

Only cryptographic hashes and metadata (model ID/version, timestamps,
a hash of the system prompt) — never your raw input/output text. See
the Decision Event Schema (`v1/decision-event.json`) for the exact shape.

## Important: call `allela.shutdown()` before exiting

Because attestation runs on a background worker so it never blocks your
application, an attestation that's still in flight when your process
exits abruptly could be lost. Call `allela.shutdown()` (which flushes
the queue) during your application's graceful shutdown sequence.

`shutdown()` waits at most `timeout` seconds (default 30; pass `None` to
wait indefinitely) and returns `True` if every attestation was written,
or `False` if the timeout expired with some still pending — in which
case a warning is logged with the count.

## License

Apache License 2.0 — see `LICENSE`.
