Buzz Authentication Scopes: Complete Guide to Permissions and Access Control
Buzz defines 16 granular permission scopes in crates/buzz-auth/src/scope.rs that control access to messages, channels, users, jobs, subscriptions, files, and repositories, utilizing database-backed TEXT[] storage and forward-compatible parsing for unknown scope variants.
The block/buzz repository implements a robust permission system using Buzz authentication scopes to govern what authenticated clients can perform. These scopes function as capability strings granted to WebSocket connections, API tokens, and HTTP requests, with enforcement distributed across the buzz-auth crate.
Available Authentication Scopes in Buzz
The system recognizes 16 distinct scope variants defined in crates/buzz-auth/src/scope.rs (lines 10-45). Each variant maps to a wire-format string used during token serialization and API authorization.
Core Messaging and Channel Scopes
- MessagesRead (
messages:read): Grants permission to read messages from channels the authenticated user belongs to. - MessagesWrite (
messages:write): Allows sending messages to channels where the user holds membership. - ChannelsRead (
channels:read): Enables listing and reading channel metadata without requiring membership. - ChannelsWrite (
channels:write): Permits creating new channels and updating existing channel configurations.
Administrative Scopes
- AdminChannels (
admin:channels): Provides administrative channel actions including deletion and forced member removal. - AdminUsers (
admin:users): Enables administrative user management such as account suspension and impersonation capabilities.
System and Resource Scopes
- UsersRead (
users:read): Allows reading user profile information and public account data. - UsersWrite (
users:write): Permits updating user profile information and account settings. - JobsRead (
jobs:read): Grants access to inspect background job status and processing queues. - JobsWrite (
jobs:write): Allows submitting new background jobs or canceling existing operations. - SubscriptionsRead (
subscriptions:read): Enables reading subscription and billing plan details. - SubscriptionsWrite (
subscriptions:write): Permits modifying subscription information and plan configurations. - FilesRead (
files:read): Allows downloading files and message attachments. - FilesWrite (
files:write): Grants permission to upload files and create new attachments. - ReposRead (
repos:read): Reserved for cloning Git repositories via HTTP routes (future-use implementation). - ReposWrite (
repos:write): Enables pushing to Git repositories and creating new repositories, currently enforced for specific Nostr events.
Forward Compatibility Handling
- Unknown(String): Preserves any unrecognized scope strings to maintain compatibility with future scope additions without breaking existing token validation.
How Buzz Stores and Parses Authentication Scopes
Buzz persists scopes as a TEXT[] column in the database, enabling the addition of new scopes without requiring schema migrations. When parsing tokens, the system invokes parse_scopes() (defined in crates/buzz-auth/src/scope.rs, lines 69-78) to convert raw string slices into typed Scope variants.
The parsing implementation handles unrecognized scope strings by wrapping them in the Scope::Unknown variant (lines 55-61). This design ensures that tokens containing future scope definitions remain valid and parseable by current system versions, with the unknown scopes preserved for forward compatibility.
Scope-Based Access Patterns in Buzz
The authentication system applies Buzz authentication scopes differently depending on whether the client uses NIP-42 Nostr authentication or token-based API access.
NIP-42 Authentication (Nostr Mode): During standard NIP-42 authentication, every authenticated WebSocket connection receives the complete set of scopes defined by Scope::all_known(). Fine-grained access control is subsequently enforced through NIP-29 membership checks rather than scope limitations.
Token-Based Authentication: For API tokens and HTTP-based endpoints, the scopes field within the token payload determines permissible operations. Request handlers inspect this vector through helper functions in crates/buzz-auth/src/access.rs to authorize specific actions such as message ingestion, file uploads, or channel administration.
Utility Methods for Scope Management
The Scope enum provides helper methods for common access patterns in crates/buzz-auth/src/scope.rs:
Scope::all_known()(lines 63-87): Returns aVec<Scope>containing all 16 defined variants, useful for generating documentation or administrative interfaces.Scope::all_non_admin()(lines 89-111): Returns every scope exceptAdminChannelsandAdminUsers, used in development mode when bareX-Pubkeyheaders grant broad access while still requiring properly scoped tokens for administrative functions.
Implementing Scope Checks in Rust
Developers interact with scopes through the buzz_auth::scope module, utilizing typed enums rather than raw strings for type-safe permission management. The following example demonstrates token creation, scope serialization, and permission verification:
use buzz_auth::scope::{Scope, parse_scopes};
/// Create a token payload containing specific scope permissions.
fn make_token(scopes: &[Scope]) -> String {
let scope_strings: Vec<&str> = scopes.iter().map(|s| s.as_str()).collect();
scope_strings.join(" ")
}
/// Verify if the request contains message write permissions.
fn can_write_message(request_scopes: &[Scope]) -> bool {
request_scopes.contains(&Scope::MessagesWrite)
}
fn main() {
// Generate token with message and channel access
let token_scopes = vec![
Scope::MessagesRead,
Scope::MessagesWrite,
Scope::ChannelsRead
];
let token = make_token(&token_scopes);
println!("Token scopes: {}", token);
// Parse and validate incoming request scopes
let parsed = parse_scopes(&["messages:write", "channels:read"]);
assert!(can_write_message(&parsed));
}
The as_str() method serializes each variant to its canonical wire-format representation, while parse_scopes() reconstructs typed variants from token data for runtime authorization decisions.
Scope Enforcement Across the Codebase
Scope validation is distributed across several specialized modules within the buzz-auth crate:
crates/buzz-auth/src/scope.rs: Central definition of all scope variants, conversion logic, and parsing functions.crates/buzz-auth/src/access.rs: Contains access-check helpers that inspect request scope vectors to grant or deny specific operations.crates/buzz-auth/src/nip98.rs: Implements NIP-98 authentication for HTTP endpoints, consulting token scopes to enforce file and repository permissions.crates/buzz-auth/src/lib.rs: Exposes the public API for token creation and validation, utilizing theScopedefinitions for credential generation.
Summary
- Buzz defines 16 granular authentication scopes in
crates/buzz-auth/src/scope.rscovering messages, channels, users, jobs, subscriptions, files, and repositories. - Scopes are stored as TEXT[] arrays in the database, allowing schema-free additions of new permissions.
- The
Scope::Unknownvariant preserves unrecognized scope strings for forward compatibility with future API versions. - NIP-42 authentication grants full scopes with NIP-29 handling fine-grained access, while token-based authentication enforces strict scope boundaries.
- Utility methods
all_known()andall_non_admin()facilitate development workflows and administrative access controls. - The
parse_scopes()function andas_str()method handle conversion between wire-format strings and typed Rust variants.
Frequently Asked Questions
How do I add a new authentication scope to Buzz?
Add the new scope variant to the Scope enum definition in crates/buzz-auth/src/scope.rs (around lines 10-45) and implement the corresponding as_str() match arm to return the wire-format string. Because scopes are stored as TEXT[] in the database, no schema migration is required; however, you must add the parsing logic in parse_scopes() to recognize the new string and map it to your variant.
What is the difference between NIP-42 and token-based scope handling?
In NIP-42 authentication, every authenticated WebSocket connection receives the full set of scopes defined by Scope::all_known(), with granular permissions enforced separately through NIP-29 group membership checks. In token-based authentication, the token's explicit scopes field restricts the client to only those specific permissions, which are validated by request handlers in crates/buzz-auth/src/access.rs before executing operations.
How does Buzz handle unknown or future scope strings?
When parse_scopes() encounters a scope string that does not match any known variant in crates/buzz-auth/src/scope.rs (lines 55-61), it wraps the string in the Scope::Unknown(String) variant rather than rejecting the token. This forward-compatibility mechanism ensures that older Buzz versions can still parse and validate tokens containing new scopes introduced in future updates.
Which scopes are required for administrative operations?
Administrative functions require either AdminChannels (admin:channels) for channel management operations like deletion and forced member removal, or AdminUsers (admin:users) for user administration including suspension and impersonation. The Scope::all_non_admin() method (lines 89-111) specifically excludes these two variants, ensuring that development-mode access granted via X-Pubkey headers cannot perform administrative actions without a properly scoped token.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →