Skip to content

Releases: SmooAI/audit

v0.4.1

Choose a tag to compare

@brentrager brentrager released this 24 Aug 21:25
f6b7d02

Patch Changes

  • 4e17d0e: Gate each registry's publish on that registry, not on a parsed stdout flag

    Every non-npm publish step was gated on steps.changesets.outputs.published, which changesets/action derives by parsing publish stdout. That fails open two ways, both seen for real on 2026-08-20: if npm published in an earlier run, a retry finds nothing new so the flag is false and all four remaining registries skip — a green run that ships nothing; and @changesets/cli 3.x renamed the very line the action parses, which silently switched every non-npm publish off in a sibling repo while its releases stayed green.

    Each step now gates on whether its own registry already carries package.json's version, so a retry publishes exactly what is missing, and a final step fails the run if npm shipped a version the others didn't. NuGet is reported but not asserted — its index lags an accepted push by minutes to tens of minutes, and a guard that reddens successful releases gets deleted.

v0.4.0

Choose a tag to compare

@brentrager brentrager released this 20 Aug 18:33
4926490

Minor Changes

  • ccc4f4e: Give every language the same emit posture: retry with backoff, and never fail
    silently.
    • Retry on transient failure in Python, Rust, Go, and .NET — previously
      TypeScript only. Transport errors and HTTP 5xx are retried with exponential
      backoff; a 4xx is surfaced immediately. The retried POST carries the same
      canonical bytes, since ingest dedupes on the event hash. The defaults now live
      in spec/parity-corpus.json's retryPolicy (3 attempts, 100 ms base,
      doubling) and every language's tests assert their client against it, rather
      than five copies of the same magic numbers.
    • BREAKING (Python): swallow_errors now defaults to False. It defaulted
      to True, so a misconfigured endpoint or an expired token dropped every audit
      event and reported success — fail-open on the exact path that carries the
      record. Pass swallow_errors=True to keep the old behaviour; on_error now
      fires whether or not the error is swallowed. (The old docstring also claimed
      this matched the TypeScript client. It never did — TS has always thrown.)
    • New: AuditClient.emit_async in Python, running the blocking urllib
      POST off the event loop via asyncio.to_thread.
    • Rust's AuditClientOptions gains max_retries / retry_backoff_ms (both
      Option, None = the shared default) and an AuditClientOptions::new
      constructor. Go's AuditClient gains MaxRetries / RetryBackoff (zero =
      default). .NET's AuditClientOptions gains MaxRetries / RetryBackoffMs.
    • Go's Emit stays synchronous on purpose — go client.Emit(ctx, event) is how
      Go does async — and now honours context cancellation between retries too.

v0.3.0

Choose a tag to compare

@brentrager brentrager released this 20 Aug 18:14
156422d

Minor Changes

  • a6a0293: Ship chain verification in all five languages, and prove it with corrupted-chain
    corpus vectors.

    verifyChain — the function that actually DETECTS a broken hash chain — existed
    only in TypeScript. Python carried a docstring describing how one would verify;
    Rust, Go, and .NET had nothing. A service in four of five languages could seal a
    chain it could never audit, which makes "tamper-evident" a claim rather than a
    capability.

    • New: verify_chain (Python, Rust), VerifyChain (Go), HashChain.Verify
      (.NET). All five return the same verdict shape: ok, brokenAt, and a shared
      failure code — hash_previous_mismatch (the link is wrong) or
      hash_current_mismatch (the event body was edited after sealing).
    • verifyChain gains an optional genesisPreviousHash, so a slice continuing an
      existing chain can be verified at all. Without it, only a first-of-day chain
      was verifiable. TypeScript additionally gains a code field alongside the
      existing human-readable reason (additive; reason is unchanged).
    • spec/parity-corpus.json gains chainFixtures: 11 real chains, sealed by the
      builder and then genuinely tampered with, each with the verdict every language
      must return. All five suites load them. The corpus previously proved sealing
      only, which is what let the asymmetry hide.
    • One fixture asserts the honest limit: deleting events from the TAIL of a chain
      still verifies. Replay cannot see it; catching it needs an external anchor.

v0.2.1

Choose a tag to compare

@brentrager brentrager released this 20 Aug 18:06
0454dfb

Patch Changes

  • 60bc0dc: Fix version sync so released artifacts carry the version they were released as.

    version:sync ran after changeset publish, mutating the manifests in the CI
    workspace where nothing ever committed them. Every git tag therefore shipped
    0.0.0 in python/pyproject.toml, rust/audit/Cargo.toml, go/version.go
    (audit.Version), and SmooAI.Audit.csproj while npm, PyPI, crates.io, and
    NuGet all showed 0.2.0 — and cargo publish --allow-dirty existed only to
    tolerate the resulting dirty tree.

    The sync now runs in the changesets version lifecycle, so the bumped manifests
    are committed with the release. A new pnpm version:check fails CI on any drift,
    including a go.mod module path whose /vN suffix disagrees with the major.
    cargo publish is now --locked.

v0.2.0

Choose a tag to compare

@brentrager brentrager released this 15 Aug 03:10
7fe7a2a

Minor Changes

  • f05a9ab: Trace correlation: an emitted audit event now carries the W3C trace context of the
    request that caused it, so a row in the audit store can be joined to a trace.

    The ids ride in the ENVELOPE, never inside the event. The wire body is now
    {"event":<the sealed event>,"spanId":"…","traceId":"…"}. The bytes under
    "event" are exactly the bytes that were hashed — unchanged, byte-for-byte, with
    or without a trace active — because hashCurrent covers canonical-JSON(event
    minus hashCurrent) and any new event field would invalidate every stored chain
    and every fixture in spec/parity-corpus.json. The corpus is untouched, and each
    language asserts it inside an active span as well as outside one. Both ids are
    OMITTED when there is no valid span: never "", never an all-zero id.

    TypeScript: AuditClient.emit(event, trace?) captures the active context at
    emit time behind an optional @opentelemetry/api peer dependency. Without it
    installed (or without a registered SDK) it is a no-op, not a crash. buildEnvelope
    / currentTraceContext are exported for consumers on their own transport.

    Rust: the same, behind a new optional otel cargo feature (off by default —
    the crate does not link OpenTelemetry unless you ask for it). AuditClient::emit
    uses the ambient span; emit_with_trace takes an explicit TraceContext that
    wins per field. TraceContext::current() reads both context homes — a tracing
    span via tracing-opentelemetry and an OTel-native one — because neither falls
    back to the other.

    Go: AuditClient.Emit(ctx, event) reads the span context already on the ctx it
    takes (trace.SpanContextFromContext(ctx).IsValid() before touching the ids), via
    the OpenTelemetry trace API only — no SDK, no exporter. Pinned to otel v1.35.0,
    the newest release whose go directive (1.22.0) still builds on the Go 1.22 the
    CI matrix pins; v1.36+ declare go 1.23.

    Python: the ids come from the ambient span behind a guarded
    from opentelemetry import trace import, exposed as the optional otel extra
    (pip install smooai-audit[otel]). Without it installed, correlation is a no-op —
    opentelemetry-api is never a hard dependency.

    .NET: reads Activity.Current — the BCL type the OpenTelemetry .NET SDK itself
    populates — so no new package reference. Non-W3C or unstarted activities report
    nothing.

v0.1.1

Choose a tag to compare

@brentrager brentrager released this 14 Jul 20:14
eef7efc

Patch Changes

  • 50f514b: Rust: feature-gate the HTTP AuditClient behind a default-on client feature (reqwest is now optional). Consumers that only need the schema + canonical JSON + hash chain — e.g. a service that publishes audit events onto its own transport such as NATS — can depend with default-features = false to drop the reqwest + async-runtime pull.

v0.1.0

Choose a tag to compare

@brentrager brentrager released this 14 Jul 16:46
c091227

Minor Changes

  • df7c377: Initial scaffold of @smooai/audit — a polyglot client SDK (TypeScript, Python, Rust, Go, .NET) for tamper-evident, SQL-queryable audit logging. Ships the intended public surface: a canonical AuditEvent schema, canonicalJson, a per-org-per-day SHA-256 hash chain (computeEventHash / buildHashChain), and an AuditClient emit client. Implementations are stubbed (TODO(audit-impl)) pending the shared parity corpus.