Skip to main content

cendor-acttrace

A tamper-evident, append-only record of every AI decision — what model, what context, what it cost, which tools, and who signed off — mapped to control templates and exportable as an evidence pack. No database, no infra: integrity comes from a hash chain, not a server.

Detect and prove: 20 categories of secrets, PII & special-category data — block / warn / redact in one line, offline — every decision in a hash chain you can verify without a server.

PyPI license · pip install cendor-acttrace

Using an AI coding assistant? npx @cendor/init (TS) / uvx cendor-init (Python) wires it up — or point it at cendor.ai/docs/for-ai-assistants.

from cendor.core import instrument
from cendor.acttrace import AuditLog

client = instrument(OpenAI())
audit = AuditLog(system="loan_triage", risk_tier="high", signing_key="…")  # auto-subscribes

with audit.decision(input=application, actor="agent") as d:
    resp = client.chat.completions.create(model="gpt-4o", messages=msgs)  # auto-logged
    d.record(model="gpt-4o", prompt_id="triage@v3")          # cost/context captured for free
    d.human_oversight(reviewer="ops@bank", action="approved")

audit.export("evidence_q3.jsonl", framework="eu_ai_act")     # evidence pack (also nist_rmf)
acttrace verify evidence_q3.jsonl --key "…"   # re-walks the chain + checks signatures; non-zero if broken

Every mirrored entry names its agent (1.13.0)

OTelMirror used to stamp cendor.audit.agent only on a guardrail_decision — the one entry type whose payload carries an agent. Measured against Cendor Monitor: 13 of 386 governance rows named their agent, so "which agent was blocked" was answerable only by inferring it from step ordering. Since 1.13.0 the mirror reads the acting agent (and its id) from cendor-core's ambient registry and stamps cendor.audit.agent / cendor.audit.agent_id on every entry — including a budget block, a decision record, an llm_call. The entry's own payload always wins, and the hash-chained evidence file is untouched: this is the operational copy.

The mirror attaches itself (1.11.0)

With OpenTelemetry installed and a provider configured by your app, AuditLog(system="support", path="audit.jsonl") also streams every chained entry to your backend as an audit.<type> span — no mirror= line. Pass mirror=False to never mirror a log, your own sink to use exactly that one, or set CENDOR_TELEMETRY=off to stop all Cendor telemetry. The mirror is an operational copy: the hash-chained file (or a signed export() pack) stays the only artifact verify() checks, and a failing mirror is swallowed rather than breaking the chain.

Highlights

  • Offline detection engine + policy — a validator-gated Detector registry spanning secrets, PII, financial, government-ID, free-text credentials, and GDPR special-category data (20 categories), plus a Policy that maps each to allow · flag · redact · block. Regex + local checksums (Luhn / IBAN mod-97 / Verhoeff / ABA) — no model, no network, no account. Presets: Policy.default() / gdpr() / pci() / strict().
from cendor.acttrace import scan, redact, Policy

scan("card 4111 1111 1111 1111 for alice@example.com")     # -> [Finding(credit_card…), Finding(email…)]
cleaned, findings = redact({"note": "ping alice@example.com"}, Policy.default())
# cleaned == {"note": "ping <redacted>"} — findings report counts, never the raw value

audit = AuditLog(system="triage", policy=Policy.gdpr())    # special-category → block, PII → redact
  • Enforce + record in one lineguard(policy, audit=…) returns a dual-shape interceptor (1.5.0): install it on core's seam yourself, or use it as a context manager (with guard(...):) that installs/removes itself around the block. It enforces the policy (block a disallowed call before it runs, redact-before-send, or flag) and acttrace records the decision as a tamper-evident policy_flag. Recorder and enforcer stay separate — core is what stops the call. resolve_findings() exports the per-category action resolution for composers.
from cendor.core.instrument import add_interceptor
from cendor.acttrace import AuditLog, Policy, guard, PolicyViolation

audit = AuditLog(system="support_bot", risk_tier="high", signing_key="ops-key")
add_interceptor(guard(Policy.gdpr(), audit=audit))         # a special-category call is blocked + recorded
  • Auto-populating — construct an AuditLog and it subscribes to the bus: every LLM/tool call, plus cost (tokenguard) and context decisions (contextkit) on the same stream, becomes an entry — no per-call wiring.
  • Bounded memory for long-running logsAuditLog(path="audit.jsonl", max_entries=N) caps the in-memory entry ring so a multi-day agent doesn't grow memory per event. The file stays the complete, verifiable chain (verify()/export() read it), evicted_from_memory counts what left memory, and the default (None) is unbounded. Bound together with path=.
  • Tamper-evident hash chainverify() catches edits, reordering, and tail-truncation. The pack's _meta head+count catch truncation, but that header is only authenticated when the log is HMAC-signed and you verify(key=…) — the header itself is signed, so a rewritten _meta fails. Without a key it's an unauthenticated in-file check, so pass an out-of-band expected_head= (captured from log.head at write time) for an authoritative completeness guarantee. Each entry is optionally HMAC-signed too.
  • Decisions & oversightdecision() groups a unit of work; d.record(...) and d.human_oversight(reviewer, action) capture Art. 14-style sign-off.
  • Compliance evidence packsexport(framework=…) annotates control IDs for EU AI Act, ISO/IEC 42001, GDPR, and NIST AI RMF (starting templates, not certified mappings), and a _meta.summary (counts of decisions, oversight, flags by action/severity) gives a reviewer the at-a-glance read first. PII redaction on by default (swap in redactor=).
  • Opt-in extras, defaults unchangedenable_locale_pack("uk", "in") (UK NINO, India Aadhaar — Verhoeff-checked), enable_entropy_detector() (high-entropy generic secrets — noisy), and NER-backed name/address redaction via pip install "cendor-acttrace[ner]" (Microsoft Presidio, still offline). All strictly opt-in — the zero-extra install stays pure-regex.
  • Auto-flag by policy — every auto-captured entry is scanned against the full registry, and each detection appends a policy_flag with its resolved action (redacted / flagged / blocked), severity, and category — so "we removed / flagged / blocked this" is in the hash chain, not silent (flag_on_redact=True by default; a custom redactor= owns its own flagging). Detection scrubs the record; block on core's interceptor seam is the pre-send control.
  • Policy flags (validation)audit.flag(reason, action="blocked", …) records a tamper-evident policy_flag (and returns the chained entry) when your pre-flight guard refuses input that shouldn't be processed — so the refusal is auditable, not just the calls that ran:
from cendor.core.instrument import add_interceptor, MISS

def guard(call):                                    # your pre-flight policy guard
    if my_policy_disallows(call):                   # YOUR rule
        audit.flag("special-category data", action="blocked")   # acttrace records the refusal
        raise PolicyViolation("blocked")            # your guard enforces it
    return MISS

add_interceptor(guard)   # the blocked call never reaches the bus — flag() is its only record

Produces evidence to support compliance — not legal advice, not a guarantee. Control mappings are starting templates for your compliance team.

See docs/acttrace.md · CHANGELOG. Part of the Cendor stack — github.com/cendorhq/cendor-libs. Powered by PowerAI Labs. Apache-2.0; provided "as is", without warranty — use at your own risk (LICENSE §7–8).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cendor_acttrace-1.13.0.tar.gz (79.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cendor_acttrace-1.13.0-py3-none-any.whl (52.7 kB view details)

Uploaded Python 3

File details

Details for the file cendor_acttrace-1.13.0.tar.gz.

File metadata

  • Download URL: cendor_acttrace-1.13.0.tar.gz
  • Upload date:
  • Size: 79.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for cendor_acttrace-1.13.0.tar.gz
Algorithm Hash digest
SHA256 aea6d77d580deed57a501952b9d1a7486e6fe68fa3cb35430cba0bf5ffa66ef2
MD5 409138164a4801ff798a8a45f0a4d18a
BLAKE2b-256 91dc3b153c59e6364f7003690f37d719070cbc1fb2d30c4614057d5271c44e16

See more details on using hashes here.

Provenance

The following attestation bundles were made for cendor_acttrace-1.13.0.tar.gz:

Publisher: release.yml on cendorhq/cendor-libs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cendor_acttrace-1.13.0-py3-none-any.whl.

File metadata

File hashes

Hashes for cendor_acttrace-1.13.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d6c3864ce9ef3a224e24689863cadf9a9496c82f438dfb0946d0d4456dc5e495
MD5 603db046f37cc1ddf0fa20183e633723
BLAKE2b-256 a1da93c801e6d18dcc4563b0f14c47cb3ad1da5acc1b972fc4684ed276598975

See more details on using hashes here.

Provenance

The following attestation bundles were made for cendor_acttrace-1.13.0-py3-none-any.whl:

Publisher: release.yml on cendorhq/cendor-libs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.14.0

2 files

1.13.1

2 files

This release

1.13.0 This release

2 files

1.12.0

2 files

1.11.0

2 files

1.10.1

2 files

1.10.0

2 files

1.9.0

2 files

1.8.0

2 files

1.7.0

2 files

1.6.0

2 files

1.5.0

2 files

1.4.2

2 files

1.4.1

2 files

1.4.0

2 files

1.3.0

2 files

1.2.2

2 files

1.2.1

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page