Skip to content

Releases: Guard-Core/guard-agent

v3.2.4

Choose a tag to compare

@rennf93 rennf93 released this 15 Oct 12:27
Immutable release. Only release title and notes can be modified.
ab52909

v3.2.4 (2026-10-15)

Redis persistence off the request path

Persisting events and metrics to Redis no longer happens under the buffer lock. The in-memory append stays on the request path and the Redis write runs as a tracked background task that stop_auto_flush drains, so a slow or failing Redis no longer delays requests. A Redis failure can no longer re-enter persistence, which removes a deadlock that blocked every later send_event once Redis started failing. In-flight writes are capped at buffer_size: past the cap an item stays in memory only, counts as a persist failure, and one warning is logged per backlog episode. Items flushed or evicted before their write starts are never written, and a failed write no longer leaves tracking state behind. Unpadded base64url project keys are accepted, and the startup encryption error now includes the underlying cause. Lockstep release with the agent family at 3.2.4.

v3.2.3

Choose a tag to compare

@rennf93 rennf93 released this 12 Oct 23:12
Immutable release. Only release title and notes can be modified.
5da7c8c

v3.2.3 (2026-10-12)

Version reporting fix

The 3.2.2 wheel shipped guard_agent/_version.py at 3.2.1, so 3.2.2 installs reported 3.2.1 to the Guard SaaS. Every version location now reads 3.2.3. Lockstep release with the agent family at 3.2.3.


v3.2.2

Choose a tag to compare

@rennf93 rennf93 released this 08 Oct 12:07
Immutable release. Only release title and notes can be modified.
57301d5

Metadata release for the Guard-Core org migration: PyPI project URLs and documentation links now point at Guard-Core. No code changes.

v3.2.1

Choose a tag to compare

@rennf93 rennf93 released this 07 Oct 02:55
v3.2.1
8c933c8

v3.2.1 (2026-10-06)

The lockstep release: version alignment with the engines at 4.3.1 (v3.2.1)

No agent behavior changes. Shipped so the agents family moves in lockstep with the engines at 4.3.1 while guard-agent-ts carries the js/polynomial-redos fix (linear stripTrailingSlashes replacing anchored quantifier regexes in config.ts/transport.ts, CodeQL HIGH).

v3.2.0

Choose a tag to compare

@rennf93 rennf93 released this 01 Oct 21:20
v3.2.0
47458f5

v3.2.0 (2026-10-01)

Buildable dev resolution, Slack-free CI, and an enforced 100% coverage gate (v3.2.0)

Fixed

  • guard-agent[dev] installs resolve again (#65). The repo commits no uv.lock, so every uv sync --extra dev re-resolves fresh; with fastapi in the dev extra, that fresh resolution backtracked semgrep all the way to 0.86.3, whose sdist setup.py aborts without a system semgrep-core binary. That broke guard-agent[dev] installs in guard-agent's own CI and in the guard-core ecosystem-gate consumer jobs, and shipped in the 3.1.0 metadata. semgrep is now floored at >=1.100,<2, so resolution lands on a wheel release (1.178.0) and the dev extra installs cleanly again.

Changed

  • CI no longer posts Slack notifications (#64). The Slack notification steps were removed from ci.yml, code-ql.yml, docs.yml, release-shim.yml, release.yml and scheduled-lint.yml.
  • The 100% coverage gate is now enforced in CI (#64). The test job runs pytest with --cov-fail-under=100, so a coverage regression fails the build instead of only being reported; this matches the project's 100%-coverage target.
  • github/codeql-action bumped from 4.38.1 to 4.38.2 (#63).

v3.1.0

Choose a tag to compare

@rennf93 rennf93 released this 27 Sep 16:10
v3.1.0
496610a

v3.1.0 (2026-09-27)

The parity release: agent feature surface locked across all four language ports (v3.1.0)

About this release

  • This is the parity release for the Guard agent family. The 3.1.0 wave (Python, TypeScript, Go, PHP, Rust) ships the same feature surface in every port. Note: the earlier 4.1.0 family tags were a version-accuracy error and were yanked/unpublished; 3.1.0 is the correct version for this train.
  • The conformance corpus (219 cases, spec 4.1.0) validates the shared surface across the ports.

Added (3.0.2 -> 3.1.0 feature list)

  • AES-256-GCM encrypted ingest. Event batches can be encrypted end to end with an AES-256-GCM key; the server decrypts after transport-level TLS, so batch contents are opaque to intermediaries even on the wire.
  • Sensitive-header redaction. Authorization, Cookie, Set-Cookie, and other configured sensitive headers are redacted at ingest and again at egress, so secrets never leave the process in plaintext form.
  • Dynamic rules. The agent polls the guard-core server for dynamic rate-limit and ban rules on a configurable interval and enforces them locally, emitting dynamic_rule_applied, dynamic_rule_updated, and dynamic_rule_violation events.
  • Local rate limiters. Rule-driven local rate limiting with auto-ban support, usable without a Redis round trip on every event.
  • Helpers. Shared helpers for event construction, batching, and redaction are exported for adapter authors building custom ingest paths.

v3.0.2

Choose a tag to compare

@rennf93 rennf93 released this 23 Sep 22:12
v3.0.2
da2f138

Fixed

  • The payload signature was computed over the compressed wire body, so every compressed request failed server-side verification. When gzip compression kicked in (payload at or above compression_threshold with compression_enabled), X-Payload-Signature was an HMAC over the gzipped bytes, while the server verifies after decompressing; compressed batches were therefore rejected as signature-invalid. The signature now always covers the uncompressed body, matching the server contract and all four agent ports (Go, PHP, Rust, TypeScript). Regression tests verify the signature equals the HMAC over the decompressed wire body on both the encrypted and unencrypted POST paths.
  • The package version and the reported agent version could drift apart. The 3.0.1 release updated pyproject.toml but left guard_agent/_version.py (the single source of truth used for the User-Agent and agent_version on every batch) at 3.0.0. Both are now 3.0.2, and a new consistency test fails if the two files ever diverge again.

Changed

  • The deprecated fastapi-guard-agent alias now resolves to guard-agent 3.x. Its dependency pin was still guard-agent>=2.0.0,<3.0.0 from the 2.0.0 rename, so installs through the alias kept resolving to 2.x after 3.0.0 shipped. The pin is now >=3.0.0,<4.0.0 and the shim version is 1.3.0; the shim itself publishes via the manual release-shim.yml workflow after the main release.

v3.0.1

Choose a tag to compare

@rennf93 rennf93 released this 23 Sep 21:01
v3.0.1
db178a5

Patch release.

Fixed

  • The flush loop's partial-failure warning no longer claims items are retained in Redis when Redis persistence is disabled; it names the backend that actually holds the requeued items.

See CHANGELOG.md for details.

v3.0.0

Choose a tag to compare

@rennf93 rennf93 released this 04 Sep 19:44
b44b5da

Recursive header and metadata redaction, an atomic block-policy buffer, and a maintainability split (v3.0.0)

Breaking Changes

  • BufferProtocol.requeue_events_in_memory and requeue_metrics_in_memory are now async. A failed send's requeue now shares the same lock as every other buffer mutation, so both methods on the protocol (and on EventBuffer) must be awaited. A custom BufferProtocol implementation needs to change its signatures to async def; GuardAgentHandler's own buffer and FlushMixin already call them with await.
  • sanitize_headers now redacts what it cannot safely classify instead of letting it through. A JSON-looking string over 8192 characters, a value of a type it does not recognize (a generator, an arbitrary object), and a bytes header key that fails to decode as UTF-8 previously passed through unchanged or matched nothing; all three now become [REDACTED]. A payload that relied on that pass-through will see [REDACTED] there instead.

Added

  • sanitize_headers recurses into every container and into JSON-looking strings, including a double-encoded JSON string. Dicts, lists, tuples, sets, frozensets and JSON string bodies are all walked (up to a bounded depth), not just the top-level mapping; a JSON string whose decoded value is itself a JSON string is unwrapped and sanitized too, then re-encoded the same way.
  • datetime, date, time, Decimal, UUID and Enum values pass through unchanged. They are legitimate scalar metadata, not unrecognized objects to redact.
  • A dataclass instance or a pydantic model passed as metadata is walked field by field, like a mapping, via dataclasses.asdict / model_dump, instead of being collapsed to [REDACTED] wholesale.
  • The default sensitive_headers set now matches guard-core's: proxy-authorization joins authorization, cookie and x-api-key.
  • KNOWN_EVENT_TYPES gained ip_ban_failed, rate_limit_script_reloaded, pattern_anomaly_timeout, pattern_anomaly_statistical_anomaly and route_unresolved, aligning with guard-core 4.0.0's event catalogue. A version-gated test skips until the installed guard_core reaches 4.0.0 and fails on any other drift once it does.
  • tests/test_maintainability_rank.py enforces radon MI rank A on every module in guard_agent.
  • on_error also fires at the flush boundary (stage='flush_events' / 'flush_metrics'), once per failed batch, with the exception and {"batch_size": N}, through the same fire_error_hook helper HTTPTransport uses. Previously it only fired inside the transport's own retry paths.

Fixed

  • A bytes header key no longer bypasses the sensitive-header match. b"authorization" is decoded as UTF-8 before the case-insensitive comparison instead of being stringified to the literal "b'authorization'", which never matched anything.
  • The event and metric buffers each serialize their overflow-check-and-append, flush, requeue and clear through one asyncio.Lock. Two concurrent add_event (or add_metric) calls at capacity could previously both evict the same slot, appending past it and orphaning the other call's Redis record; the check and the append are now one atomic step.
  • The block overflow policy waits on an asyncio.Condition bound to that same lock and re-checks at least once every 0.5 seconds. A requeue after a failed send always keeps its slot (durability over a new writer), and a blocked writer no longer depends solely on an explicit signal that a send-failure backoff (up to 300 seconds) can delay past.
  • clear_buffer now clears the Redis-key maps along with the buffers. Previously only the deques were cleared, so a later event or metric object could inherit a stale or foreign key once CPython reused its id().
  • A Redis record is deleted on a requeue eviction and on an overflow drop without racing the buffer it was tracked against.
  • The Makefile's semgrep target scans guard_agent with --no-git-ignore, so a new, not-yet-tracked file is scanned deterministically instead of depending on git state.
  • A BaseException from the transport (an asyncio.CancelledError, not an Exception subclass) no longer skips the batch's requeue and failure bookkeeping. _flush_events/_flush_metrics catch BaseException, requeue the popped batch and advance the failure streak and backoff before re-raising, so a cancellation still propagates but never silently drops the batch.
  • Loading persisted events/metrics from Redis at startup no longer races live add_event/add_metric traffic. Each load now runs under its buffer's own condition lock, and forgets the oldest tracked key before its own append would otherwise evict an item past capacity, so a startup load can never orphan a Redis record or corrupt the key map racing a concurrent write.

Internal (v3.0.0)

  • buffer.py, client.py and transport.py were split into focused mixins (_buffer_lifecycle, _buffer_overflow, _buffer_queue, _buffer_redis; _client_flush, _client_ingest, _client_loops, _client_status; _transport_dispatch, _transport_lifecycle, _transport_send), each independently testable and each rank A.
  • anyio<4.15 and semgrep were added to the dev extra.

What's Changed

  • release(v3.0.0): redaction at ingest and egress, lossless atomic buffer, rank-A split, event-type parity by @rennf93 in #52

Full Changelog: 2.10.0...3.0.0

v2.10.0

Choose a tag to compare

@rennf93 rennf93 released this 01 Sep 20:53
70c33b7

Agent logs that identify themselves (v2.10.0)

Added

  • guard-agent log lines now carry an origin prefix. setup_agent_logging attaches guard-core's standard formatter ([guard_agent.client] 2026-09-01 12:00:00 - WARNING - ...) to the guard_agent logger tree, so agent records such as Events flush recovered after N consecutive partial failure(s) are identifiable in hosted log viewers instead of arriving bare. It runs automatically when a GuardAgentHandler or SyncGuardAgentHandler is constructed, and the function is exported for explicit host use: setup_agent_logging(log_file=..., log_format="json").
  • JSON output and an optional file sink. log_format="json" emits {"timestamp", "level", "logger", "message"}; log_file=... adds a FileHandler alongside the console handler. A console handler is only attached when the root logger has no handlers, matching guard-core's yield-to-host contract, so records still reach host-configured logging through propagation.

Changed

  • The automatic setup is non-destructive: hosts keep their logging configuration. Constructing the handler attaches at most one console handler, never changes a level the host set, and never removes a host-configured file handler or formatter, no matter how many times the handler is constructed. Hosts reconfigure by calling setup_agent_logging(...) explicitly, which re-applies intent (agent handlers are cleared first).
  • The old async setup_agent_logging stub in guard_agent.utils is gone. It was never called and had zero consumers across the ecosystem, and its export now points at the real implementation in guard_agent.logging_utils. The signature changed from async (log_level: str) to (log_file: str | None, log_format: str, *, reconfigure: bool).

Internal (v2.10.0)

  • pytest now runs with filterwarnings = ["error"], so any warning, including one raised at import/collection time, fails the suite instead of passing silently.
  • That escalation surfaced a real resource leak: tests/test_adapter_fastapi.py's SecurityConfig didn't disable Redis, leaving an unclosed async Redis socket to raise a ResourceWarning at a later, unrelated test's garbage collection. Added enable_redis=False, the same fix already applied to test_performance.py in v2.3.0 to eliminate ResourceWarning pollution.

What's Changed

  • chore(conventions): scaffold .roboco/conventions.yml by @roboco-app[bot] in #48
  • Bump actions/github-script from 7 to 9 by @dependabot[bot] in #46
  • Bump github/codeql-action from 4.37.8 to 4.37.9 by @dependabot[bot] in #47
  • feat(logging): identifiable agent logs with non-destructive setup (v2.10.0) by @rennf93 in #49

New Contributors

  • @roboco-app[bot] made their first contribution in #48

Full Changelog: 2.9.1...2.10.0