)]}'
{
  "log": [
    {
      "commit": "ad7aaebddc8b5e8362711044ab593df1c10eab66",
      "tree": "47f90ee6972dcca48712c6f0dbfeec0b04687816",
      "parents": [
        "06cf04e5e4c9e0c88aaceffeb90bac003e7fe002"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Thu Nov 05 17:44:34 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 17:44:34 2026 +0100"
      },
      "message": "fix(ci): prevent false Python example passes (#4762)"
    },
    {
      "commit": "06cf04e5e4c9e0c88aaceffeb90bac003e7fe002",
      "tree": "e10c8c65cb28caf329594a2e21a2f53af6b1817b",
      "parents": [
        "c59961ce4312c0723115bf83db896cf410103d4a"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Thu Nov 05 16:44:33 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 16:44:33 2026 +0100"
      },
      "message": "fix(metadata): send metadata prepares in op order (#4755)\n\nA metadata submit claims its op number, then waits on\njournal_gate. LocalGate::release freed the gate and woke all\nwaiters, and shard 0 runs each forwarded submit in its own task.\nSo a new submit took the free gate ahead of the waiters, and the\nprimary sent a later op first. A backup accepts only the next op.\nIt dropped that prepare, and every later one, as a gap until a\nretransmit or a repair filled the hole. In a 3-node run to 10k\nstreams, the two backups logged 6,683 and 3,965 gap drops and 8\nstate transfers.\n\nRelease now hands the gate to the oldest waiter, so every\nLocalGate user gets arrival order. A dropped waiter gives up its\nplace, or passes the gate on if it already got it. Each submit\nreaches the acquire with no await after its claim, so arrival\norder is op order. The send stays outside the gate: the next\nsubmit runs only after this one yields, and replicate queues\nevery copy without yielding, so the backups still get the\nprepares in op order. The same run now logs no gap drops and no\ntransfers."
    },
    {
      "commit": "c59961ce4312c0723115bf83db896cf410103d4a",
      "tree": "c493ef7a8154cb389050e74c469ad09ea4f09d52",
      "parents": [
        "e4764be3c265659d6408fbaacb0ff7194b8f26f5"
      ],
      "author": {
        "name": "yum",
        "email": "yang@chihum.dev",
        "time": "Thu Nov 05 22:26:32 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 15:26:32 2026 +0100"
      },
      "message": "feat(python): add multi-tenant producer and consumer examples (#4751)"
    },
    {
      "commit": "e4764be3c265659d6408fbaacb0ff7194b8f26f5",
      "tree": "ffcd7dd8cda93f3c388fdc81453ecf9f0a789ebb",
      "parents": [
        "8d6d0fefee3e8912cd0a8f3d714a5a3581b8d5b7"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Thu Nov 05 14:15:15 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 14:15:15 2026 +0100"
      },
      "message": "fix(connectors): retry failed Iceberg catalog commits (#4740)\n\nCloses #4723\nRelates to #4232, #4236\n\nThe Iceberg sink committed each append once. iceberg-rust retries only\nHTTP 409 conflicts, so a catalog that was unreachable, restarting or\nanswering with a 5xx failed the commit at once, and the runtime had\nalready stored the offset of the batch. A single catalog restart in an\nexternal test lost 4,700 rows.\n\nThe sink now retries the commit when the catalog cannot be reached or\nanswers with an unexpected status, with a capped exponential backoff set\nby three new plugin options: `max_retries` (total attempts, default 5),\n`retry_delay` (default `1s`) and `max_retry_delay` (default `30s`). The\ndata files are written once, and only the commit is repeated. A commit\nthat the catalog refuses for good, such as one to a missing table, fails\nat once as before. The default of 5 attempts is above the usual 3\nbecause a retry only repeats one catalog request, and its waits of about\n15 seconds cover a catalog restart.\n\nA failed commit may still have been applied, for example when a gateway\nanswers 502 or 504 while the catalog keeps working. Each append\ntherefore carries an `iggy.commit-id` snapshot summary property, and\nbefore every retry the sink reloads the table and stops if a snapshot\nwith that id is already there. A request that the catalog applies only\nafter that check can still be appended twice, which the README states.\nThe same marker can identify a batch that the runtime delivers again\nonce #4232 lands, for #4236. Fan-out to several tables is still not\natomic.\n\nUnit tests drive the commit against a memory catalog that fails on\ndemand: an unreachable catalog is retried until the commit lands, a lost\nresponse after an applied commit adds no second snapshot, a refused\ncommit is not retried, and the attempts are bounded. An integration test\nkills the REST catalog container, sends a batch, waits until the sink\nhas read it, and starts the catalog again, after which all 7 rows are in\nthe table. The REST catalog fixture now maps a reserved host port, which\na container restart keeps.\n\nDisabling the marker check, treating no error as transient, or treating\nevery error as transient each fails a unit test, and the integration\ntest fails with `max_retries \u003d 1`.\n\nStacked on #4739."
    },
    {
      "commit": "8d6d0fefee3e8912cd0a8f3d714a5a3581b8d5b7",
      "tree": "3c24733f1c5b82c23b4c7e9d1a6c828f1efc7bcd",
      "parents": [
        "fbe348433baee846b29ec87efc46abf6fd8ffbc1"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Thu Nov 05 12:35:05 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 12:35:05 2026 +0100"
      },
      "message": "fix(connectors): skip Iceberg rows that do not match the table schema (#4739)\n\nCloses #4724\nRelates to #4232, #4235\n\nThe Iceberg sink decodes a batch with one arrow-json reader built from\nthe table schema, so a single row with a wrong type or a missing\nrequired field fails the whole `consume`. The runtime stores the offset\non read, so every good row of that batch is lost with the bad one.\n\nA new `invalid_rows` plugin option picks what happens to such a row.\n`fail`, the default, keeps today\u0027s behaviour. With `skip`, the sink\ndecodes the rows one at a time after the batch decode fails, writes the\nrows that match, and logs each skipped row as a warning with its offset\nand the decode error. A batch in which no row matches writes no data\nfile and commits nothing. A batch without bad rows is decoded once, as\nbefore.\n\nSending skipped rows somewhere durable stays with #4235.\n\nUnit tests cover both modes and the batch with only bad rows, and an\nintegration test sends one bad row among four good ones with `skip` and\nchecks that the snapshot added four records. The integration test fails\nwhen the fixture sets `fail`, and the unit test fails when `skip`\nbehaves like `fail`."
    },
    {
      "commit": "fbe348433baee846b29ec87efc46abf6fd8ffbc1",
      "tree": "393534297810e2e144f14f8cee5f6347eb4aad60",
      "parents": [
        "ff74972d449752bc5b5d18ecf38eeeb2d90ba152"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Thu Nov 05 11:33:20 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 11:33:20 2026 +0100"
      },
      "message": "test(integration): pin the crash loss of replicated partitions (#4738)"
    },
    {
      "commit": "ff74972d449752bc5b5d18ecf38eeeb2d90ba152",
      "tree": "d046e539286cae43c9babdf055ef600fae3cceba",
      "parents": [
        "e49908dc125482490641ff2d156518087377f0a0"
      ],
      "author": {
        "name": "Abhinav Negi",
        "email": "125854461+08abhinav@users.noreply.github.com",
        "time": "Thu Nov 05 14:59:47 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 10:29:47 2026 +0100"
      },
      "message": "feat(php): add PHP message envelope example (#4718)"
    },
    {
      "commit": "e49908dc125482490641ff2d156518087377f0a0",
      "tree": "7eeaa76d817919033a61bbd2d1181d2cecd8119b",
      "parents": [
        "df68779fcc4fc2d8fe6c76640051274030a7c05c"
      ],
      "author": {
        "name": "Hang Wang",
        "email": "75254497+coding-by-sea@users.noreply.github.com",
        "time": "Thu Nov 05 04:20:33 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 10:20:33 2026 +0100"
      },
      "message": "test(partitions): stop the removal test panicking when run as root (#4754)"
    },
    {
      "commit": "df68779fcc4fc2d8fe6c76640051274030a7c05c",
      "tree": "28bbe2759b999f3f3db159b834122c832e807bd6",
      "parents": [
        "7bcf398ead79d60ecd13c33e9c01000547752bea"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Thu Nov 05 09:37:29 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 09:37:29 2026 +0100"
      },
      "message": "feat(connectors): deliver sink messages at least once (#4734)\n\n## Which issue does this PR address?\n\nCloses #4233\nCloses #4234\nCloses #4235\nRelates to #4232, #4236, #4237, #2927, #2928\n\n## Rationale\n\nA sink stores the offset of a poll when the server answers it, before\nthe plugin writes anything, so a failed or interrupted write loses the\nbatch. The 503-then-restart reproduction in #4232 drops the rejected\nrecord, and a local Iceberg test lost 500 rows to sink kills and 4,700\nto a single catalog restart. This PR states the delivery contract that\n#4233 asks for and implements it as an opt-in mode in the SDK, the\nruntime and the HTTP sink.\n\n## What changed?\n\nThe runtime stored a sink\u0027s offset on read, so it could not hand a\nfailed batch over again. A sink with `[delivery] guarantee \u003d\n\"at_least_once\"` now polls without storing, the plugin accepts batches\nin `consume`, and the runtime stores an offset only after the plugin\u0027s\n`flush` proved the records up to it delivered. A failure is retried from\nthe first unflushed record or stops the sink with its partition and\noffset, and an invalid record stops it unless `invalid_messages \u003d\n\"skip\"`. Sinks without the table keep today\u0027s behaviour, plugins built\nwith an older SDK keep loading, and the HTTP sink is the first plugin to\nopt in.\n\n## Delivery contract (#4233)\n\nMerging this PR records the agreement #4233 asks for. Version 1\nimplements every rule below except the parts marked as later or #4238.\n\n**Modes.** `at_most_once`, the unchanged default, can lose a record on\nfailure. `at_least_once` never loses a record while Iggy still holds it,\nand can deliver it more than once:\n\n```toml\n[delivery]\nguarantee \u003d \"at_least_once\"\nretry_max_attempts \u003d 10      # unset retries until the destination recovers\nretry_initial_backoff \u003d \"1s\"\nretry_max_backoff \u003d \"30s\"\nflush_interval \u003d \"5s\"        # unset flushes after every batch\nflush_max_messages \u003d 10000\ninvalid_messages \u003d \"stop\"    # or \"skip\"\n```\n\n**Rules for `at_least_once`:**\n\n1. **Checkpoint.** The stored offset of a partition is its last complete\nrecord: delivered, filtered by a transform, or skipped under `skip`. It\nnever passes the first incomplete record, even when later records\nsucceeded.\n2. **Acknowledgment.** A successful `consume` only accepts records. A\nsuccessful `flush` proves every accepted record of the listed partitions\ndelivered, up to each listed offset. The runtime flushes after every\nbatch, or with `flush_interval` when it runs out or a partition holds\n`flush_max_messages` unflushed records, and at shutdown. A plugin that\nwrites inside `consume` keeps the default no-op flush.\n3. **Failures.** A call or a flush covers all of its records, and an\nuncertain outcome counts as a failure. A failure crosses the plugin\nboundary as transient or permanent, and the plugin logs the reason. A\nplugin built before the contract reports every failure as transient.\n4. **Retries.** A transient failure hands the unflushed records over\nagain with a capped exponential backoff, up to `retry_max_attempts`. A\npermanent failure, or the last attempt, stops the consumer task with the\npartition and offset in the sink error, and a restart resumes at the\nstored offset. A failed offset store is retried and never treated as\nundelivered data.\n5. **Duplicates.** A failed call or flush, a crash, an ownership change\nor an uncertain store can deliver records above the stored offset again,\nwith the same stream, topic, partition and offset to deduplicate on.\n6. **Invalid records.** A record that fails to decode, transform or\nserialize stops the task, unless `invalid_messages \u003d \"skip\"` skips it\nwith an error count and its offset in the log. A dead-letter policy can\ncome later.\n7. **Lifecycle.** A stop, restart or reconfiguration flushes once and\nstores what the flush proved. A crash resumes at the stored offsets. A\nresult of a closed or replaced plugin instance, or of a partition\ngeneration that is no longer owned, stores nothing. Version 1 has no\nhook before a rebalance takes a partition away, so the next owner\nredelivers from the stored offset. Flushing before release is later.\n8. **Isolation.** Version 1 isolates the consumer task, one per\nconfigured topic: a stopped partition stops its topic, and the sink\u0027s\nother topics keep delivering.\n9. **Retention and durability.** A partition whose next record is gone,\nafter retention or a new partition history, stops with an error instead\nof skipping ahead. Records lost by Iggy itself are out of scope, so use\n`persisted` durability and a retention longer than the longest expected\nsink outage.\n\n**Compatibility.** The SDK gains two optional exports,\n`iggy_sink_enable_at_least_once` and `iggy_sink_flush`, and a status\ncode for a permanent failure. No existing symbol changes, so no major\nSDK version is needed. A sink configured for the guarantee refuses to\nstart with a plugin that does not opt in, and a plugin can refuse a\nconfiguration it cannot support. The status values stay as they are, and\na stopped task reports `error`. Changing the mode keeps the consumer\ngroup and its offsets and replays nothing. The default switch, migration\nguidance and per-partition visibility follow in #4238, and the other\nbundled sinks in #4236.\n\n**Selected approach.** The runtime-owned commit and the rule that a\nfailure is never stepped over come from #3203 and #3518. The flush\nbarrier is new, because sinks such as S3 buffer across calls. A commit\ncallback driven by the plugin (#3203) was rejected because it splits\ncheckpoint ownership, and per-batch acknowledgments with batch ids\n(#3855) because they need a callback registry and gap tracking. In Kafka\nConnect terms, `consume` is `put`, a flush followed by a checkpoint is\n`flush`/`preCommit`, and `invalid_messages` is `errors.tolerance`."
    },
    {
      "commit": "7bcf398ead79d60ecd13c33e9c01000547752bea",
      "tree": "9690d9ea9d5cf060596aee969ee75f5c55a24e4e",
      "parents": [
        "f451fb91e458f4ae75053a707556b96e86e9af54"
      ],
      "author": {
        "name": "Himanchal Kaushale",
        "email": "himanchalkaushale432@gmail.com",
        "time": "Thu Nov 05 13:02:48 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 08:32:48 2026 +0100"
      },
      "message": "feat(python): add sink data producer example (#4710)\n\nCo-authored-by: Hubert Gruszecki \u003ch.gruszecki@gmail.com\u003e"
    },
    {
      "commit": "f451fb91e458f4ae75053a707556b96e86e9af54",
      "tree": "88522ef594a5a9ba64a16a325d231515fa54f79f",
      "parents": [
        "fff4f983c5270787f715b3ae1cbe5cbd8c6be793"
      ],
      "author": {
        "name": "Chaitanya Reddy",
        "email": "rchaitu1999@gmail.com",
        "time": "Thu Nov 05 12:21:35 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 07:51:35 2026 +0100"
      },
      "message": "feat(example): add a multi-tenant Go producer and consumer (#4716)"
    },
    {
      "commit": "fff4f983c5270787f715b3ae1cbe5cbd8c6be793",
      "tree": "afe4a707cdee239dcc6e34ec560724c0f60c191e",
      "parents": [
        "09b5a02c01d02049d110a92e47f30cb4298f1867"
      ],
      "author": {
        "name": "Partridge",
        "email": "94586971+p4rtridge@users.noreply.github.com",
        "time": "Thu Nov 05 13:25:53 2026 +0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 07:25:53 2026 +0100"
      },
      "message": "test(go): cover access token command serialization (#4640)\n\nRelates to #2883"
    },
    {
      "commit": "09b5a02c01d02049d110a92e47f30cb4298f1867",
      "tree": "dab8f98b55fc8933049ba7d1e672600de31e9942",
      "parents": [
        "2487036c45e1a963af9ef4602caad8826625a393"
      ],
      "author": {
        "name": "Justin Mclean",
        "email": "jmclean@apache.org",
        "time": "Thu Nov 05 10:32:51 2026 +1100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 00:32:51 2026 +0100"
      },
      "message": "fix(mcp): document allowed values for poll strategy and partitioning (#4588)"
    },
    {
      "commit": "2487036c45e1a963af9ef4602caad8826625a393",
      "tree": "f2f65696279b75ac96603e8a60f4b86b4382ad63",
      "parents": [
        "c4d86e3fffb21027221e40287ab5335cc326fb55"
      ],
      "author": {
        "name": "Parthiban Rajasekaran",
        "email": "36532774+ParthibanRajasekaran@users.noreply.github.com",
        "time": "Wed Nov 04 23:02:31 2026 +0000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 05 00:02:31 2026 +0100"
      },
      "message": "feat(python): expose consumer shutdown and offset drain timeout (#4660)"
    },
    {
      "commit": "c4d86e3fffb21027221e40287ab5335cc326fb55",
      "tree": "704c2f09f62346f23aed5adf743ca380ef8c24ea",
      "parents": [
        "d0fc19a269b3ecc415e0c44ba3fb53e4045e397c"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Nov 04 23:20:47 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Nov 04 23:20:47 2026 +0100"
      },
      "message": "chore(deps): Bump the python group across 3 directories with 3 updates (#4752)"
    },
    {
      "commit": "d0fc19a269b3ecc415e0c44ba3fb53e4045e397c",
      "tree": "8598f114f7f056db1f058c4c114881b36562f963",
      "parents": [
        "7b053f4cbf635d03ba187123a42c0157ac554587"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Nov 04 14:41:10 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Nov 04 14:41:10 2026 +0100"
      },
      "message": "chore(deps): Bump nlohmann_json from 3.12.0.bcr.1 to 3.12.0.bcr.2 in /foreign/cpp in the cpp group across 1 directory (#4689)"
    },
    {
      "commit": "7b053f4cbf635d03ba187123a42c0157ac554587",
      "tree": "2f77d7dfe9ddf22a6182528da08e5c3db9cdaf58",
      "parents": [
        "55ae03c6302c4dc41eaf239bd3f615477e1d9d47"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Nov 04 10:17:37 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Nov 04 10:17:37 2026 +0100"
      },
      "message": "chore(deps): Bump ch.qos.logback:logback-classic from 1.6.5 to 1.6.6 in /foreign/java in the java group across 1 directory (#4743)"
    },
    {
      "commit": "55ae03c6302c4dc41eaf239bd3f615477e1d9d47",
      "tree": "0c3d7d5beb4ab6c9c2aa0236c7463ddf8141b997",
      "parents": [
        "91e0206367aad2d30259f7d45203832b08ae84e7"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Nov 04 09:53:41 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Nov 04 09:53:41 2026 +0100"
      },
      "message": "chore(deps): Bump the github-actions group across 1 directory with 6 updates (#4744)"
    },
    {
      "commit": "91e0206367aad2d30259f7d45203832b08ae84e7",
      "tree": "879343bedb44fffab2fa0e64b3f1013f6721aed3",
      "parents": [
        "17b8c4b117244a1a74e02adec6430ace546de7f6"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Wed Nov 04 08:24:46 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Nov 04 08:24:46 2026 +0100"
      },
      "message": "fix(repo): renew the localhost TLS certificate expiring 2026-11-17 (#4745)"
    },
    {
      "commit": "17b8c4b117244a1a74e02adec6430ace546de7f6",
      "tree": "bec5031385f58146efca7602ee66d5294ac38781",
      "parents": [
        "7d0ab2a174d0c81e5f8ae28e13a8fa37ee9bf49c"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Wed Nov 04 07:58:11 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Nov 04 07:58:11 2026 +0100"
      },
      "message": "chore(repo): prepare versions for 0.10.0 release (#4742)"
    },
    {
      "commit": "7d0ab2a174d0c81e5f8ae28e13a8fa37ee9bf49c",
      "tree": "167017f52bd7c1e41b1716ebf71a025420a0a30e",
      "parents": [
        "8f2c9ffd65e21ff04dba02f7cbd4cfd12adc62a5"
      ],
      "author": {
        "name": "tison",
        "email": "wander4096@gmail.com",
        "time": "Wed Nov 04 06:01:47 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 23:01:47 2026 +0100"
      },
      "message": "fix: preserve JSON numbers and synchronize mux cancellation tests (#4733)\n\nCloses #4732"
    },
    {
      "commit": "8f2c9ffd65e21ff04dba02f7cbd4cfd12adc62a5",
      "tree": "89de0639d3a21df2862e20d3c8960032a2682dba",
      "parents": [
        "ec6b651b2679f37c58ad0d5c03b3d9ec23fbe4fc"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Tue Nov 03 22:16:13 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 22:16:13 2026 +0100"
      },
      "message": "fix(server): clamp recovered consumer offsets after the WAL replay (#4737)\n\nCloses #4726"
    },
    {
      "commit": "ec6b651b2679f37c58ad0d5c03b3d9ec23fbe4fc",
      "tree": "3d4ce2f9045d5de5a3b09f639c986141e304795d",
      "parents": [
        "35915be1e8578bb76a9a83712e9e9ee94772f55e"
      ],
      "author": {
        "name": "Prabhath",
        "email": "14313773+udaya-arka@users.noreply.github.com",
        "time": "Wed Nov 04 02:13:40 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 21:43:40 2026 +0100"
      },
      "message": "fix(common): reject malformed user headers during deserialization (#4670)\n\n## Which issue does this PR address?\n\nCloses #4604\n\n## Rationale\n\nThe HTTP produce endpoint accepted typed user headers that violate the\nwire protocol\u0027s TLV bounds (0-byte values, widths mismatched with\nfixed-size types like `int32`, or values \u003e 255 bytes). When encoded by\n`user_headers_to_wire`, these headers corrupted the message TLV stream,\ncausing consumers polling the message to fail header decoding\n(`Err(InvalidHeaderValue)`) or silently drop all headers on the message\n(`Ok(None)`).\n\n\n## What changed?\n\n- **Marker-specific error mapping:** Added a sealed `HeaderFieldMarker`\ntrait on `KeyMarker` and `ValueMarker` returning\n`IggyError::InvalidHeaderKey` and `IggyError::InvalidHeaderValue`\nrespectively.\n- **Constructor validation:** Parameterized `HeaderField\u003cT\u003e::from_raw`\nby `T: HeaderFieldMarker`, returning the marker-specific error on empty\nfields, lengths \u003e 255 bytes, or fixed-kind width mismatches.\n- **Serde deserialization validation:** Implemented custom `Deserialize`\nfor `HeaderField\u003cT\u003e` that deserializes raw base64 data and routes it\nthrough `Self::from_raw()`, rejecting malformed keys/values at the\nHTTP/JSON boundary (HTTP 400).\n- **Defense-in-depth:** Added a `debug_assert!` in\n`user_headers_to_wire` verifying that the encoded buffer is structurally\nvalid TLV.\n- **Tests:** Enabled and passed the boundary rejection tests in\n`send_messages.rs`, and added unit tests for `HeaderField`,\n`HeaderEntry`, `HeaderKey`, `HeaderValue`, and `IggyMessage`\ndeserialization."
    },
    {
      "commit": "35915be1e8578bb76a9a83712e9e9ee94772f55e",
      "tree": "f4b6fd73beef6e6003d35483ea55aab456df6ec5",
      "parents": [
        "89dc4a2699ff3b8abcec3118f3ddf942eaa23ad9"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Tue Nov 03 17:58:36 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 17:58:36 2026 +0100"
      },
      "message": "fix(connectors): address Iceberg sink issues batch (#4736)"
    },
    {
      "commit": "89dc4a2699ff3b8abcec3118f3ddf942eaa23ad9",
      "tree": "db89125c3f52970609ffd1b3cc8eedd682418a1f",
      "parents": [
        "c190ae75c71ff83e0bbf6aeea2481d7a3888d9a5"
      ],
      "author": {
        "name": "Piotr Gankiewicz",
        "email": "piotr.gankiewicz@gmail.com",
        "time": "Tue Nov 03 16:52:36 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 16:52:36 2026 +0100"
      },
      "message": "feat(swift): publish the SDK to SwiftPM from the repository root (#4714)\n\nThe Swift SDK had no release path. SwiftPM resolves packages only from\na repository root with semver tags, the README pointed users at an\napache/iggy-swift mirror that does not exist, foreign/swift carried no\nLICENSE or NOTICE, and its version was not on the edge scheme the\nother SDKs release from.\n\nA root Package.swift now publishes the Iggy library from\nforeign/swift, and the publish workflow tags releases as v\u003cversion\u003e,\nthe way it tags the Go module. Post-merge publishes X.Y.Z-edge.N\nsnapshots, and a plain X.Y.Z is tagged only by a manual dispatch after\nthe release vote. The publish step builds the root manifest without a\nlockfile before tagging, as a consumer would. Pre-merge fails when the\nroot manifest drifts from foreign/swift/Package.swift, and\nbump-version.sh learns sdk-swift and regenerates the golden vectors\nthat embed the version.\n\nA root manifest keeps one repository, one tag namespace for SwiftPM\nand the existing GITHUB_TOKEN tag push. A mirror repository would need\na cross-repository credential, Infra authorization for automated\npushes and recovery for publishes spanning two repositories, only to\nsave consumers the first full clone.\n\nRelates to #4001"
    },
    {
      "commit": "c190ae75c71ff83e0bbf6aeea2481d7a3888d9a5",
      "tree": "a7975c6d99f87066f75047c4744a0e43d5e6bfaf",
      "parents": [
        "a941a94be207e854ec9eea048185b43cb82ad558"
      ],
      "author": {
        "name": "Tchoupinax",
        "email": "corentinfiloche@hotmail.fr",
        "time": "Tue Nov 03 15:28:53 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 15:28:53 2026 +0100"
      },
      "message": "ci: set a default advertised address for the local Docker image (#4635)"
    },
    {
      "commit": "a941a94be207e854ec9eea048185b43cb82ad558",
      "tree": "74c7c73323de56af307e5e2055955c21a2edbff0",
      "parents": [
        "d70f3668b919d2dc2fa7bfa42c47c33b57389688"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Tue Nov 03 18:13:21 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 13:43:21 2026 +0100"
      },
      "message": "fix(gateways): keep Kafka Fetch answering when one partition stalls (#4706)\n\nRelates to #4515"
    },
    {
      "commit": "d70f3668b919d2dc2fa7bfa42c47c33b57389688",
      "tree": "ad7f6e587d31f61cde0f53e62bcd981d2b4de999",
      "parents": [
        "3c265dd89eaf565ce0484164e30c22f86c3594bd"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Tue Nov 03 12:55:13 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 12:55:13 2026 +0100"
      },
      "message": "chore(deps): upgrade Rust dependencies (#4731)"
    },
    {
      "commit": "3c265dd89eaf565ce0484164e30c22f86c3594bd",
      "tree": "e4eecd66a67181020b79fe9222d5671db8116fd4",
      "parents": [
        "785a456c9279cf2133d7d5ddbd1bb5e439047324"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Tue Nov 03 07:25:37 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Nov 03 07:25:37 2026 +0100"
      },
      "message": "chore(deps): Bump the csharp group with 7 updates (#4712)\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e"
    },
    {
      "commit": "785a456c9279cf2133d7d5ddbd1bb5e439047324",
      "tree": "7ad3ec508240876c6b20541d232ebcf3f215a66b",
      "parents": [
        "eef3f688860f7b9ea0e5124cbdcd439942220fe4"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Mon Nov 02 20:56:49 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 02 20:56:49 2026 +0100"
      },
      "message": "docs(repo): document RFC labels and the flow after acceptance (#4705)"
    },
    {
      "commit": "eef3f688860f7b9ea0e5124cbdcd439942220fe4",
      "tree": "60c1123013acf483789f3ff59bdbbe8d0e2e804b",
      "parents": [
        "430d976ccfc638a0c2ee33541c2b71bf81e99c82"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Tue Nov 03 00:57:24 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 02 20:27:24 2026 +0100"
      },
      "message": "test(gateways): add failover tests for gateways beside a 3-node cluster (#4702)\n\nRelates to #4515"
    },
    {
      "commit": "430d976ccfc638a0c2ee33541c2b71bf81e99c82",
      "tree": "b3ffcf5ef7a14824d9693ec2a91e04cc61a9daa2",
      "parents": [
        "763c8edd93c1ba8af6d6dbac2c322bffc5672fca"
      ],
      "author": {
        "name": "Maciej Modzelewski",
        "email": "maciej@modzelewski.dev",
        "time": "Mon Nov 02 11:31:12 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 02 11:31:12 2026 +0100"
      },
      "message": "feat(java): let clients close without Netty\u0027s 2 s quiet period (#4704)\n\nA client that creates its own event loop group stopped it with\nNetty\u0027s shutdownGracefully(), which waits for 2 seconds with no new\ntasks. Every close took at least that long, so test suites that open\nand close many short-lived clients spent most of their time waiting.\n\nThe TCP builders now take shutdownQuietPeriod and shutdownTimeout.\nA value that the builder leaves unset comes from the system property\niggy.tcp.shutdownQuietPeriodMillis or iggy.tcp.shutdownTimeoutMillis.\nWithout the property, the Netty defaults of 2 and 15 seconds apply.\nA property changes every client in the JVM at once, also clients\nthat a library or a fixture builds, so the Gradle test task sets the\nquiet period to 0.\n\nbuild() rejects a negative value, a timeout below the quiet period,\nand a property that is not a whole number of milliseconds. The\nvalues do not apply to a group that the caller supplies, because the\ncaller shuts that group down."
    },
    {
      "commit": "763c8edd93c1ba8af6d6dbac2c322bffc5672fca",
      "tree": "c5913d97976acc15dd1bbb086844a0f08faf1e1d",
      "parents": [
        "c173126069c6b55a2efc0f02ce615a17ed999f6a"
      ],
      "author": {
        "name": "Piotr Gankiewicz",
        "email": "piotr.gankiewicz@gmail.com",
        "time": "Mon Nov 02 09:16:55 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 02 09:16:55 2026 +0100"
      },
      "message": "fix(sdk): resume stopped partitions and keep readiness waits current (#4693)\n\nA grouped fetch evaluates partitions that another shard owns against\na mirror of their state, and the owner pushes commits to that mirror\nonly while a wait parks on it. A wait that another ready partition\nended at once answered from the stale mirror, and the fetch read only\nthe partitions that the wait named, so a busy partition on one shard\nstarved a partition with records on another shard of the same node.\nSuch a wait now asks the owners again for the targets that a mirror\nwithout pushes found empty, reads a mirror once more when a push\nrefreshed it during the wait, and that mirror then gets the pushes\nof its partition while waits name it.\n\nThe reader stops a partition after six lasting refusals in a row and\nreads it again only after a seek, but the connectors sink never\nsought, and the C# and Node consumers, whose docs say that the reader\nretries by itself, never did either. The sink resumes such a\npartition through a new ReadError::is_lasting_refusal, the same\nclassifier that consume_messages uses. The C#, Node and Swift\nconsumers end with a stop, as consume_messages does, and say which\nerrors stop a partition and which a seek to Next resumes. A stop\nwhile a lost committing read is still pending reports that read, in\nRust, C# and Node, and the C# and Node readers give up a catch-up\ngap before every stop, so a seek after the stop skips nothing without\na word. A Go probe refusal no longer counts against its group mates.\n\nA committing reader started anywhere but Next never read a partition\nagain after a seek to Next, because the walk that resolves a start\nreturned early. It now returns early only when every partition is in\na group fetch.\n\nwith_binary_lineage and with_binary_deadline were async functions\nthat kept the wrapped future twice, which doubled every SDK call\nthat they wrap. Without that, the producer send future shrinks\nseveralfold and its layout gets shallower, with no allocation, so a\nlibrary can await a send under its own futures without boxing it."
    },
    {
      "commit": "c173126069c6b55a2efc0f02ce615a17ed999f6a",
      "tree": "ad81be27ac7ed58b0cd575b61d6e368572b40127",
      "parents": [
        "e8912af1df50d0d2e97f5f2fec73b54cbf11cfad"
      ],
      "author": {
        "name": "RustToMetal",
        "email": "thehodophile10@gmail.com",
        "time": "Mon Nov 02 01:20:24 2026 -0600"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 02 08:20:24 2026 +0100"
      },
      "message": "feat(swift): add connection strings, examples and BDD suite (#4679)\n\nRelates to #4001"
    },
    {
      "commit": "e8912af1df50d0d2e97f5f2fec73b54cbf11cfad",
      "tree": "6b4550817e69c41cec54fb0ae5ed1998ed17c4aa",
      "parents": [
        "42e3dafbe150661034f95f5b4c53a748fcef7692"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Mon Nov 02 02:10:44 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Nov 01 21:40:44 2026 +0100"
      },
      "message": "feat(partitions)!: add idempotent producers to Iggy (#4691)\n\nRelates to #4513"
    },
    {
      "commit": "42e3dafbe150661034f95f5b4c53a748fcef7692",
      "tree": "1d704c993dc89fced7f71b125c6ca1a2223933cb",
      "parents": [
        "3226d89f0801b6b813af37023f6e441b6aa967df"
      ],
      "author": {
        "name": "Radiance\u0027 波波",
        "email": "39521534+Radiancebobo@users.noreply.github.com",
        "time": "Mon Nov 02 03:54:03 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Nov 01 20:54:03 2026 +0100"
      },
      "message": "fix(configs): reject clusters exceeding VSR replica limit (#4661)\n\nCloses #4659"
    },
    {
      "commit": "3226d89f0801b6b813af37023f6e441b6aa967df",
      "tree": "b760e29bd77a0427b6cce3caeddd73efea12646c",
      "parents": [
        "fb7f7271be7d0139c0b86a185f28b5d82ed23895"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Mon Nov 02 00:37:54 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 02 00:37:54 2026 +0530"
      },
      "message": "feat(gateways): share Kafka consumer groups across multiple gateways in Iggy (#4681)\n\n## Which issue does this PR address?\n\nRelates to #4515\n\n## Rationale\n\nSince #4662, every gateway lists the others, but one gateway still holds\nevery consumer group in its memory. FindCoordinator names the gateway\nwith the lowest node id. While that gateway is down, every group waits\nfor it. When it restarts, it has lost every member, so every group\nrebalances.\n\nFor N gateways that cover for each other, the gateways must share the\ngroups. Any gateway must take over the groups of a gateway that stops,\nwith their members, generations and offsets. Kafka keeps this state in\n`__consumer_offsets`, and a new coordinator replays it. This PR keeps it\nin an Iggy topic in the same way.\n\n## What changed?\n\nThe gateways share their groups through a coordinator log: the Iggy\ntopic `coordinators`, with 16 partitions, in the stream `\u003cdefault\nstream\u003e.meta`. A hash of the group id picks the partition. Every gateway\njoins the Iggy consumer group `kafka-gateways` on that topic, and Iggy\nspreads the partitions over the gateways. No gateway polls through that\ngroup, so Iggy hands a partition over at once. A gateway that Iggy gives\na partition reads it from its last checkpoint. Then it appends a claim,\nnumbered above every claim before it. While its claim is the newest, it\ncoordinates the groups of that partition.\n\nWhen the leader\u0027s SyncGroup completes a generation, the coordinator\nstores the whole group. The members get their assignments only after the\ngateway reads that record back. The coordinator also appends each offset\ncommit, with its metadata and leader epoch, and each deletion. It reads\neach append back. If a newer claim comes before the record, the record\ndoes not count. The coordinator then answers NOT_COORDINATOR (16) and\nstops serving the partition. So a gateway that lost a partition cannot\nchange it. Each owner follows its partitions with the waiting read of\n#4629. It writes a checkpoint after 10,000 records, and at least once a\nday. It announces itself on the topic `coordinator_owners`, which every\ngateway reads."
    },
    {
      "commit": "fb7f7271be7d0139c0b86a185f28b5d82ed23895",
      "tree": "2f73b39fbbf7b224584c68526f10c30262b7b057",
      "parents": [
        "f9ba48a998ea6892dfe7818b05dd1c4d7f643f51"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Sun Nov 01 07:29:11 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Nov 01 07:29:11 2026 +0100"
      },
      "message": "feat(cluster)!: add per-topic replication factor (#4692)\n\nCloses #4663"
    },
    {
      "commit": "f9ba48a998ea6892dfe7818b05dd1c4d7f643f51",
      "tree": "93f19c89f4a4a182d7baf13a57526085c614c71a",
      "parents": [
        "af99cd9f74680495280aa69ad3e71152c7b20e8e"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Sat Oct 31 13:20:23 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 31 13:20:23 2026 +0100"
      },
      "message": "fix(cluster)!: let slow replicas catch up under load (#4658)"
    },
    {
      "commit": "af99cd9f74680495280aa69ad3e71152c7b20e8e",
      "tree": "ee4c27edb3ba827f7ecd3e00ad1787979f380725",
      "parents": [
        "4eccc5e2c4203c1cd78af9a6441111dae88f8768"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Sat Oct 31 08:33:42 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 31 08:33:42 2026 +0100"
      },
      "message": "feat(server)!: replace readiness waits with grouped deferred fetches (#4683)"
    },
    {
      "commit": "4eccc5e2c4203c1cd78af9a6441111dae88f8768",
      "tree": "8f75018b6f5a2e9f839d02aee7498cbc672c2fae",
      "parents": [
        "bdc41d49f2ba16eefe8c1577edc2f424341de783"
      ],
      "author": {
        "name": "sunilkumarpradhan",
        "email": "sunilkumarweb47@gmail.com",
        "time": "Fri Oct 30 23:27:34 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 30 18:57:34 2026 +0100"
      },
      "message": "fix(cli): use correct consumer kind wording in consumer-offset set (#4620)"
    },
    {
      "commit": "bdc41d49f2ba16eefe8c1577edc2f424341de783",
      "tree": "8977305d6f8d43dace079fff72caef0bf4a7b2c6",
      "parents": [
        "5d37d244449f8a8ebdb3ed399fdfc6e83dd10886"
      ],
      "author": {
        "name": "Souyama",
        "email": "souyamadebnath@gmail.com",
        "time": "Fri Oct 30 20:39:04 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 30 16:09:04 2026 +0100"
      },
      "message": "feat(server): generic external authentication callout (#4634)"
    },
    {
      "commit": "5d37d244449f8a8ebdb3ed399fdfc6e83dd10886",
      "tree": "340e868a61dd44fb86419c727e65acf70db1dcda",
      "parents": [
        "dcd6dbe5b6b00fbc6130aa660bea18f21bd723b3"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Oct 30 14:03:55 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 30 14:03:55 2026 +0100"
      },
      "message": "chore(deps): Bump the web group across 1 directory with 5 updates (#4688)"
    },
    {
      "commit": "dcd6dbe5b6b00fbc6130aa660bea18f21bd723b3",
      "tree": "6067522e956dcddfc1a8562535e264e2040bf6d9",
      "parents": [
        "27b13088adf6b30779da1773b7ecec802bc154b8"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Oct 30 13:41:35 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 30 13:41:35 2026 +0100"
      },
      "message": "chore(deps): Bump the node group across 2 directories with 14 updates (#4690)"
    },
    {
      "commit": "27b13088adf6b30779da1773b7ecec802bc154b8",
      "tree": "5528f7dca2752f60ec11429c599e47cf689c2d2c",
      "parents": [
        "a05636a9849392ddb79a33d9df13893aac4c3c53"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Thu Oct 29 21:45:05 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 21:45:05 2026 +0100"
      },
      "message": "fix(integration): avoid rejoin race in segment deletion test (#4687)"
    },
    {
      "commit": "a05636a9849392ddb79a33d9df13893aac4c3c53",
      "tree": "d6578bf7ffe0a637366d82b736e811d85b4b1fc0",
      "parents": [
        "36cfd9529ab97c6cc86c3931c4fa82a7aa3d44cc"
      ],
      "author": {
        "name": "Parthiban Rajasekaran",
        "email": "36532774+ParthibanRajasekaran@users.noreply.github.com",
        "time": "Thu Oct 29 16:39:38 2026 +0000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 17:39:38 2026 +0100"
      },
      "message": "fix(connectors): reject ambiguous proto field mappings (#4581)"
    },
    {
      "commit": "36cfd9529ab97c6cc86c3931c4fa82a7aa3d44cc",
      "tree": "c45b6756378194ca0c644b6b191a3651f02bfaa7",
      "parents": [
        "975277b0fcf3e70f36cb2bfb3ad57510837bf8c6"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Oct 29 13:03:17 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 13:03:17 2026 +0100"
      },
      "message": "chore(deps): Bump the java group across 3 directories with 3 updates (#4665)"
    },
    {
      "commit": "975277b0fcf3e70f36cb2bfb3ad57510837bf8c6",
      "tree": "99c8bcc6652d52b1d21fe6aebe0c28a97ec44ccf",
      "parents": [
        "e352181c37da70caa4faef06b6697cfc1a29679a"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Oct 29 11:58:18 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 11:58:18 2026 +0100"
      },
      "message": "chore(deps): Bump zip from 9.0.3 to 9.0.5 in the rust-security group across 1 directory (#4678)"
    },
    {
      "commit": "e352181c37da70caa4faef06b6697cfc1a29679a",
      "tree": "e6d5200ff7b7c38d4c75ec540c8d5da8ddca1df2",
      "parents": [
        "9ac27ee07409c4eeb737457f5b296e03674d7465"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Oct 29 10:46:53 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 10:46:53 2026 +0100"
      },
      "message": "chore(deps): Bump gradle from 9.8.0-jdk17 to 9.8.1-jdk17 in /bdd/java in the docker group across 1 directory (#4676)"
    },
    {
      "commit": "9ac27ee07409c4eeb737457f5b296e03674d7465",
      "tree": "644a7967887d91a8746ff5873bf400cb5b514b9f",
      "parents": [
        "c92d2bd907cf2f2829f29113a9a20cc05b8d62b5"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Oct 29 10:27:50 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 10:27:50 2026 +0100"
      },
      "message": "chore(deps): Bump the python group across 3 directories with 2 updates (#4677)"
    },
    {
      "commit": "c92d2bd907cf2f2829f29113a9a20cc05b8d62b5",
      "tree": "9dcdcee4024a9a09689c69962ebac95f68fb5b61",
      "parents": [
        "153fdf466aea94b7bcb90681ee7274f9f76ec00d"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Thu Oct 29 14:21:09 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 09:51:09 2026 +0100"
      },
      "message": "feat(gateways): list every Kafka gateway and dial other Iggy nodes (#4662)"
    },
    {
      "commit": "153fdf466aea94b7bcb90681ee7274f9f76ec00d",
      "tree": "840ae91206d69fd330b56c51c4af3c12349b29a8",
      "parents": [
        "922c6d14a09456fe1c2002bf17e752b442234db8"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Thu Oct 29 11:35:25 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 07:05:25 2026 +0100"
      },
      "message": "fix(gateways): stop Kafka Produce sends at the request deadline (#4643)"
    },
    {
      "commit": "922c6d14a09456fe1c2002bf17e752b442234db8",
      "tree": "5f36cffac1c9602e2e7fa531a5d7cb877b2f140f",
      "parents": [
        "c0f05036c1aa38d8e61998d1ecf4e6e4c79e0d95"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Thu Oct 29 05:27:22 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 00:57:22 2026 +0100"
      },
      "message": "test(cluster): test the Kafka gateway and the SDK on split primaries (#4642)"
    },
    {
      "commit": "c0f05036c1aa38d8e61998d1ecf4e6e4c79e0d95",
      "tree": "cf16ed50353852945a6ba33b5fd71df908c8d2d0",
      "parents": [
        "64004157829fbdcbfa9a3d10ad35a81a93d7a2f6"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Thu Oct 29 00:27:29 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 00:27:29 2026 +0530"
      },
      "message": "feat(gateways): report Kafka group members\u0027 client id and host (#4651)\n\n## Which issue does this PR address?\n\nCloses #4511\n\n## Rationale\n\n`kafka-consumer-groups.sh --describe --members` showed an empty\nCLIENT-ID and HOST for every member of a gateway group. Operators use\nthese two columns to find the process behind a consumer. Kafka fills\nboth from the `JoinGroup` request: the client id from the request\nheader, and the host from the address of the connection. The gateway\nhandlers saw neither.\n\n## What changed?\n\nEach connection now keeps the first client id that a request header\nnames, cut to 1,024 bytes. It also keeps the peer IP address in Kafka\u0027s\n`/10.0.0.7` form. A member takes both from the connection that it last\njoined through, and DescribeGroups reports them. A new member id now\nstarts with the client id, as in Kafka, where it used to start with\n`member`."
    },
    {
      "commit": "64004157829fbdcbfa9a3d10ad35a81a93d7a2f6",
      "tree": "8b88ebe4a0bb446827bdd8f9ab92f0bdbb01db85",
      "parents": [
        "d6e42a7cd51d1457a7964dd52ec742ac5874a8a2"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Wed Oct 28 22:51:21 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 18:21:21 2026 +0100"
      },
      "message": "fix(gateways): survive Kafka accept errors and a late Iggy at startup (#4645)"
    },
    {
      "commit": "d6e42a7cd51d1457a7964dd52ec742ac5874a8a2",
      "tree": "67d98e0f7b4511fa9e5da4901dd0d1c48557f857",
      "parents": [
        "c938707c2321f2479a10fd5485892e0c49c99444"
      ],
      "author": {
        "name": "Maciej Modzelewski",
        "email": "maciej@modzelewski.dev",
        "time": "Wed Oct 28 11:15:23 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 11:15:23 2026 +0100"
      },
      "message": "fix(ci): stop Markdown lint hanging on a Node 23 download (#4669)\n\nNode 23 is in neither the runner tool cache nor the\nactions/node-versions manifest, so setup-node downloaded it from\nnodejs.org on every run. That usually took 8 to 39 seconds, but on\n2026-10-28 the download stalled until the 10-minute job timeout\ncancelled Markdown lint.\n\nEvery CI pin on Node 22 or 23 now uses 24, the active LTS. The\nubuntu-26.04 image caches 24, and the manifest hosts it on GitHub, so\nsetup-node no longer falls back to nodejs.org. The devcontainer moves\nto 24 too, because it pins the major version of the web UI release\nbuild.\n\nubuntu-slim has no tool cache but ships Node 24, so Markdown lint\ndrops setup-node and prints the Node and npm versions it got."
    },
    {
      "commit": "c938707c2321f2479a10fd5485892e0c49c99444",
      "tree": "bdfc918d95222e0cdb78a0d7bff78bcd3cc0d080",
      "parents": [
        "b9bca85b2f0aefe242968c45af46b2ffccc9c09a"
      ],
      "author": {
        "name": "Subham",
        "email": "subham.sangwan@adypu.edu.in",
        "time": "Wed Oct 28 15:14:23 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 10:44:23 2026 +0100"
      },
      "message": "feat(cpp): expose create_personal_access_token (#4614)\n\nCloses #4613"
    },
    {
      "commit": "b9bca85b2f0aefe242968c45af46b2ffccc9c09a",
      "tree": "85afc647f122016f5194bc33b3e49055818dd7f7",
      "parents": [
        "419be1a08003d1dd216694bcefcb7496a005a847"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Wed Oct 28 09:19:18 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 09:19:18 2026 +0100"
      },
      "message": "refactor(gateways): extract the Kafka wire layer into iggy_kafka_wire (#4664)"
    },
    {
      "commit": "419be1a08003d1dd216694bcefcb7496a005a847",
      "tree": "63c36f0aaa3b1c108bf567edcb95cee56323209c",
      "parents": [
        "d47ff5ddd88242951360d1e8bf110cd1090f414e"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Wed Oct 28 06:16:38 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 06:16:38 2026 +0100"
      },
      "message": "feat(message_bus)!: spread replica traffic over k links per peer (#4656)\n\nEach pair of nodes shared one replica link, and one shard per node\nowned it. Every other shard forwarded its consensus frames through\nthat shard. In September OMB runs with 100 topics, one node led every\npartition, and its link shard used a full core. In October bench runs\non AWS, each replica link carried at most about 1.2 GB/s. If that cap\nholds per TCP connection, k links raise it.\n\ncluster.coordinator.replica_connections_per_peer sets k. Link j\ncarries the frames of a contiguous range of shards, and on each node\none shard of that range owns it. Frames for the metadata group take\nlink 0. No benchmark used k \u003e 1 yet, so the default stays 1.\n\nBefore TLS and the hello, the dialer sends an 8-byte preamble with its\nreplica id and the link. Shard 0 of the acceptor reads only those\nbytes and hands the socket to the owner of the link. The hello carries\nk and the link, and with auth on the MAC covers them. The owner\nrefuses a hello that does not match the preamble or its own k.\n\nFrames of a shard range have only their own link, because a fallback\nto another link can reorder them. So shard 0 tracks the k links to a\npeer as one bundle. A node sends to that peer only while its bundle is\nup. If a link of the bundle closes, or the bundle stays incomplete for\nmessage_bus.handshake_grace, shard 0 closes all k. The node with the\nlower id then dials them again. A failed handshake changes no bundle\non its node, because an inbound hello is not authenticated before it\ncompletes.\n\nA peer that died without a FIN or RST left a half-open link. VSR\nheartbeats told consensus that the peer was gone, but nothing closed\nthe socket. The link kept its owner slot, so the reconnect sweep\nskipped the peer. With Linux defaults, the kernel closed the link only\nafter at least 15 minutes of retransmits. Replica sockets now get\nTCP_USER_TIMEOUT on Linux, equal to cluster.heartbeat_timeout. From\nLinux 5.11 it also covers a zero window, so a peer that stops reading\nloses its link too.\n\nEvery link sends the preamble, also at k \u003d 1. So a node with this\nchange and a node without it never link, even on the same release. An\nupgrade must restart every node at once. Nodes with another k refuse\neach other. Each node places links on its own shards, so shard counts\nmay differ, as before.\n\nShard metrics now count bundles that are up, teardowns, preamble\nrefusals, handshake failures and frames dropped at a full peer queue.\nThe connection cache had no users, so this change deletes it and rand,\nwhich only the cache used in message_bus.\n\nBREAKING CHANGE: replica links open with an 8-byte placement preamble\nand the hello carries the link count, so nodes from earlier builds and\nnodes with this change never link. Upgrade every replica together and\nrun the same cluster.coordinator.replica_connections_per_peer on every\nnode."
    },
    {
      "commit": "d47ff5ddd88242951360d1e8bf110cd1090f414e",
      "tree": "6a7a4c8e1ffb9873d63526a2080179bdf1f4ef1f",
      "parents": [
        "deda7c4e426038edc96030fcd558314123f8ebb3"
      ],
      "author": {
        "name": "Justin Mclean",
        "email": "jmclean@apache.org",
        "time": "Tue Oct 27 14:26:31 2026 +0000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 15:26:31 2026 +0100"
      },
      "message": "docs(mcp): explain consumer offsets and HTTP access (#4610)\n\nRelates to #4589"
    },
    {
      "commit": "deda7c4e426038edc96030fcd558314123f8ebb3",
      "tree": "a40b51ff81da9de7e0c4695818391199fbf6b64e",
      "parents": [
        "f8dab5c9330f54eda09a92563346b4087150c439"
      ],
      "author": {
        "name": "Manas Choksi",
        "email": "162600900+choksi2212@users.noreply.github.com",
        "time": "Tue Oct 27 19:10:27 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 14:40:27 2026 +0100"
      },
      "message": "feat(go): parse iggy:// connection strings into TCP client options (#4497)"
    },
    {
      "commit": "f8dab5c9330f54eda09a92563346b4087150c439",
      "tree": "d67e030248b35c426436d9bae2570e9d7ca93848",
      "parents": [
        "57ac178fbbfb56545ddc26af5513f04fa4b007ef"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Tue Oct 27 11:28:33 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 11:28:33 2026 +0100"
      },
      "message": "chore(deps): Bump Microsoft.Extensions.Configuration and 7 others (#4654)"
    },
    {
      "commit": "57ac178fbbfb56545ddc26af5513f04fa4b007ef",
      "tree": "e6d985e8d443782a795586c02d6347d9c10baa85",
      "parents": [
        "6e9ca2e277899012a64856ece66591312d8cac8c"
      ],
      "author": {
        "name": "Ryan Huang",
        "email": "ryankert01@gmail.com",
        "time": "Tue Oct 27 17:54:50 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 10:54:50 2026 +0100"
      },
      "message": "test(integration): drop the elasticsearch nextest test-group (#4555)"
    },
    {
      "commit": "6e9ca2e277899012a64856ece66591312d8cac8c",
      "tree": "d4466e39a4fcbc961f4176ef5b508dfb6419798e",
      "parents": [
        "dc4006d92f0565dd0ae26e842a3433b51e3c3f1b"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Tue Oct 27 08:36:22 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 08:36:22 2026 +0100"
      },
      "message": "fix(java): keep a queued sign-in from failing as replaced (#4652)"
    },
    {
      "commit": "dc4006d92f0565dd0ae26e842a3433b51e3c3f1b",
      "tree": "6f27a7fb94286c994805870c7b32e171df50664b",
      "parents": [
        "8c9b9d2e59d1358679839393bdd81085f424bbbf"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Tue Oct 27 08:05:31 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 08:05:31 2026 +0100"
      },
      "message": "feat(server)!: add bounded deferred (long) polling (#4629)"
    },
    {
      "commit": "8c9b9d2e59d1358679839393bdd81085f424bbbf",
      "tree": "ffbf13f69269e959def5d8c45352c92d3103895c",
      "parents": [
        "f43ecc4eea5fce971f1c9b20190131a08056ee62"
      ],
      "author": {
        "name": "Maciej Modzelewski",
        "email": "maciej@modzelewski.dev",
        "time": "Mon Oct 26 23:58:19 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 23:58:19 2026 +0100"
      },
      "message": "test(partitions): expose bugs when a slow replica rejoins the cluster (#4653)"
    },
    {
      "commit": "f43ecc4eea5fce971f1c9b20190131a08056ee62",
      "tree": "38761da90797025531fb9f12e1bf40f5cbfe6fc6",
      "parents": [
        "6ce2f92a9f9650242caf09997e7ec6c2b4c763df"
      ],
      "author": {
        "name": "RustToMetal",
        "email": "thehodophile10@gmail.com",
        "time": "Mon Oct 26 12:49:13 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 18:49:13 2026 +0100"
      },
      "message": "feat(swift): add the consumer (#4641)"
    },
    {
      "commit": "6ce2f92a9f9650242caf09997e7ec6c2b4c763df",
      "tree": "e3a2488e61059c490cb01f05c510f7aa1dfb3d44",
      "parents": [
        "ec97c3e0a9ffc6c64cd31384cc533cbe463077fe"
      ],
      "author": {
        "name": "ByteBaker",
        "email": "42913098+ByteBaker@users.noreply.github.com",
        "time": "Sun Oct 25 23:04:37 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Oct 25 18:34:37 2026 +0100"
      },
      "message": "refactor(sdk): delete code through refactor (#4571)"
    },
    {
      "commit": "ec97c3e0a9ffc6c64cd31384cc533cbe463077fe",
      "tree": "360956de3ee5bf90e6344427579db272b1270aee",
      "parents": [
        "bfc805c27701b1ffbd623dc2fa978783be8584fe"
      ],
      "author": {
        "name": "Piotr Gankiewicz",
        "email": "piotr.gankiewicz@gmail.com",
        "time": "Sun Oct 25 12:06:56 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Oct 25 12:06:56 2026 +0100"
      },
      "message": "feat(sdk): send a raw request to a chosen replica (#4639)\n\nA client could not read the state of one particular node. Each node\nkeeps its own stream, topic and system stats, but every official SDK\nsends a raw request, and every typed command other than the five\npartition commands, to the metadata primary. The client discovers the\nroster and keeps a link per replica, yet the choice of link stays\ninternal. Tools, dashboards and tests could read only the metadata\nprimary, and a client could not spread reads that any node answers.\n\nAdd one function per SDK that sends a raw request to the replica id\nthat cluster metadata names. Rust gains\n`IggyClient::send_binary_request_to_replica` and\n`BinaryTransport::send_raw_to_replica,` Node.js\n`sendBinaryRequestToReplica` and a replica option on `sendCommand`, Go\n`SendBinaryRequestToReplica`, Java `sendBinaryRequestToReplica` on the\nasync, blocking and HTTP clients, C# `SendBinaryRequestToReplicaAsync`,\nSwift `sendRawRequest(code:payload:replica:)`, and the Python, C++ and\nPHP bindings expose the Rust method.\n\nThe request travels on that replica\u0027s link with the session\u0027s metadata\nwatermark, so the replica answers after it applied the metadata the\nclient saw. Replicated commands, polls and session control fail with\n`InvalidCommand` before anything is sent. An id the roster does not name\nafter one roster read fails with `InvalidIdentifier`. A lost link or\n`TransientNotAccepted` retries the same replica until the deadline, and\n`NotPrimary` is returned rather than re-routed, because the caller chose\nthe node. HTTP has no raw binary path and returns `FeatureUnavailable`,\nas the existing raw request does.\n\nA new function keeps every existing caller compiling, and the trait\nmethod has a default so external `BinaryTransport` implementations do\ntoo. Reusing `InvalidIdentifier` avoids a new code in every SDK\u0027s error\ntable. No command code, wire format or server behavior changes."
    },
    {
      "commit": "bfc805c27701b1ffbd623dc2fa978783be8584fe",
      "tree": "b3026c4d118778cfc152799ced5e593d7207aca8",
      "parents": [
        "b1adfe5d5d414b7158b28338400491c88fd14467"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Sat Oct 24 20:23:19 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 20:23:19 2026 +0200"
      },
      "message": "ci: run 4 sliced test shards without disk cleanup (#4637)\n\nhash partitioning stacked slow tests on one shard: in apache run\n40076155357 the slowest of three shards ran nextest for 902s. slice\ndeals\nthe sorted test list round-robin, and with four shards the slowest\nnextest run took 700s in a fork trial.\n\nTest shards have about 78 GB free when tests start and the suite uses\nabout 15 GB, so the free-disk-space cleanup spent 1.5 to 4 minutes per\nshard reclaiming space that was never needed. The shard now prints free\nspace before and after the tests to keep that margin visible.\n\nFixture generation ran `cargo run -p kafka-message-gen`, which resolves\nfeatures for that one package and recompiled the workspace crates under\nit for about 33s. The workspace build already produced the binary, so\nthe build step now runs it and falls back to `cargo run` only when the\nruntime build was skipped.\n\ncargo-nextest moves to 0.9.149, which keeps the 0.9.127 floor that slice\nneeds, and the review bot\u0027s Claude Code pin moves to 2.1.310."
    },
    {
      "commit": "b1adfe5d5d414b7158b28338400491c88fd14467",
      "tree": "242c9a2080fea8f50e17b4478d5d44bd38da1dff",
      "parents": [
        "ce56aa13039657aaa7534e3e5a4d1f0142911ab7"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Sat Oct 24 14:04:17 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 14:04:17 2026 +0200"
      },
      "message": "fix(go): reject malformed user headers in NewIggyMessage (#4481)\n\nCloses #4471\n\nCo-authored-by: Mark Aron Szulyovszky \u003cmark.szulyovszky@gmail.com\u003e"
    },
    {
      "commit": "ce56aa13039657aaa7534e3e5a4d1f0142911ab7",
      "tree": "f2b65a5fbeeab161e6fd451ff8d7f22e798d52b8",
      "parents": [
        "b818d646b43c041c6bf9d1bdea9866748685901f"
      ],
      "author": {
        "name": "Tom Planche",
        "email": "tomplanche@proton.me",
        "time": "Sat Oct 24 12:15:46 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 12:15:46 2026 +0200"
      },
      "message": "fix(sdk): end a reconnect sweep\u0027s pause on disconnect and shutdown (#4569)"
    },
    {
      "commit": "b818d646b43c041c6bf9d1bdea9866748685901f",
      "tree": "f4d18c09fae4c823e88908db4c06dacad228ead2",
      "parents": [
        "f7dc8cfe224848ebc056f389d6f700dff1958c7c"
      ],
      "author": {
        "name": "Piotr Gankiewicz",
        "email": "piotr.gankiewicz@gmail.com",
        "time": "Sat Oct 24 11:11:52 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 11:11:52 2026 +0200"
      },
      "message": "feat(metadata)!: let clients follow metadata changes without re-listing (#4625)\n\nAnything that mirrors Iggy metadata had to list every stream, then\nthe topics of every stream, then the consumer groups of every topic,\non every refresh. The metadata journal could not replace that: a\ncheckpoint drains it, it holds requests rather than results, it\ncarries password, token and wrapped stream key material, and it hides\nside effects such as the grants a delete prunes. Nothing recorded who\nchanged what.\n\nKeep a bounded log of the public results of committed metadata\noperations on every node. Handlers describe each result in the apply\nreply, both apply paths append the records before the applied\nfrontier moves, and the log rides in the metadata snapshot, so it\nsurvives restarts and moves with state transfer. Records are encoded\nonce through the option projection the read routes use, so stream\nkeys, wrapped keys and every secret or internal option stay on the\nserver. Topics now keep the op that created them, as streams do, so a\nrecreated id is never mistaken for its predecessor.\n\nServe the log as metadata.changes (code 17) on TCP, QUIC and\nWebSocket and as GET /metadata/changes, answered by the receiving\nnode and gated by manage_servers. A caller pages records after a\ncursor, filtered by groups and a prepare timestamp range. When its\ncursor expires it pages the redacted current state, anchored to a\ncursor taken before the scan, and replays the real changes after it.\nEvery SDK supports the command on each of its existing client\ntransports, the CLI follows it, and the web UI lists it for accounts\nthat may read it.\n\nRecords come from the handlers rather than from diffing state,\nbecause only a handler knows the assigned id, the previous values and\nthe deleted resource. Only committed and applied operations are\nrecorded, so a cursor from one replica is valid on another, and a\nlagging node answers behind instead of skipping records.\n\nBREAKING CHANGE: data directories need snapshot format 14 and the new\nstorage tag, topics persist their creating op, and the Rust\nSystemClient trait gains get_metadata_changes."
    },
    {
      "commit": "f7dc8cfe224848ebc056f389d6f700dff1958c7c",
      "tree": "6247188b0c86afbe508c35bc423fff86a9756880",
      "parents": [
        "dbb9d2cced59c01e5057582d518512a3940404a2"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Sat Oct 24 10:28:27 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 10:28:27 2026 +0200"
      },
      "message": "fix(partitions): finish state transfer for a replica whose view ran ahead (#4483)\n\nCloses #4438\n\nCo-authored-by: Mark Aron Szulyovszky \u003cmark.szulyovszky@gmail.com\u003e"
    },
    {
      "commit": "dbb9d2cced59c01e5057582d518512a3940404a2",
      "tree": "08ba72892e1227d02295f0627a242cd0d7ef8f1d",
      "parents": [
        "405c84f5b58fec0cb827bacb9be584df0c7a5daf"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat Oct 24 09:54:28 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 09:54:28 2026 +0200"
      },
      "message": "chore(deps): Bump the java group across 3 directories with 2 updates (#4600)"
    },
    {
      "commit": "405c84f5b58fec0cb827bacb9be584df0c7a5daf",
      "tree": "302045968a5a1a99226c3666a6956ee25d7ebe89",
      "parents": [
        "aaa875f5baf6dc3ebd6fcfdf09c0b74b32f1bfc5"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat Oct 24 09:30:15 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 09:30:15 2026 +0200"
      },
      "message": "chore(deps): Bump fast-uri from 3.1.8 to 3.1.9 in /foreign/node in the node-security group across 1 directory (#4630)"
    },
    {
      "commit": "aaa875f5baf6dc3ebd6fcfdf09c0b74b32f1bfc5",
      "tree": "c0934eec7bef33caf91fd82a55f67afa90cdc6fd",
      "parents": [
        "f10a0309beeb205b439083a3824c72b734acbdb7"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat Oct 24 09:21:14 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 09:21:14 2026 +0200"
      },
      "message": "chore(deps): Bump the web group across 1 directory with 8 updates (#4621)"
    },
    {
      "commit": "f10a0309beeb205b439083a3824c72b734acbdb7",
      "tree": "e6da6059fd900ceca1496904b7fd8f4fb44f3db2",
      "parents": [
        "b15c8e1e9a24470e3d273b3983bc856335068f3a"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat Oct 24 09:06:02 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 09:06:02 2026 +0200"
      },
      "message": "chore(deps): Bump the github-actions group across 1 directory with 3 updates (#4599)"
    },
    {
      "commit": "b15c8e1e9a24470e3d273b3983bc856335068f3a",
      "tree": "94bc6b4d5ec53a52d36089df62d6ed19db3411c6",
      "parents": [
        "969246ba772a2979e6badd796748505aaf1d34d5"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Sat Oct 24 02:15:10 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 02:15:10 2026 +0200"
      },
      "message": "chore: simplify team review and reuse CI results (#4592)"
    },
    {
      "commit": "969246ba772a2979e6badd796748505aaf1d34d5",
      "tree": "fc45a4c431f584bda3d8431ac2aaca4caa8b16dd",
      "parents": [
        "d5a74c955e83a337af322080dc145464d0ca9c40"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat Oct 24 01:33:59 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 01:33:59 2026 +0200"
      },
      "message": "chore(deps): Bump the python group across 3 directories with 1 update (#4596)"
    },
    {
      "commit": "d5a74c955e83a337af322080dc145464d0ca9c40",
      "tree": "dd83b7277820c799f3c6414882cef56f45848eb5",
      "parents": [
        "44c7dc834533f8b94c8562a938874eab3b704155"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Sat Oct 24 01:05:36 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 01:05:36 2026 +0200"
      },
      "message": "feat(ci): label prolific authors when pull requests open (#4622)"
    },
    {
      "commit": "44c7dc834533f8b94c8562a938874eab3b704155",
      "tree": "41b0d64f94a3ab57b2831aaac162da37ec6842da",
      "parents": [
        "ea8c68e9152fc385b978dee2a9153ef3735aebe8"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Sat Oct 24 00:20:26 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 24 00:20:26 2026 +0200"
      },
      "message": "fix(consensus): retry the primary\u0027s own write on prepare timeout (#4457)"
    },
    {
      "commit": "ea8c68e9152fc385b978dee2a9153ef3735aebe8",
      "tree": "eb1df6cbfb1d84abe7aa4e94d64396dfaa92770e",
      "parents": [
        "a83e8e85da5c9d6271102c1a15cb3f78883ff730"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Fri Oct 23 22:57:32 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 22:57:32 2026 +0200"
      },
      "message": "perf(ci): reduce Miri scope and avoid duplicate Java tests (#4626)"
    },
    {
      "commit": "a83e8e85da5c9d6271102c1a15cb3f78883ff730",
      "tree": "d126d77e72a5d526ff61b96f3695d7d252dbcfd8",
      "parents": [
        "5d51cb6c834fb45a965536c915d3297c90ffdc42"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Fri Oct 23 21:52:24 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 21:52:24 2026 +0200"
      },
      "message": "fix(partitions): make writer-lease drain wait executor-agnostic (#4434)\n\nCloses #4139"
    },
    {
      "commit": "5d51cb6c834fb45a965536c915d3297c90ffdc42",
      "tree": "eacf3d02ea1eaaa0a129d69022831aff62d36729",
      "parents": [
        "a307176846a90ea01c740c0027e46b47d30eeffb"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Fri Oct 23 20:45:54 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 20:45:54 2026 +0200"
      },
      "message": "fix(partitions): don\u0027t commit a stale prepare after adopting a newer view (#4433)\n\nCloses #4284\n\nCo-authored-by: Mark Aron Szulyovszky \u003cmark.szulyovszky@gmail.com\u003e"
    },
    {
      "commit": "a307176846a90ea01c740c0027e46b47d30eeffb",
      "tree": "16e44983e3f813ea928f6e34ef81aba619cecd50",
      "parents": [
        "3b3013594bb6fe072a1e15ae3f0ab1eac3f09e7f"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Fri Oct 23 19:10:47 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 19:10:47 2026 +0200"
      },
      "message": "perf(ci): improve cache reuse and reduce Miri workload (#4624)"
    },
    {
      "commit": "3b3013594bb6fe072a1e15ae3f0ab1eac3f09e7f",
      "tree": "f5b65e45920a4550223ebdc77063e69060cfefe3",
      "parents": [
        "02dac78967f885d14b10868694d7842ff692293f"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Fri Oct 23 15:46:23 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 15:46:23 2026 +0200"
      },
      "message": "fix(consensus): keep committed ops when restarted replicas elect (#4500)\n\n## Which issue does this PR address?\n\nCloses #4266\nCloses #4476\n\nStacked on #4485, which adds the failing test this PR turns green.\n\n## Rationale\n\nUnder `replicated` durability a replica acknowledges a prepare once it\nis queued to the WAL writer, before it is written, so a replica that\nrestarts can be missing ops it acknowledged. Restarted partition\nreplicas came up `Recovering` and probed for the current view, but after\n5 unanswered probes they elected on their on-disk log as if it were\ncomplete, and their DoViewChange looked like any other. After two\ncrashes in quick succession the restarted replicas formed the\nview-change quorum without the only replica holding the committed ops,\ndropped them, and that replica later failed the WAL parent check and\nshut its whole node down (#4476). A single crash plus a partition could\ndo the same through a trusted, lagging candidate.\n\n## What changed?\n\nA partition replica that rejoins on a log recovered from disk under\n`replicated` durability marks its log untrusted and sets a new\n`FLAG_UNTRUSTED_LOG` bit in every DoViewChange it sends. Every candidate\nmerges only trusted logs once it has a view-change quorum of them;\notherwise it waits until every replica has reported and does not treat a\nshorter log as a nack. Any quorum of trusted replicas overlaps every\ncommit quorum in a replica that never lost state, so a committed op\nalways survives. An untrusted replica never becomes Normal, acknowledges\nnothing and accepts no writes until it adopts a settled view.\n\nA replica that cannot use its log (its partition is awaiting a state\ntransfer) answers a StartViewChange with an empty, flagged DoViewChange.\nIt counts toward every replica reporting but can never be chosen as the\nlog or drop an op, so a full restart no longer halts on it. When a\nreplica adopts a view that contradicts history it already applied, only\nthat partition is quarantined to `.fenced.N` and rebuilt through state\ntransfer at the offset frontier both histories agree on, instead of the\nnode stopping on the WAL parent check.\n\nPartitions where both `durability` and `consumer_offset_durability` are\n`persisted`, and the metadata plane, keep the previous merge. The README\nnow states what each durability policy survives and when a `replicated`\npartition halts.\n\nObservability: `untrusted_log` in replica log context, a rate-limited\nwarning naming the replicas whose DoViewChange is awaited, a\n`partitions_untrusted_log` gauge and a\n`partition_applied_divergence_total` counter.\n\nWire change: `DoViewChangeHeader` carves a `flags` byte out of\n`reserved` at offset 156. Every other offset is unchanged and unknown\nflag bits are rejected. An older replica can read a newer replica\u0027s\nempty vote as a short log, so every replica must be upgraded together,\nwith the cluster down.\n\nKnown limitations, documented in the README:\n- After a full-cluster restart under `replicated`, a replica that never\nreturns keeps the partition unavailable; there is no operator override\nyet.\n- A replica that already joined a view while its state transfer is\npending stays out of later elections until the transfer completes.\n- A divergence outside the headers carried by the view, or a WAL append\nparent-check failure, still stops the node."
    },
    {
      "commit": "02dac78967f885d14b10868694d7842ff692293f",
      "tree": "ce0c0ae53ad918c1b90958fb5c42752edb905430",
      "parents": [
        "3d21f31336ee5ae87acc4b2b129d5c42dad54243"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Fri Oct 23 13:16:43 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 13:16:43 2026 +0200"
      },
      "message": "test(consensus): expose committed-op loss in probe-exhaustion election (#4485)\n\nCo-authored-by: Hubert Gruszecki \u003ch.gruszecki@gmail.com\u003e"
    },
    {
      "commit": "3d21f31336ee5ae87acc4b2b129d5c42dad54243",
      "tree": "4ff9ed3b51d17d7ecc39d751d297e81ca3f9320a",
      "parents": [
        "87528db58f2941e180547b54860dc855c161594b"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Fri Oct 23 12:02:28 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 12:02:28 2026 +0200"
      },
      "message": "fix(shard): keep the pipeline head when the metadata walk stalls (#4429)"
    },
    {
      "commit": "87528db58f2941e180547b54860dc855c161594b",
      "tree": "23fdd13aaf843b2b80a6d539e422919035e2a528",
      "parents": [
        "a859762ca7b548453d8b693f68d90367ab153ac3"
      ],
      "author": {
        "name": "RustToMetal",
        "email": "thehodophile10@gmail.com",
        "time": "Fri Oct 23 04:04:37 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 11:04:37 2026 +0200"
      },
      "message": "feat(swift): add the producer (#4577)\n\n## Which issue does this PR address?\n\nRelates to #4001\n\n## Rationale\n\nThe Swift client can send and poll messages (#4553), but an application\nthat produces steadily needs what the Rust `IggyProducer` gives it. This\nis the next step of #4001: the producer.\n\n## What changed?\n\n`IggyProducer` is a port of the current Rust producer, its core,\ndispatcher, and shard workers. `initialize()` creates the stream and\ntopic when they are named and missing. A direct producer splits a batch\ninto requests of `batchLength` messages, awaits them one after another,\nand returns their confirmations, or throws `ProducerSendError` with the\nconfirmations so far and the unconfirmed tail. A background producer\nqueues the batch on a worker that flushes by length, size, or linger\ntime, merges adjacent sends to one destination, and reports write\nfailures to an error handler. A byte budget with three backpressure\nmodes and an in-flight bound span the workers, and `shutdown()` writes\nwhat is still buffered.\n\nAs in Rust, the producer retries only `transientNotAccepted`,\n`notConnected`, and `cannotEstablishConnection`, since a producer retry\nis a new request: an outcome that may have committed stops the send. The\nids the client mints on the first attempt stay with the messages, so\nretries and the failed tail carry them. A custom partitioner wins over\nany partitioning, and a missing numeric stream or topic is reported\nbefore auto-creation is considered, in the Rust order. Encryption is not\npart of the Swift client yet, so the producer has no encryptor."
    },
    {
      "commit": "a859762ca7b548453d8b693f68d90367ab153ac3",
      "tree": "22acbcea619b31371c1ad52b9af96495fd16d6db",
      "parents": [
        "ea19ecb9d2d6ae855daf420c31df639c93e39020"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Oct 23 10:53:21 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 10:53:21 2026 +0200"
      },
      "message": "chore(deps): Bump the go group across 2 directories with 2 updates (#4595)"
    },
    {
      "commit": "ea19ecb9d2d6ae855daf420c31df639c93e39020",
      "tree": "9a6353894bfc956a2cc858764e30869719d14048",
      "parents": [
        "2b47452196e5d6abec9cdb947781a0e2605c59b8"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Oct 23 10:09:18 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 10:09:18 2026 +0200"
      },
      "message": "chore(deps): Bump the node group across 1 directory with 12 updates (#4598)"
    },
    {
      "commit": "2b47452196e5d6abec9cdb947781a0e2605c59b8",
      "tree": "f475112b546e17f22d36152c95bc80a7f59af488",
      "parents": [
        "b0a951ea8e96f964499dcde67e123ba88523da8a"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Fri Oct 23 09:25:07 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 09:25:07 2026 +0200"
      },
      "message": "test(integration): add primary-side torn-tail crash recovery specs (#4442)\n\nCloses #4441"
    },
    {
      "commit": "b0a951ea8e96f964499dcde67e123ba88523da8a",
      "tree": "5bd758be7018276752e7ea093d2e5ddeabfee100",
      "parents": [
        "786382afe62acc4ecf57f185eaec05112ceae741"
      ],
      "author": {
        "name": "Piotr Gankiewicz",
        "email": "piotr.gankiewicz@gmail.com",
        "time": "Fri Oct 23 08:36:03 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 23 08:36:03 2026 +0200"
      },
      "message": "feat(server)!: unify resource options and add per-stream keys (#4615)\n\nStream, topic and user settings followed separate paths. Updating one\nsetting meant resending the name and every other field, nothing could\nreturn a setting to its default, and each SDK encoded options its own\nway per resource and transport. Encryption shared one data key across\nall streams, and a read could not tell which key a stream used.\n\nRoute every create and update through one scoped catalog on the\nexisting ResourceOptions map. Each key declares its scopes, kind,\nstorage, mutability, default, visibility and criticality, and one\nadmission path enforces it on every transport. UpdateResource carries\nonly the keys that change and explicit resets, and metadata prepare\nresolves it into a per-scope internal operation so defaults are\nreplicated, not recomputed. Names and user status stay indexed\ncolumns while the catalog validates them.\n\nGive every encrypted stream its own data key, supplied by the client\nor generated by the admitting node, and store it only inside an\nauthenticated envelope wrapped under a key derived from the config\nkey. Reads expose the key source and fingerprints, never key bytes.\nRotating the config key replicates rewrapped envelopes and leaves\nmessage data untouched, and boot refuses envelopes the configured\nkeys cannot open.\n\nTyped options replace raw maps in every SDK, with a raw escape hatch\nfor keys newer than the SDK. Unknown critical state fails closed.\nProtocol 0.12.0 and snapshot format 13 move together because both\nlayouts change.\n\nBREAKING CHANGE: clients and servers must run protocol 0.12.0, data\ndirectories need snapshot format 13, and UpdateStream, UpdateTopic\nand UpdateUser are replaced by UpdateResource."
    },
    {
      "commit": "786382afe62acc4ecf57f185eaec05112ceae741",
      "tree": "7c34c91fda3324fc9c54508dd1465da22a7c6340",
      "parents": [
        "717719c8e60fedfa3334fd243d646077d91f6298"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Thu Oct 22 23:05:10 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 22 23:05:10 2026 +0200"
      },
      "message": "feat(cluster)!: spread and rebalance partition primaries (#4619)\n\nPartition primaries followed the initial metadata view and a fixed\nfailover rotation, so primary work piled onto a subset of replicas\nand stayed there after a failed node returned.\n\nA new partition takes its primary from the last known primary views,\nwith a fallback to the committed initial view. Placement counts\ncommitted ownership plus admitted creates, each partition once. The\nmetadata primary also moves existing primaries one at a time. It\nuses fresh authenticated peer probes and a stable set of eligible\nreplicas. A move confirms the source frontier and the target\u0027s\napplied prefix, drains accepted work under a revocable grant, and\npersists an ordinary consensus view change before the target serves.\n\nA replica refuses client partition requests with 58 when its last\ncommit evidence from the metadata primary is older than the\nheartbeat timeout plus 3 s. This stops a replica that is cut off\nfrom the metadata primary from serving on stale grants.\n\nSome restarts lost a committed metadata op. The bug predates this\nchange. A replica now claims only a log view that it proved, in a\nview change and on disk, and refuses a StartView or an old prepare\nthat contradicts an op it already applied. The superblock keeps the\nadopted view, its head and exact headers across restarts. A state\ntransfer keeps the WAL entries above its snapshot when the current\nview proves their identity.\n\nEvery SDK now follows the same session rules. Rust, Node, Swift, Go,\nC# and Java implement them, and the Python, C++ and PHP bindings get\nthem from the Rust SDK. A sign-in, sign-out or disconnect by the\ncaller ends the calls it affects with 40. A call whose deadline\npasses while it waits for a session or a primary ends with 58. Swift\nno longer sends a write with an unknown outcome again under a new\nsession or request id, and ends it with 57 instead. The Rust SDK\nwaits 10 s, not 30 s, for a session bind, then drops the link and\ndials again.\n\nHTTP relays between nodes carry an HMAC proof that is valid for\n10 s, so node clocks must agree within a few seconds.\n\nBREAKING CHANGE: storage moves to the IGGY-BOUND-GRANTS-5 boundary,\nbecause partition creation records now carry an initial view per\nassignment. Older data directories are refused unchanged before\nreplay, and no migration is provided. Superblocks are written as\nenvelope v2, which older binaries refuse. The ProducerSendFailed\nerror gains an uncertain-message count and stores its stream and\ntopic names as Box\u003cstr\u003e. ConsumerGroupPartitionNotOwned carries a\nu128 client id. SDK call outcomes change as described above.\n\n---------\n\nCo-authored-by: Piotr Gankiewicz \u003cpiotr.gankiewicz@gmail.com\u003e"
    },
    {
      "commit": "717719c8e60fedfa3334fd243d646077d91f6298",
      "tree": "74a4dcd689c0588dd6c760454c5661801615f537",
      "parents": [
        "b8b37da5a55d58a7790be5c6b49dd14a41eaa362"
      ],
      "author": {
        "name": "Krishna Vishal",
        "email": "krishnavishal2008@gmail.com",
        "time": "Thu Oct 22 22:04:03 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 22 22:04:03 2026 +0530"
      },
      "message": "feat(gateways): serve DeleteGroups and OffsetDelete, report Empty groups (#4559)"
    },
    {
      "commit": "b8b37da5a55d58a7790be5c6b49dd14a41eaa362",
      "tree": "6d91600edb439d2efd713610cc4ba24684a4d582",
      "parents": [
        "48f34fd621423689aac52a32644bd5c2189948d5"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Thu Oct 22 13:22:10 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 22 13:22:10 2026 +0200"
      },
      "message": "feat(gateways): honour Kafka static group membership (#4575)\n\nCloses #4417"
    },
    {
      "commit": "48f34fd621423689aac52a32644bd5c2189948d5",
      "tree": "846ed5f066cc6c65d5023a0add75cffa068cb62a",
      "parents": [
        "bb4765dbb4aeef552c697510aee040a82931be26"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Thu Oct 22 10:13:14 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 22 13:43:14 2026 +0530"
      },
      "message": "fix(ci): reduce runner usage and simplify workflows (#4594)"
    },
    {
      "commit": "bb4765dbb4aeef552c697510aee040a82931be26",
      "tree": "2a2e42c9912df6c703c4f9ef3a171fee888e956f",
      "parents": [
        "67e3a3c7882b68ec5c5c9591c558bb291523f536"
      ],
      "author": {
        "name": "Hubert Gruszecki",
        "email": "h.gruszecki@gmail.com",
        "time": "Thu Oct 22 09:05:44 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 22 09:05:44 2026 +0200"
      },
      "message": "feat(ci): publish Kafka gateway image to Docker Hub (#4593)\n\nCo-authored-by: Krishna Vishal \u003ckrishnavishal2008@gmail.com\u003e"
    },
    {
      "commit": "67e3a3c7882b68ec5c5c9591c558bb291523f536",
      "tree": "7dab880da1f3f1ddbb13dc0055a6d00b53bd832b",
      "parents": [
        "5906e2db7b293b7177083014f0b07feb6447c9e5"
      ],
      "author": {
        "name": "Grzegorz Koszyk",
        "email": "112548209+numinnex@users.noreply.github.com",
        "time": "Wed Oct 21 21:42:30 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 21 21:42:30 2026 +0200"
      },
      "message": "feat(gateways): serve Kafka IncrementalAlterConfigs (key 44) (#4572)\n\nCloses #4509"
    }
  ],
  "next": "5906e2db7b293b7177083014f0b07feb6447c9e5"
}
