All notable changes to com.babelqueue:babelqueue-core are documented here.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
The envelope wire format is versioned separately by meta.schema_version
(currently 1) — see the contract at babelqueue.com.
- Runtime GDPR field encryption (ADR-0030) in the new optional
com.babelqueue.gdprmodule — the SDK-enforcement half of the registry'sx-gdpr-sensitivedeclaration. babelqueue-registry only declares and audits whichdatafields are personal data; this module enforces it on the wire: a producer encrypts each marked leaf before publish, a consumer decrypts it after decode. It is the Java mirror of the Go reference, so every SDK round-trips byte-for-byte. Standalone and opt-in.Cipheris a caller-provided interface (encrypt(byte[])/decrypt(String)) — a seam onto KMS/Vault/HSM/tokenisation, so the core pulls no crypto dependency (GR-7).AesGcmCipheris a JDK-only reference (javax.crypto, AES-256-GCM, a fresh random 12-byte IV prepended, Base64); the caller owns the key. A wrong key or tampered ciphertext fails GCM authentication and throws rather than returning corrupt plaintext.Gdpr.protect(data, schema, cipher)/Gdpr.unprotect(...)rewrite eachx-gdpr-sensitiveleaf in place: the value is canonically JSON-encoded then replaced by the ciphertext string, andunprotectdecodes the decrypted bytes back — so the round-trip is byte-for-byte (a number restores to a number, an object to an object). An absent path is skipped; a non-string leaf inunprotectis left untouched (idempotent re-runs); a value the cipher cannot open throwsDecryptExceptionso the message takes retry / dead-letter.SensitivePaths.of(schema)(+ theSensitivePathrecord) incom.babelqueue.schemawalk a decoded JSON Schema for thex-gdpr-sensitivemarks (booleantrueor a non-empty string category), descending nested objects, array items (field[]) and the root. The keyword is validation-neutral — annotating a schema is never a breaking change.- The wire envelope stays frozen: only the value of a sensitive field changes (to a
ciphertext string), so
datais still pure JSON (GR-3),meta.schema_versionstays1andtrace_idis untouched (GR-4). This is additive and opt-in.
- Transactional-outbox helper (ADR-0029) in the new optional
com.babelqueue.outboxmodule — the producer-side mirror of the consumer-side idempotency helper (ADR-0022). It removes the producer dual write: the message is persisted into the caller's own database, inside the caller's own transaction, so it commits or rolls back atomically with the business data, and a separate relay publishes the durable rows afterwards. Exactly-once handoff into the broker, then at-least-once on the wire as always (consumers still dedupe onmeta.id).Outbox.write(envelope)encodes via the frozenEnvelopeCodecand hands the bytes verbatim to the store — the outbox stores the wire envelope byte-for-byte and never adds an envelope field (GR-1,schema_versionstays1);trace_idis preserved end-to-end (GR-4) and the relay publishes those exact bytes (GR-5).OutboxStoreis the persistence contract the caller binds to their own DB (JDBC) — the core adds no DB driver (GR-7);InMemoryOutboxStoreis the reference for tests/single-process demos. The transaction boundary is the caller's:Outboxnever begins/commits.OutboxRelay.flush()/drain(maxPasses)publish a batch through the publish-onlyOutboxTransportseam, marking a row published only after the transport accepts it; a throwing publish is caught, recorded viamarkFailedwith a bounded linear backoff (injectableSleeperso tests stay instant), and the row stays pending — one poison row never blocks the batch.- The envelope wire format is unchanged; this is an additive, opt-in producer-side concern.
- W3C
traceparenttransport-header propagation (ADR-0028, the v0.2 follow-up to ADR-0025) in the optionalcom.babelqueue.otelmodule — true cross-hop span parent-child linkage layered over the existing v0.1trace_id↔ OTel-trace-id mapping. The producer injects the active span'straceparent(andtracestate) onto an out-of-bandMap<String, String>header carrier that rides beside the frozen envelope (GR-1,schema_versionstays1), never inside it; the consumer reads it and starts itsprocess <urn>span as a true child of the producer span. With notraceparentpresent it falls back to the v0.1trace_id-derived parent — a strict, backward-compatible upgrade, no regression.- New produce-side seam
otel.HeaderSender(aSenderthat also receives the out-of-band header map) plusTracing.publish(..., HeaderSender)overloads. - New consume-side overload
Tracing.wrapHandler(tracer, handler, Supplier<Map<String,String>>)that reads a delivered message's out-of-band headers (e.g.Redrive.Reserved.headers()). - The W3C wire format is produced by OpenTelemetry's own
W3CTraceContextPropagator(shipped inopentelemetry-api, the already-optional dependency), so no new dependency is pulled and the core stays zero-dependency for non-opt-in users (GR-7);trace_idis preserved unchanged (GR-4). - Transport wiring is a documented follow-up: the Java transports live in
separate artifacts (
babelqueue-java-sqs,babelqueue-java-redis,babelqueue-spring), so carrying the header map on each broker's native per-message metadata channel (AMQP headers, SQSMessageAttributes, a Redis transport-owned frame) is the per-transport rollout — the same seam ADR-0027 and the broker bindings roll out per SDK.
- New produce-side seam
1.0.0 - 2026-06-07
1.0.0 — the public API is now SemVer-stable: breaking changes require a MAJOR,
following the deprecation policy. The wire envelope is unchanged
(schema_version: 1). Full reference at babelqueue.com.
- Build adds JaCoCo (line-coverage gate ≥90%, bound to
verify) and SpotBugs (effort=Max,threshold=Medium); both run in CI viamvn verify. Added JSON codec edge-case + exception tests to clear the gate. A documentedspotbugs-exclude.xmlwaives the EI_EXPOSE patterns on the read-only envelope records (no hot-path defensive copy — GR-8). - GR-8 latency benchmark (
OverheadBenchmarkTest) — asserts the envelope encode/decode path adds ≤2% over plain-JSON serialization vs a conservative 750µs broker round-trip.
0.1.0 - 2026-06-06
EnvelopeCodec— builds (make,fromMessage), encodes and decodes the canonical{job, trace_id, data, meta, attempts}envelope (schema_version1). The single Java implementation of the wire format.Envelope/Meta/DeadLetterimmutablerecordtypes.EnvelopeCodec.encodeemits compact UTF-8 JSON (slashes/unicode unescaped) — byte-identical to the PHP, Python and Node cores (insertion order preserved).EnvelopeCodec.urn(...)— resolve the URN (job, acceptingurnas an alias).EnvelopeCodec.accepts(...)— consumer-side validation (rejects empty URN, unsupportedmeta.schema_version, missingdata, blanktrace_id).DeadLetters.annotate(...)— additivedead_letterblock builder.- Contracts
PolyglotMessage/HasTraceId. UnknownUrnStrategy(FAIL/DELETE/RELEASE/DEAD_LETTER);BabelQueueException/UnknownUrnException.- A built-in minimal JSON reader/writer so the core ships with zero dependencies — no Jackson/Gson forced on consumers.
- Shared cross-SDK conformance suite under
src/test/resources/conformance/(vendored from the canonicalconformance/set) plus a runner.
- Pre-1.0: the public API may change before the
1.0.0tag. - Zero runtime dependencies (pure JDK); requires Java 17+.