Evidence ledger
One row per claim in Making the client carry it: ten years of Discord's gateway contract: who published it, what grade it carries, when it was written, when the link was last checked, and the quote or figure it rests on. Nothing in the guide is cited from memory, so anything not in this table is not in the guide.
One row per claim. Every URL was fetched during the research session on 2026-09-22.
Evidence limit, stated up front. The research environment's outbound network policy allowed
github.com, raw.githubusercontent.com, gitlab.com and the public package registries
(hex.pm, registry.npmjs.org, pypi.org, files.pythonhosted.org, proxy.golang.org,
index.crates.io). Engineering blogs, status pages, video hosts and paper archives were
unreachable, including discord.com, where Discord publishes its engineering posts and its
incident write-ups. This guide is therefore built entirely from the repository record and the
registries: the published documentation source, its dated change log, pull requests and issue
threads, library READMEs, and release timestamps. There are no blog, paper or talk sources in
the mix, and that absence is stated in the guide rather than papered over.
| # | Org | Title | Tier | Published | Checked | URL | Claim I take from it | Supporting quote or figure |
|---|---|---|---|---|---|---|---|---|
| 1 | Discord | Developer change log (developers/change-log.mdx) |
adr | 2017-07-19 to 2026-09-18 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | The public contract has a countable decision record: 237 dated entries over nine years and two months, 27 of them tagged Breaking Change. | Parsed from the file: 237 <Update label="…"> blocks; earliest label "July 19, 2017", latest "September 18, 2026"; entries per year 2017:8, 2018:8, 2019:11, 2020:12, 2021:14, 2022:27, 2023:23, 2024:33, 2025:42, 2026:59; 27 blocks carry the tag "Breaking Change". |
| 2 | Discord | Change log, "Breaking Change: Version 6" | adr | 2017-07-19 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | The record opens with a schema-shaped break, not a capacity-shaped one: renamed and retyped fields. | "is_private removed"; "[type] is now an integer"; "recipient is now recipients"; "idle_since renamed to since". |
| 3 | Discord | Change log, "Semi-Breaking Change: Very Large Bot Sharding" | adr | 2018-01-03 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | The first capacity-shaped contract change moves partitioning work onto the app, for apps above 100,000 guilds. | "Additional sharding requirements and information for bots in over 100,000 guilds has been added. This requires a small change in numbers of shards for affected bots." |
| 4 | Discord | Change log, "Gateway Intents" | adr | 2020-02-14 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | Declared subscriptions arrive framed as a saving for the app, not as load shedding for the platform. | "Gateway Intents are a great way to specify which events you want to receive from our gateway. Go on, save yourself some bandwidth and CPU usage." |
| 5 | Discord | Change log, "API and Gateway V8" | adr | 2020-09-24 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | Seven months after intents were optional, a version bump made them mandatory and deleted the previous opt-out. | "Gateway Intents are now required"; "Removed guild_subscriptions in identify in favor of Gateway Intents"; "v6 is still the default for the time being". |
| 6 | Discord | Change log, "Gateway v6 Intent Restrictions" | adr | 2020-10-27 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | The restrictions were then applied to the old version too, so staying on v6 stopped being an escape route. | "The v6 gateway now applies the restrictions for gateway intents. This means the new chunking limitations are now in effect, regardless of intents being used." |
| 7 | Discord | Change log, "Large Bot Sharding Lowered to 150,000 Guilds" | postmortem | 2021-03-15 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | A reported reliability failure at connection time was fixed by moving more apps into the stricter sharding regime rather than by adding capacity. | "There have been reports that sessions have higher frequency of errors when starting if a bot has joined too many guilds (the gateway connection times out). To account for this we have lowered the requirement for large bot sharding down to 150,000 guilds in order to improve reliability." |
| 8 | Discord | Change log, "Session-specific Gateway Resume URLs" | adr | 2022-08-09 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | Reconnect steering was introduced with a penalty rather than a deadline: non-adopters get disconnected sooner. | "Starting on September 12, 2022, apps that aren't using the new resume_gateway_url field to resume gateway sessions will be disconnected significantly faster than normal"; "At the moment, the value of resume_gateway_url will always be wss://gateway.discord.gg to give developers more time to adopt the new field." |
| 9 | Discord | Change log, "Message Content is a Privileged Intent" | adr | 2022-09-01 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | The largest capability withdrawal in the record, with a grace period whose price is a growth freeze. | "Any app that does not have the message content intent configured … will receive empty values in fields that expose message content across Discord's APIs"; "if you opt-in to the grace period, your app will be prevented from joining any additional servers until you opt-out". |
| 10 | Discord | Change log, "Introducing Rate Limit When Requesting All Guild Members" | postmortem | 2025-08-14 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | Bulk state hydration was metered mid-flight for the largest guilds, ahead of the announced date, for stability. | "Rate Limit: 1 request per guild per bot every 30 seconds"; "A small number of applications are currently exceeding this rate limit"; "if you are the developer of an app that is requesting all guild members in very large guilds then you may start seeing this as soon as today, so we can ensure platform stability"; general rollout "October 1, 2025". |
| 11 | Discord | Change log, "Changes to Privileged Intent Access for Discord Apps" | adr | 2026-06-10 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | The gate on the firehose was re-cut from servers to users, and made an annually renewed grant. | "Previously, apps in fewer than 100 servers could access Privileged Intents by toggling them on … Starting today, the threshold is based on the number of users your app can access"; "Once your app reaches 10,000 users, you'll need to apply"; "Apps must now reapply annually for continued access". |
| 12 | Discord | Change log, "Channel Obfuscation for Users and Bots" | postmortem | 2026-08-12 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | For nine years the contract handed every app the full channel list of every guild it joined, including channels it could not view; that is now being withdrawn. | "Today, bots receive every channel in a guild, including ones they can't view, with full metadata over both the Gateway and the HTTP API"; the name becomes "___hidden___", a CHANNEL_OBFUSCATED flag (1 << 17) is set, and GET /guilds/{guild.id}/channels "will omit those channels from the response entirely". |
| 13 | Discord | Change log, "Shard Param Required for Get Current User Guilds with Large Bot Sharding" | adr | 2026-09-15 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | The newest entry in the record extends client-side partitioning from the socket to the HTTP path. | "Apps using large bot sharding are required to pass it — requests without shard now return 400 Bad Request. Valid values are 0 through max_concurrency - 1." |
| 14 | Discord | Gateway documentation (developers/events/gateway.mdx) |
vendor | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/events/gateway.mdx | Partitioning is delegated to the app by a published formula, with a hard per-connection ceiling. | "Each shard can only support a maximum of 2500 guilds, and apps that are in 2500+ guilds must enable sharding"; shard_id = (guild_id >> 22) % num_shards; "num_shards does not relate to (or limit) the total number of potential sessions. It is only used for routing traffic." |
| 15 | Discord | Gateway documentation, Max Concurrency and Session Start Limit | vendor | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/events/gateway.mdx | Admission is bucketed and budgeted: concurrency keyed by shard id, a daily session budget, and token reset as the penalty for exhausting it. | rate_limit_key = shard_id % max_concurrency; "you must start them by 'bucket' in order"; "Clients are limited to 1000 IDENTIFY calls to the websocket in a 24-hour period … all active sessions for the app will be terminated, the bot token will be reset"; large bots get "max(2000, (guild_count / 1000) * 5)" per day. |
| 16 | Discord | Gateway documentation, Rate Limiting and Privileged Intents | vendor | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/events/gateway.mdx | Metering is per connection, and the privileged subset is enforced at the handshake with a close code. | "Apps can send 120 gateway events per connection every 60 seconds"; "If you pass a privileged intent in the intents parameter without configuring it in your app's settings … your Gateway connection will be closed with a (4014 close code)"; "Events associated with the GUILD_PRESENCES and GUILD_MEMBERS intents are turned off by default regardless of the API version." |
| 17 | Discord | Gateway documentation, Encoding and Compression | vendor | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/events/gateway.mdx | The wire format is an app-side choice with app-side cost: JSON or Erlang term format, payload or transport compression, zlib or zstd. | "apps can use the encoding parameter to choose whether to communicate with Discord using a plain-text JSON or binary ETF encoding"; "The currently-available transport compression options are zlib-stream and zstd-stream"; zstd requires "a zstd decompression context that stays alive for the lifetime of the gateway connection". |
| 18 | Discord | Gateway events documentation, Request Guild Members | vendor | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/events/gateway-events.mdx | Discord names both forces behind the hydration limits in one sentence, and the limits are graded by guild size. | "Due to our privacy and infrastructural concerns with this feature, there are some limitations that apply"; "if the guild is over 75k members, it will only send members who are in voice"; chunks carry "up to 1000 members per chunk"; GUILD_MEMBERS intent "is required to request the entire member list". |
| 19 | Discord | Gateway events documentation, Identify structure (large_threshold) |
vendor | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/events/gateway-events.mdx | The oldest narrowing control in the protocol is a single integer chosen by the app, defaulting to the smallest value. | "large_threshold? |
| 20 | Discord | "You Might Not Need a Privileged Intent" guide | vendor | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/gateway/you-might-not-need-a-privileged-intent.mdx | The substitute path is now taught explicitly: pull the member you need instead of subscribing to all of them. | "Many developers may enable privileged intents out of habit or because a tutorial told them to, only to later discover that our API offers alternatives"; "If you know who you're looking for, you don't need to enumerate every member"; interaction payloads include the member object "with no intent required". |
| 21 | Discord | PR #2127, "More serious v7 status?" (closed unmerged) | adr | opened and closed 2020-10-04 | 2026-09-22 | https://github.com/discord/discord-api-docs/pull/2127 | A documentation fix for the skipped v7 was rejected on brand grounds, with the confusion acknowledged by contributors. | Closed unmerged the day it was opened; staff reply: "this PR does not seem warranted/necessary, was opened without discussion of its worthiness nor even a proper pull request description", defending the joke as fitting a "playful and fun brand"; a contributor: "The whole v6/v7 thing is repeatedly a cause for confusion for new developers." |
| 22 | Discord | PR #4376, "Clarify Which Gateway Close Codes to Resume on" (closed unmerged) | adr | opened 2022-01-24, closed 2022-02-01 | 2026-09-22 | https://github.com/discord/discord-api-docs/pull/4376 | Discord deliberately refused to publish a close-code-to-recovery mapping, keeping reconnect policy one sentence wide. | Staff reply on closing: "I think we dont need this, if you get a disconnect, try to resume, otherwise reconnect." |
| 23 | Discord | PR #6877, "[gateway] Clarify Reconnect opcode, Document zstd-stream" (merged) | source | opened 2024-05-17, merged 2024-05-23 | 2026-09-22 | https://github.com/discord/discord-api-docs/pull/6877 | Two levers were documented together in 2024: the server may evict a connection at any moment, and a cheaper compression option returned. | The PR "clarifies that Reconnect can come at any time, even before hello" and documents "the resurrected zstd-stream gateway compression option". |
| 24 | Discord | discord-api-spec (OpenAPI 3.1) | source | checked at main | 2026-09-22 | https://github.com/discord/discord-api-spec | The machine-readable contract covers only the current version and is closed to outside edits. | "OpenAPI 3.1 specification for Discord's API", covering "only the most recent version (v10)"; "we do not allow public contributions to this repo"; the preview spec "is subject to breaking changes without advance notice, and should not be used within production environments". |
| 25 | Discord | Manifold README | source | first release 2017-02-20 | 2026-09-22 | https://raw.githubusercontent.com/discord/manifold/master/README.md | The cost of fanout inside Discord's own realtime tier is a per-recipient send, and it is what the contract changes are ultimately paying down. | "Send calls cost about 70 µs/op so doing them in a loop eventually gets too expensive"; "Discord runs a single GenServer per Discord server and some of these ~100,000 PIDs connected to them from many different Erlang nodes"; after deploying Manifold "We observed packets/sec drop by half". |
| 26 | Discord | Manifold on Hex, release timeline | source | 2017-02-20 to 2026-07-07 | 2026-09-22 | https://hex.pm/api/packages/manifold | The fanout primitive is the one piece of published infrastructure that never went quiet: ten releases across nine years, the newest this year. | inserted_at 2017-02-20; releases 0.1.0 (2017-02-20) through 1.7.0 (2026-07-07); 10 releases; 646,908 all-time downloads at check time. |
| 27 | Discord | FastGlobal README and Hex record | source | single release 2017-02-20 | 2026-09-22 | https://hex.pm/api/packages/fastglobal | A hot-path workaround published once and never revised, because the runtime absorbed the idea. | README: "Storing data in a single process results in overloading the process, using an ETS table gets more expensive to read as the data gets larger"; benchmark "fastglobal get 0.33 µs/op", "ets get 7.64 µs/op", "agent get 12.67 µs/op"; Hex: exactly one release, 1.0.0, 2017-02-20; 766,789 downloads. |
| 28 | Erlang/OTP | ERTS 10.2 release notes (persistent_term) |
source | OTP 21.2 (ERTS 10.2) | 2026-09-22 | https://raw.githubusercontent.com/erlang/otp/master/erts/doc/notes.md | Twenty-two months after FastGlobal shipped, the platform added the same capability as a built-in, which is why the library stopped. | "There is a new module persistent_term that implements a term storage suitable for terms that are frequently used but never or infrequently updated. Lookups are done in constant time without copying the terms. Own Id: OTP-14669 Aux Id: PR-1989". |
| 29 | Erlang/OTP | PR #1989, "RFC: Add a persistent term storage" | adr | opened 2018-10-17, merged 2018-11-06 | 2026-09-22 | https://github.com/erlang/otp/pull/1989 | The OTP proposal names the workaround family it was replacing, which is the family FastGlobal belongs to. | Discussion references "mochiglobal" and "live-compiled modules as data stores" as the patterns the feature would make unnecessary; ETS named as the alternative for frequently-updated data. |
| 30 | Discord | SortedSet NIF README | source | first release 2019-05-10 | 2026-09-22 | https://raw.githubusercontent.com/discord/sorted_set_nif/master/README.md | When the BEAM data structure became the bottleneck, the structure moved to Rust rather than the workload moving off the BEAM. | "The core data structure and algorithms are implemented in a Native Implemented Function in the Rust Programming Language"; "when the Vector needs to grow beyond it's capacity, copying Terms over to the new larger Vector proved to be a performance bottle neck"; "A default bucket size of 500 was chosen". |
| 31 | Discord | SortedSet NIF on Hex, release timeline | source | 2019-05-10 to 2025-12-04 | 2026-09-22 | https://hex.pm/api/packages/sorted_set_nif | The Rust-in-the-BEAM component is still being changed six years later, with two releases in a single month of 2025. | Releases 1.0.0 (2019-05-10), 1.1.0 and 1.2.0 (2021-07), 1.3.0 (2025-11-12), 2.0.0 (2025-12-02), 2.0.1 (2025-12-04); 307,716 downloads at check time. |
| 32 | Discord | ZenMonitor README | source | first release 2019-02-27 | 2026-09-22 | https://raw.githubusercontent.com/discord/zen_monitor/master/README.md | The second fanout primitive is about failure notifications, not messages: one process dying must not page every watcher at once. | "ZenMonitor allows for the efficient monitoring of remote processes with minimal use of ERTS Distribution"; it is "a drop-in replacement for Process.monitor/1" whose cost at scale is "an additional ETS row on the local node and additional processing time at fan-out". |
| 33 | Discord | Erlpack README and npm record | source | npm 2017-02-17 to 2021-12-22 | 2026-09-22 | https://registry.npmjs.org/erlpack | The binary wire format offered to apps is Discord's own internal encoding, published so third parties can speak it. | README: "Erlpack is a fast encoder and decoder for the Erlang Term Format (version 131) for Python and JavaScript"; npm created 2017-02-17T18:00:35.491Z, five versions, latest 0.1.4 published 2021-12-22T16:09:51.561Z. |
| 34 | Discord | lilliput README and Go module versions | source | v1.0.0 2018-02-09, v1.5.0 2025-06-23 | 2026-09-22 | https://proxy.golang.org/github.com/discord/lilliput/@v/list | The media path shows the same instinct as the realtime path, and a six-year publication gap that closed when formats changed. | README: "It aims to do as little memory allocation as possible and especially not to create garbage in Go"; module versions v1.0.0 (2018-02-09), v1.1.0 (2018-02-10), v1.2.0 (2018-05-21), then v1.3.0 (2024-09-27), v1.4.0, v1.5.0 (2025-06-23). |
| 35 | Discord | Osprey README | casestudy | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/osprey/main/README.md | The anti-abuse rules engine that consumes the same event stream was open-sourced through a non-profit and adopted by two other messaging networks. | "Originally developed internally at Discord to combat spam, abuse, botting, and scripting across its platform, Osprey has been open-sourced"; adopters listed as Bluesky, Discord and Matrix.org; "Osprey is an event stream decisions engine … to investigate and take automatic action on events and their properties as they happen in real-time". |
| 36 | Rapptz (discord.py) | PyPI release history | source | 2015-08-23 to 2026-03-03 | 2026-09-22 | https://pypi.org/pypi/discord.py/json | The largest Python client stopped publishing for 432 days across exactly the window in which intents and message content were being enforced. | 87 releases; 1.7.3 published 2021-06-12T16:14:40; next release 2.0.0 published 2022-08-18T03:47:52 (432 days later); latest 2.7.1 on 2026-03-03. |
| 37 | Rapptz (discord.py) | discord.py 2.0.0 source artefact | source | 2022-08-18 | 2026-09-22 | https://files.pythonhosted.org/packages/source/d/discord.py/discord.py-2.0.0.tar.gz | The return release is a real artefact, two weeks before the message content deadline of 2022-09-01. | Source distribution served for version 2.0.0; PyPI upload time 2022-08-18T03:47:52 (row 36). |
| 38 | discord.js | npm release history | source | 2015-08-10 to 2026-09-22 | 2026-09-22 | https://registry.npmjs.org/discord.js | Each contract change lands as a major version of the largest JavaScript client, within months of the announcement. | First release of each major: v12 2020-03-01 (intents documented 2020-02-14), v13 2021-08-06 (API v9), v14 2022-07-17 (message content privileged 2022-09-01); package created 2015-08-10. |
| 39 | discord.js | Issue #10089, "The WebSocket rate limit has been hit, this should never happen" | postmortem | opened 2024-01-14, closed | 2026-09-22 | https://github.com/discordjs/discord.js/issues/10089 | Gateway metering surfaces to app authors as an unexplained shard death, and the library, not the app, had to be fixed. | Error logged: "The WebSocket rate limit has been hit, this should never happen", followed by shard destruction with close code 4008 and reconnection; follow-on logs show "Members didn't arrive in time"; resolved through library PR #10098. |
| 40 | Matrix.org | Client-Server API specification, "Lazy-loading room members" | adr | checked at main | 2026-09-22 | https://raw.githubusercontent.com/matrix-org/matrix-spec/main/content/client-server-api/_index.md | A second protocol solves the same hydration problem with a per-request filter that is explicitly allowed to be imprecise. | "Membership events often take significant resources for clients to track"; lazy-loading is enabled by "lazy_load_members" on a RoomEventFilter; "lazy-loading is not intended to be a perfect optimisation … it is valid for the server to send redundant membership events to the client to ease implementation". |
| 41 | Bluesky | Jetstream README and design document | source | checked at main | 2026-09-22 | https://raw.githubusercontent.com/bluesky-social/jetstream/main/docs/README.md | A third network answers the same question with a separate filtered projection service rather than a protocol option. | "It provides the same user-friendly JSON interface that allows for data filtering in a backwards-compatible manner"; design goals include "Dead-simple and cheap for us and others to operate on a single server"; non-goals include "Exactly once delivery … We do at-least-once delivery and require clients to be idempotent". |
| 42 | Discord | discord/react-native (fork) | source | checked at main | 2026-09-22 | https://github.com/discord/react-native | The client-side half of the same decade is carried as forks of other people's frameworks, which the repository record shows but does not explain. | Repository page shows "forked from react/react-native", default branch main, 41,132 commits in history. |
| 43 | Discord | rules_elixir README | source | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/rules_elixir/main/README.md | The build tooling for the BEAM tier is a fork of somebody else's Bazel rules, published with a warning. | "Bazel rules for Elixir sources. Compatible with rules_erlang"; repository description on the org listing reads "Fork of rabbitmq's toolset with a mix-based approach. Unstable, unsupported."; copyright line "© 2020-2024 Broadcom". |
| 44 | Discord | Access README | source | checked at main | 2026-09-22 | https://raw.githubusercontent.com/discord/access/main/README.md | What Discord publishes about its internals in 2026 is operational tooling, not the data or realtime tiers. | "Meet Access, a centralized portal for employees to transparently discover, request, and manage their access to all internal systems"; "Access is a React and Typescript single-page application (SPA) with a FastAPI backend that connects to the Okta API". |
| 45 | Discord | Change log, "Slash Commands and Interactions" | adr | 2020-12-15 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | The pull-shaped substitute shipped 20 months before the push-shaped capability was fenced. | "Slash Commands are here! There's a lot to cover"; documented under Application Commands, with webhook edit and delete endpoints added at the same time. |
| 46 | Discord | Change log, "Deprecating Guild Creation by Apps" | adr | 2025-04-15 | 2026-09-22 | https://raw.githubusercontent.com/discord/discord-api-docs/main/developers/change-log.mdx | A withdrawal with a three-month runway and an automated ownership migration, which is the template the later ones follow. | "The Create Guild endpoint (POST /guilds) will be restricted for applications starting July 15, 2025"; "Existing Guilds owned by bots will have their ownership transferred to a real user"; notifications sent 2025-06-15. |
| 47 | Rust community | rustler crate index | source | 0.1.0 to 0.38.0 | 2026-09-22 | https://index.crates.io/ru/st/rustler | The Rust-to-BEAM bridge Discord's SortedSet depends on is itself a long-lived, actively versioned project, which is what makes the native-code escape hatch repeatable. | Index lists 58 published versions, from 0.1.0 to 0.38.0. |