serenity-rs/serenity
A Rust library for the Discord API.
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 2w ago
- ✓25+ active contributors
- ✓Distributed ownership (top contributor 20% of recent commits)
Show all 6 evidence items →Show less
- ✓ISC licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/serenity-rs/serenity)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/serenity-rs/serenity on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: serenity-rs/serenity
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/serenity-rs/serenity shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 2w ago
- 25+ active contributors
- Distributed ownership (top contributor 20% of recent commits)
- ISC licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live serenity-rs/serenity
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/serenity-rs/serenity.
What it runs against: a local clone of serenity-rs/serenity — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in serenity-rs/serenity | Confirms the artifact applies here, not a fork |
| 2 | License is still ISC | Catches relicense before you depend on it |
| 3 | Default branch current exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 42 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of serenity-rs/serenity. If you don't
# have one yet, run these first:
#
# git clone https://github.com/serenity-rs/serenity.git
# cd serenity
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of serenity-rs/serenity and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "serenity-rs/serenity(\\.git)?\\b" \\
&& ok "origin remote is serenity-rs/serenity" \\
|| miss "origin remote is not serenity-rs/serenity (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(ISC)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"ISC\"" package.json 2>/dev/null) \\
&& ok "license is ISC" \\
|| miss "license drift — was ISC at generation time"
# 3. Default branch
git rev-parse --verify current >/dev/null 2>&1 \\
&& ok "default branch current exists" \\
|| miss "default branch current no longer exists"
# 4. Critical files exist
test -f "src/lib.rs" \\
&& ok "src/lib.rs" \\
|| miss "missing critical file: src/lib.rs"
test -f "src/client/mod.rs" \\
&& ok "src/client/mod.rs" \\
|| miss "missing critical file: src/client/mod.rs"
test -f "src/gateway/mod.rs" \\
&& ok "src/gateway/mod.rs" \\
|| miss "missing critical file: src/gateway/mod.rs"
test -f "Cargo.toml" \\
&& ok "Cargo.toml" \\
|| miss "missing critical file: Cargo.toml"
test -f "build.rs" \\
&& ok "build.rs" \\
|| miss "missing critical file: build.rs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 42 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~12d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/serenity-rs/serenity"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Serenity is a Rust library that provides a complete, type-safe wrapper around the Discord API, handling both the HTTP REST endpoints and WebSocket gateway connections. It manages sharding transparently, maintains an auto-updating cache of Discord data, and provides strongly-typed structs for all Discord objects (messages, guilds, users, etc.), eliminating the need for raw JSON serialization when building Discord bots. Monorepo structure: main library in src/ with modules for model (Discord objects), gateway (WebSocket handling), http (REST client), cache (in-memory storage), and client (orchestration). Companion crate command_attr/ is a procedural macro for command parsing. Examples/ contains standalone runnable bots (e01_basic_ping_bot through e05_command_framework) demonstrating progressive complexity. Benches/ holds performance-critical path benchmarks.
👥Who it's for
Rust developers building Discord bots who want type safety, automatic sharding, and minimal boilerplate. Users range from hobby bot developers to those building production bots, especially those wanting to avoid JavaScript/Python alternatives and leverage Rust's performance and memory guarantees.
🌱Maturity & risk
Highly mature and production-ready. At version 0.12.5 with rust-version 1.74+, the library shows active maintenance with comprehensive CI/CD workflows (.github/workflows/ci.yml, docs.yml, lint.yml), full test coverage patterns, and a well-organized changelog (CHANGELOG.md). The ecosystem includes sister projects (poise, songbird) indicating sustained community adoption.
Low risk for a stable API library. Dependencies are well-vetted (tokio, serde, reqwest, time) and version-locked precisely (e.g., tokio = 1.34.0). Main risks: async Rust complexity for inexperienced contributors, and potential breaking changes between minor versions as Discord API evolves—though the workspace structure and examples mitigate onboarding pain.
Active areas of work
Active development focused on API completeness and Rust ecosystem alignment. The library maintains lockstep with Discord API changes and Tokio async patterns. Presence of multiple GitHub workflows (labeler.yml, matchers/rust.json) and comprehensive examples indicates active issue triage and contributor onboarding. The command_attr macro crate suggests ongoing work on ergonomic command definition.
🚀Get running
git clone https://github.com/serenity-rs/serenity.git
cd serenity
cargo build
cargo run --example e01_basic_ping_bot
Set DISCORD_TOKEN environment variable before running examples.
Daily commands:
For local development: cargo build to compile with all features. Run examples via cargo run --example e01_basic_ping_bot (requires DISCORD_TOKEN env var). Run tests: cargo test. Run lints: cargo clippy --all-targets. See examples/Makefile.toml for convenience targets.
🗺️Map of the codebase
src/lib.rs— Library root and main API surface; defines the core Client, Context, and EventHandler types that all Discord integrations depend onsrc/client/mod.rs— Client implementation managing gateway connection, event dispatching, and shard lifecycle—the entry point for all bot behaviorsrc/gateway/mod.rs— Gateway protocol handling for Discord's real-time WebSocket API; critical for receiving and sending eventsCargo.toml— Workspace configuration defining all dependencies (bitflags, tokio, serde) and workspace members; required reading for dependency managementbuild.rs— Build script that likely handles code generation or compile-time checks; contributors must understand how the crate is builtcommand_attr/src/lib.rs— Procedural macro implementation for command framework attributes; essential for understanding how the declarative API worksexamples/e01_basic_ping_bot/src/main.rs— Canonical starter example showing minimal Client setup, EventHandler implementation, and gateway intent configuration
🛠️How to make changes
Add a new EventHandler event type
- Add a new Event variant to the Event enum in src/model/event.rs (or whichever module defines events) (
src/model/event.rs) - Implement parsing logic in src/gateway/mod.rs to deserialize the Discord gateway event into your new Event variant (
src/gateway/mod.rs) - Add a corresponding method to the EventHandler trait in src/client/event_handler.rs (e.g., on_my_new_event) (
src/client/event_handler.rs) - Call the new handler method in src/client/mod.rs dispatch logic when the Event variant is received (
src/client/mod.rs) - Add an example in examples/e11_gateway_intents/src/main.rs or create a new example showing the event in use (
examples/e11_gateway_intents/src/main.rs)
Add a new Discord REST API method
- Define the request/response DTOs in src/model/ (e.g., src/model/guild/mod.rs for guild endpoints) (
src/model/guild/mod.rs) - Add the HTTP route method in src/http/mod.rs following the existing pattern (e.g., get_channel, create_message) (
src/http/mod.rs) - Optionally add a convenience method to src/model/guild/mod.rs or relevant model file to call the HTTP method (
src/model/guild/mod.rs) - Add test coverage or an example in examples/ demonstrating the new endpoint (
examples/e06_sample_bot_structure/src/main.rs)
Add a new command attribute for the command framework
- Define the attribute name and parsing in command_attr/src/attributes.rs (
command_attr/src/attributes.rs) - Add any validation constants to command_attr/src/consts.rs if the attribute has preset values (
command_attr/src/consts.rs) - Implement code generation logic in command_attr/src/lib.rs to expand the new attribute into the command struct (
command_attr/src/lib.rs) - Update src/framework/standard/mod.rs to respect the new attribute at runtime when executing commands (
src/framework/standard/mod.rs) - Add an example in examples/e05_command_framework/src/main.rs or a dedicated example showing the attribute in use (
examples/e05_command_framework/src/main.rs)
Add support for a new Discord interaction type (e.g., new modal field type)
- Add the new field type/variant to the interaction model in src/model/interactions/mod.rs (
src/model/interactions/mod.rs) - Update deserialization in the same file to handle the new type from Discord's JSON (
src/model/interactions/mod.rs) - Add a builder method in src/builder/ (e.g., CreateInputText, CreateSelectMenu) to allow users to construct the type (
src/builder/create_input_text.rs) - Add an example in examples/e14_slash_commands/src/commands/ (e.g., modal.rs) demonstrating the new interaction type (
examples/e14_slash_commands/src/commands/modal.rs)
🔧Why these technologies
- Tokio async runtime — Serenity handles multiple WebSocket shards and concurrent HTTP requests; Tokio provides efficient scheduling for high concurrency
- Serde for JSON serialization — Discord API uses JSON exclusively; Serde provides zero-copy, type-safe serialization with derive macros for all model structs
- Bitflags for permission/intent bitmasks — Discord represents permissions and intents as bit flags; bitflags provides type-safe bit operations without manual shifting
- Procedural macros for command framework — Declarative syntax (e.g., #[command]) improves ergonomics; macros generate validation, parsing, and dispatch code at compile time
- TypeMap for client data storage — Enables type-safe heterogeneous data storage in Client (any type as key/value); used by handlers to access shared state without casting
⚖️Trade-offs already made
-
Transparent shard management (user doesn't explicitly manage shards)
- Why: Simplifies the API for small/medium bots; most users never need to think about sharding
- Consequence: Advanced users lose fine-grained shard control; scaling to 100k+ guilds requires more boilerplate
-
In-memory cache (no persistent storage built-in)
- Why: Reduces latency (~5ms lookups) and external dependencies; cache is optional
- Consequence: Cache invalidation is critical; stale data can occur on bot restart; users must implement persistence if needed
-
EventHandler trait with separate methods per event type
- Why: Type-safe, IDE autocompletion; handlers only implement events they care about
- Consequence: Lots of boilerplate for event routing; no unified event stream (harder to implement cross-cutting concerns)
-
Synchronous command framework (text-based commands with regex parsing)
- Why: Simple to understand; no external dependency; works offline
- Consequence: undefined
🪤Traps & gotchas
- DISCORD_TOKEN must be set as environment variable before running examples or client will panic—no default fallback
- GatewayIntents bitmask is required when creating Client; missing intents cause events not to be received even if handler is registered (e.g., MESSAGE_CONTENT intent required for msg.content field)
- Cache is opt-in and incomplete by default—not all API objects are cached automatically; some queries still require HTTP calls
- Tokio multi-threaded runtime assumed (tokio::main default)—single-threaded runtime will deadlock if used
- Discord API changes may break compilation against old versions; keep CHANGELOG.md and Discord official docs in sync when updating
- Rate limiting is handled transparently in http/client.rs but can cause unexpected delays; no user-facing queue introspection
- Async-trait impl reduces compile performance; macroexpansion in command_attr adds build time
🏗️Architecture
💡Concepts to learn
- Gateway Intents (bitmask) — Serenity requires explicit intent selection to receive events; understanding bitflags-based intent filtering is essential to prevent silent event drops and understand bot permissions model
- Automatic Sharding (horizontal scaling) — Serenity transparently splits gateway connections across shards; understanding how shards partition guilds by hash prevents confusion when working with large-scale bots
- Event-driven architecture with async-trait — Serenity's EventHandler pattern uses the async-trait macro to work around Rust's async trait limitations; understanding this pattern is critical to extending the library
- Cache invalidation and eventual consistency — Serenity's cache is updated asynchronously from events, not from HTTP responses; understanding cache lag and staleness is crucial for avoiding race conditions in bot logic
- Rate-limit aware HTTP client (token bucket pattern) — Discord enforces strict per-endpoint rate limits; Serenity's HTTP client implements token bucket internally to avoid 429 responses—understanding backpressure is essential for production bots
- Strongly-typed serde models for Discord API versioning — All Discord API objects are modeled as Rust structs with serde derive; understanding optional fields and #[serde(skip)] helps navigate API deprecations and Discord versioning
- Procedural macros for command parsing (command_attr) — The command_attr crate uses syn/quote to generate argument parsing code at compile-time; understanding macro expansion helps debug command ergonomics and error messages
🔗Related repos
serenity-rs/poise— Higher-level command framework built on Serenity, handles slash commands and text commands with derive macros—users of Serenity often graduate to poise for ergonomic command definitionserenity-rs/songbird— Audio library for Discord voice channels; designed as companion to Serenity for building voice bots without reimplementing gateway connection managementtwilight-rs/twilight— Alternative Rust Discord library with different design philosophy (more modular, less opinionated caching); useful comparison for API design trade-offsdiscordpy/discord.py— Most popular Discord library overall (Python); Serenity documentation often contrasts or borrows patterns from discord.py for familiarity
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive benchmarking suite for Discord API message parsing
The repo has benches/bench_args.rs but only benchmarks command argument parsing. With simd-json as an optional dependency and serde_json as required, there's no benchmark coverage for the most performance-critical path: parsing incoming Discord API messages. This would help contributors understand optimization opportunities and prevent regressions when modifying message deserialization or event handling.
- [ ] Create benches/bench_message_parsing.rs with benchmarks for deserializing common Discord message types (Guild, User, Message structs)
- [ ] Add benchmarks comparing serde_json vs simd-json (when the optional feature is enabled) for realistic Discord API payloads
- [ ] Add benchmark for the EventHandler dispatch flow to measure message routing overhead
- [ ] Document results in CONTRIBUTING.md with guidance on running benchmarks locally
Add integration tests for voice channel operations in examples/e08_shard_manager
The examples directory contains e01 through e08, with e08_shard_manager being the most advanced. However, there are no integration tests verifying that shard manager operations work correctly with the Discord API simulation. The tokio-tun dependency is listed but appears unused, suggesting incomplete voice/sharding integration testing infrastructure.
- [ ] Create examples/e08_shard_manager/tests/integration_tests.rs with mock Discord API responses
- [ ] Add tests for shard spawning, reconnection logic, and event redistribution across shards
- [ ] Verify the example compiles and runs without errors in CI by adding to .github/workflows/ci.yml under a new 'test-examples' step
- [ ] Document the test setup in examples/README.md
Implement missing builder pattern tests for command_attr derive macro
The command_attr procedural macro (command_attr/src/) handles command attribute parsing, but there are no dedicated unit tests for the derive macro expansion. This is risky for contributions since broken macro output won't be caught until runtime. Adding tests would improve code review velocity and prevent subtle bugs in command parsing.
- [ ] Create command_attr/tests/macro_expansion.rs with trybuild or similar macro testing framework
- [ ] Add test cases for valid and invalid #[command] attribute combinations (e.g., duplicate #[description], missing required fields)
- [ ] Test edge cases: empty command names, invalid Unicode in descriptions, conflicting permissions
- [ ] Document testing approach for macro contributors in CONTRIBUTING.md
🌿Good first issues
- Add integration tests for cache invalidation under src/cache/test.rs—currently no test coverage visible for cache correctness when events arrive out of order or with missing data
- Expand examples/e04_message_builder/ with additional embed field examples (currently minimal coverage of EmbedBuilder API surface) and document in markdown
- Add doc comments to command_attr/src/structures.rs—macro output is undocumented, making it hard for users to understand generated code and error messages
⭐Top contributors
Click to expand
Top contributors
- @GnomedDev — 20 commits
- @arqunis — 19 commits
- @mkrasnitski — 14 commits
- @jamesbt365 — 13 commits
- @nwerosama — 5 commits
📝Recent commits
Click to expand
Recent commits
f84addf— Added AI usage guidelines in CONTRIBUTING.md (#3543) (KiloOscarSix)51222dd— Update description forUSE_EMBEDDED_ACTIVITIESpermission (#3524) (kshitijanurag)7f7baad— FixInviteCreateEvent::temporarydoc comment (#3527) (nwerosama)4f3afc5— DocumentCacheRefdeadlock risk across await points (#3520) (Sim-hu)06ce58f— Update docs forUser::global_name(#3521) (nwerosama)87b2bd1— Fix Clippy (#3504) (meditationmind)1e5af4d— Revert "Update rustls feature name for reqwest 0.13 compatibility (#3492)" (arqunis)e114212— Update rustls feature name for reqwest 0.13 compatibility (#3492) (shimaenaga1123)554bfbb— Restore Discord docs links (#3491) (meditationmind)d901c82— Update Discord docs links (#3486) (meditationmind)
🔒Security observations
The serenity Discord API library demonstrates generally good security practices with the inclusion of the 'secrecy' crate for sensitive data handling and use of established, well-maintained dependencies. However, there are notable issues: (1) unspecified and flexible version constraints on critical dependencies (tokio-tun, reqwest) that could introduce unexpected vulnerabilities, (2) potential credential exposure risks in example code and .env.example files if developers don't follow proper security practices, and (3) reliance on proper input validation in the command framework which should be explicitly documented. The codebase would benefit from stricter dependency version pinning, comprehensive security guidelines in documentation, and example code that explicitly demonstrates secure credential handling patterns.
- High · Unspecified tokio-tun Dependency —
Cargo.toml - [dependencies] section. The dependency 'tokio-tun' is listed in Cargo.toml without a version specification. This could lead to unexpected breaking changes or security vulnerabilities in future versions. The package lacks proper version pinning, which violates best practices for dependency management. Fix: Specify an explicit version for tokio-tun (e.g., 'tokio-tun = "0.x.x"') and regularly audit dependencies using 'cargo audit' and 'cargo outdated'. - High · Flexible reqwest Version Constraint —
Cargo.toml - [dependencies] section, reqwest. The reqwest dependency uses '>=0.11.22' without an upper bound, allowing any version from 0.11.22 onwards to be installed. This could introduce breaking changes or security issues from newer minor/patch versions that might not be backward compatible. Fix: Use a more restrictive version constraint such as '0.11.22' (exact) or '^0.11.22' (compatible versions) to control updates more carefully. - Medium · Hardcoded Example Configuration —
examples/e06_sample_bot_structure/.env.example. The file 'examples/e06_sample_bot_structure/.env.example' suggests environment variable usage for sensitive data. While the .example file itself isn't a vulnerability, it indicates the codebase handles Discord tokens and credentials. If developers copy this without proper renaming or if .env files are accidentally committed, credentials could be exposed. Fix: Ensure .env files are properly added to .gitignore, use the 'secrecy' crate (already present in dependencies) for handling sensitive data, and provide clear documentation on credential management. - Medium · Potential Credential Exposure in Examples —
examples/e06_sample_bot_structure/src/main.rs. The example bot structure (e06_sample_bot_structure) likely demonstrates token usage. If examples are not properly sanitized or if developers copy them verbatim into production, Discord bot tokens could be exposed. The secrecy crate is available but may not be consistently used across examples. Fix: Audit all example files to ensure tokens are never hardcoded, use the 'secrecy' crate consistently for sensitive data handling, and add clear warnings in documentation about not committing real tokens. - Low · Missing Dependency Version Precision —
Cargo.toml - [dependencies] section. Several dependencies use flexible versioning (e.g., 'chrono' with default-features disabled, multiple '0.x.x' version ranges). While not critical, this reduces reproducibility and could introduce subtle bugs or security issues across different build environments. Fix: Generate and commit a 'Cargo.lock' file for binary projects/examples to ensure reproducible builds. For library crates, consider using more conservative version constraints for critical dependencies. - Low · Potential Input Validation Risk in Command Framework —
command_attr/src/lib.rs and related macro code. The command_attr procedural macro generates command handling code. If user input (Discord messages/interactions) is not properly validated before processing, it could lead to injection attacks or unexpected behavior. Fix: Ensure all user-provided input from Discord is validated and sanitized. Review command_attr macro generated code for proper input handling. Implement rate limiting and input size checks.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.