Skip to content

Releases: octavuntila-prog/BIJOTEL

v2.16.0 — append_event: seal non-span events into the chain

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 15 Jun 06:42

Adds bijotel.append_event(db_path, secret_key, event, ...) — a public primitive that seals an arbitrary JSON-canonicalizable dict into the same tamper-evident HMAC chain HmacChainSpanProcessor writes, without an OpenTelemetry span. Non-span consumers (e.g. an external audit framework with its own event model) can now back their chain with bijotel and inherit verify_chain, signed Ed25519 export, Rekor anchoring, and federation for free.

Parity is structural, not just tested. The row-sealing critical section and schema init are now single shared module functions (_seal_canonical, _init_chain_db) called by BOTH on_end (span path) and append_event (event path) — one write path, so a span row and an event row are byte-identical chain links. Proven by the mixed-chain test: interleaved span + event rows -> verify_chain VALID + chain-linked across the source boundary (runs on all platforms).

994 existing tests unchanged (the span path is behaviorally identical) + 6 new. Released end-to-end by CI (test -> PyPI -> ghcr multi-tag), no manual steps.

v2.15.1 — Release pipeline proven end-to-end (no runtime changes)

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 12 Jun 07:34

Test/CI-only patch — no library or runtime changes; production on 2.15.0 needs no redeploy.

Fixed

  • Stale test contract that blocked the v2.15.0 release run: test_existing_chain_db_perms_preserved still asserted the pre-2.15.0 contract (existing chain.db perms preserved), while ISSUE-15 deliberately changed it to idempotent chmod 0o600 on every open. The test now asserts the new contract explicitly (644 → open → 600), renamed test_existing_chain_db_perms_tightened_to_0600.

CI

  • GitHub Actions bumped ahead of the Node 24 forcing (2026-06-16): actions/checkout v4→v5, actions/setup-python v5→v6.
  • This release is cut end-to-end by the tag-triggered workflow (test gate → PyPI), no manual upload — closing the process gap behind audit M1/M2.

v2.15.0 — Security hardening (audit remediation)

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 08 Jun 20:41

Remediation of the 2026-06-08 technical audit (shipped-library P0/P1 findings). Every fix carries a regression test that failed before the change. 994 tests pass; ruff clean. HMAC chain core, JCS canonicalization, Ed25519/ECDSA crypto and multi-writer concurrency were audited and found sound.

  • ISSUE-3: cross-anchor receipt verify no longer fail-open — requires an out-of-band federation key; CLI federation verify --federation-key now required.
  • ISSUE-5: AST python checker fails closed on SyntaxError (emits unparseable_python warning).
  • ISSUE-4: ReDoS removed in override/reverse jailbreak regexes + 16KB scan cap.
  • ISSUE-8: library verify_export requires a trust anchor (HMAC secret or Ed25519 pubkey).
  • SP-1: new IsolatingSpanProcessor — a third-party SpanProcessor can no longer abort the dispatch and skip the HMAC seal.
  • ISSUE-15: idempotent 0600 chmod-on-open for the chain DB.
  • ISSUE-13/14: python-multipart>=0.0.18 (CVE-2024-24762 / CVE-2024-53981); lock header.

PyPI: https://pypi.org/project/bijotel/2.15.0/
Full notes: CHANGELOG.md

v2.14.1 — fix: EnergySpanProcessor broke chain sealing on a live TracerProvider

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 05 Jun 13:44

Fixed: EnergySpanProcessor now subclasses opentelemetry.sdk.trace.SpanProcessor. It was duck-typed and lacked _on_ending (called by OTel SDK ≥1.42 on span end) — registering it on a live TracerProvider raised AttributeError and broke chain sealing (2026-06-05 incident). Subclassing inherits the full protocol incl. future hooks.

New tests/test_span_processor_protocol.py: real TracerProvider start→end over ALL processors (red-first validated). Suite 983 passed.

v2.14.0 — cross-view REST + idempotent Rekor 409 + energy live-cutover guard

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 05 Jun 12:33

Added: POST /cross-view (bijotel cross-view CLI over serve; per-ecosystem stats + structural integrity; not federation). bijotel energy mark-live + DB-stored live_cutover_seq so backfill never double-counts live rows.

Fixed: Rekor HTTP 409 (entry already exists) is now an idempotent success in anchor publish (typed RekorEntryExistsError; non-409 still fails hard).

Suite 979 passed, ruff clean. Full notes: CHANGELOG.md.

v2.13.3 — MCP invocations sealed by the default processor

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 02 Jun 14:29

MCP invocations now sealed by the default processor

The default HmacChainSpanProcessor filter kept only gen_ai.* spans, so MCP
tool invocations (bijotel.mcp.* attributes emitted by MCPInstrumentor) were
silently dropped — the v2.12 "sealed by the existing processor" claim only
held if the host passed a custom filter_fn. The 18 mocked MCP unit tests never
verified a real sealed call, so this never surfaced (same class of gap as the
2.13.2 Rekor fix).

Fixed

  • _default_filter now also keeps bijotel.mcp.* spans → MCP invocations seal out-of-the-box.

Added

  • Real (non-mocked) e2e test: in-memory MCP server + client, real call_tool, sealed by the default processor.
  • _default_filter regression-guard unit test.

No impact on existing GENA/ARA deployments (neither instruments an MCP client).

v2.13.2 — Rekor anchoring live-interop fix (ECDSA P-256)

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 02 Jun 11:58

Fixes Rekor anchoring against the live Sigstore Rekor. Anchors were signed with pure Ed25519, but Rekor verifies Ed25519 hashedrekord entries via Ed25519ph (unsupported by Python's cryptography) — so every live submission failed with 'ed25519: invalid signature' (the 15 unit tests mocked Rekor and missed it). Anchoring now signs with ECDSA P-256 over a SHA-256 digest (Rekor's canonical hashedrekord path). Adds bijotel.crypto.ecdsa_p256, 'bijotel keygen --type ecdsa', and a non-mocked live Rekor test (BIJOTEL_REKOR_LIVE=1). Verified end-to-end against rekor.sigstore.dev. Ed25519 signed-exports are unchanged.

v2.13.1 — verify_chain reports last_seq

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 02 Jun 09:28

[2.13.1] — 2026-06-02 — verify_chain reports last_seq

Fixed

  • verify_chain(...) now reports the last verified seq in its
    success tuple (True, last_seq, None). Previously the success path
    always returned (True, None, None) regardless of how many rows were
    verified — confusing when callers wanted to know how far the verify
    reached. The valid flag was always authoritative and is unchanged;
    this only populates the previously-None middle element.
    • Full-chain verify: last_seq = seq of the last row in the chain.
    • Range / last_n verify: last_seq = last seq in the window.
    • Empty chain: last_seq stays None (nothing verified) — documented.
    • Surfaced during the stabilization freeze (ARA Day-4 check showed
      (valid=True, last_seq=None) on a full-chain verify).

Notes

  • Behavior change is limited to the middle element of the return tuple
    on the success path. Failure path (False, seq, reason) unchanged.
    Tests updated to assert the real last seq; 1 new empty-chain test.

v2.13.0 — Cross-ecosystem view

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 27 May 09:27

[2.13.0] — 2026-05-27 — Cross-ecosystem view

For operators with more than one BIJOTEL-instrumented ecosystem
(GENA + ARA + Gen4, or production + staging), the new
CrossEcosystemView aggregates totals, provider union, and
per-chain integrity in one read-only view. No chain merging —
each chain keeps its own HMAC secret, signing key, and Rekor
anchor.

Added

  • bijotel.cross_view module:
    • CrossEcosystemView class — add N chains by local DB path or
      pre-exported JSON, get unified summary() dict and
      integrity_report().
    • ChainStats dataclass — per-chain snapshot (entries, providers,
      models, first/last timestamps).
    • load_chain_stats_from_db(name, db_path) — read SQLite chain.db.
    • load_chain_stats_from_export(name, export_path) — read v2 export
      JSON.
  • bijotel cross-view CLI subcommand: --chain NAME=PATH
    (repeatable, mix DB and export files), --json, --integrity.
  • 20 new tests in tests/test_cross_view.py covering loaders,
    summary, integrity report, CLI human + JSON paths, edge cases
    (empty, single chain, duplicate names, mixed sources).
  • Public API: CrossEcosystemView, ChainStats promoted to
    top-level bijotel.* namespace.
  • Docs: docs/guides/cross-ecosystem.md (usage + CLI + what it
    proves / does not prove).

Honest scope

  • The view is read-only and observational. It does not validate
    cross-chain causality, does not merge chains, does not propose
    trust beyond what each chain establishes individually.
  • The structural integrity check (when no HMAC secret is provided)
    is weaker than a full bijotel verify — it only confirms the
    chain is non-empty and parseable. For real HMAC integrity, pass
    the secret via integrity_report({"name": secret_bytes}).
  • Mixed DB + export sources work, but exports lose the HMAC chain
    (the export format strips raw prev_hash linkage by design);
    integrity for export-sourced chains is structural only.

v2.12.0 — MCP invocation sealing

Choose a tag to compare

@octavuntila-prog octavuntila-prog released this 27 May 08:12

[2.12.0] — 2026-05-27 — MCP invocation sealing

First non-LLM seal target: Model Context Protocol (MCP) tool invocations
now land in the same HMAC chain alongside LLM calls. Motivated by 40+
CVEs filed against MCP servers in Jan-Apr 2026 and NSA CSI (May 2026)
recommending audit logging for MCP — bijotel-mcp adds the cryptographic
integrity layer NSA stops short of specifying.

Added

  • bijotel.mcp module:
    • MCPInstrumentor — monkey-patches mcp.ClientSession.call_tool so
      every async tool invocation emits an OTel span carrying
      bijotel.mcp.* attributes. Idempotent; raises a clean
      ImportError if the MCP SDK isn't installed.
    • mcp_invocation_context(...) — public helper that builds the
      attribute dict for callers who emit MCP spans manually (e.g. from
      a non-stdlib MCP client).
    • MCP_ATTRS — authoritative attribute vocabulary (10 keys covering
      server identity, tool, input/output hashes, status, transport,
      timing).
  • MCP attribute vocabulary under the bijotel.mcp.* namespace:
    server_name, server_version, tool_name, tool_input_hash (SHA-256 hex),
    tool_output_hash, caller, duration_ms, status, error_type, transport.
  • Hash-only content capture: tool inputs/outputs are SHA-256 hashed
    before sealing, never stored raw. Preserves forensic value (verify
    against a known-good blob later) without exposing potentially
    sensitive content (file paths, credentials, prompts) in the chain.
  • Optional dependency: pip install bijotel[mcp] pulls in the MCP
    SDK. The base install does NOT require MCP — bijotel.mcp module
    imports cleanly, only .instrument() needs the SDK.
  • Design doc: docs/design/bijotel-mcp.md covers attribute
    vocabulary, 3 integration patterns (in-process, proxy, Go collector),
    threat model, and explicit out-of-scope items.
  • 18 new tests in tests/test_mcp_invocation.py: hash determinism,
    attribute completeness, instrumentor idempotency, success/error path
    span emission. Uses an in-tree mcp module stub so tests are hermetic.

Honest scope

  • v2.12.0 covers integration pattern A (in-process Python
    instrumentor) only. Pattern B (bijotel-mcp-proxy for non-Python
    servers) and pattern C (bijotel-collector extension) are documented
    but not implemented.
  • bijotel-mcp seals invocations; it does not gate them. Policy
    enforcement on MCP tool calls (e.g. "deny write_file for user X")
    is separate PolicyEngine work.
  • No production MCP traffic was sealed at release time — pattern A is
    built and tested against a stub, real deployment validation
    follows once an MCP-consuming agent is running on GENA or ARA.

Trust hierarchy after v2.12.0

Layer Mechanism Scope
L1 HMAC chain LLM calls (existing)
L2 + MCP attrs LLM + MCP tool invocations (new)
L3 Ed25519 export external auditors
L4 Rekor anchor public transparency log
L5 TEE attestation producing software
L6 Federation cross-org co-signing