Skip to content

Moderation

Layer 1 moderation: what a server operator controls on their own infrastructure. Every operation here requires an admin session - see Server Administration.

An operator’s reach is deliberately narrow. They can decide who gets tokens from their server and which peers they federate with. They cannot see conduct, because they cannot decrypt anything, and no protocol path reports it to them.

Layer Answers for Specified in
Server the blobs it stores, and who it issues tokens to this page
Group conduct within a group Moderation Architecture
Device what one person sees Client-side Blocking
DeviceRecord Structure
struct DeviceRecord {
device_id: DeviceId,
// The only timestamp a server holds for a device. Everything after
// announcement is anonymous
announced_at: u64,
trust_tier: TrustTier,
admin_verified: bool,
// Current issuance budget, including any override
bucket: TokenBucket,
custom_budget_expires_at: Option<u64>,
}

Grants a device the Verified Trust Tier immediately, skipping the 24-hour climb.

  • Input: VerifyRequest { device_id, reason }
  • Output: DeviceRecord
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED, ADMIN_INSUFFICIENT_PERMISSIONS, DEVICE_NOT_ANNOUNCED
  • Servers MUST reject a device_id with no announcement record
  • Verification MUST raise only the issuance budget, and MUST NOT make the device’s activity observable
  • Servers MUST record the acting administrator in the audit log
  • Verification MUST persist across announcement renewal

Verification is keyed by device_id because issuance is where a server authenticates a device. A verified device’s tokens are indistinguishable from anyone else’s when spent.

POST /admin/v1/trust/verify
Authorization: AdminSession <token>

Sets a device’s budget to zero.

  • Input: BlockRequest { device_id, reason }
  • Output: DeviceRecord
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED, ADMIN_INSUFFICIENT_PERMISSIONS, DEVICE_NOT_ANNOUNCED
  • Blocking MUST set rate and capacity to zero
  • Servers MUST NOT retract tokens already issued; those remain spendable until they expire
  • Servers MUST record the acting administrator and the stated reason
  • Blocking MUST persist across announcement renewal
  • Servers MUST return DEVICE_BLOCKED at issuance thereafter
POST /admin/v1/trust/block
Authorization: AdminSession <token>

Overrides a device’s bucket parameters for a bounded period.

  • Input: BudgetRequest { device_id, rate, capacity, expires_at }
  • Output: DeviceRecord
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED, ADMIN_INSUFFICIENT_PERMISSIONS, DEVICE_NOT_ANNOUNCED, CONFIG_INVALID
  • rate and capacity MUST be non-negative
  • Servers MUST reject an expires_at in the past
  • Servers MUST revert to the tier default at expires_at
  • An override MUST NOT change how tokens are verified at redemption
POST /admin/v1/trust/budget
Authorization: AdminSession <token>

Returns everything a server holds about one device.

  • Input: None - device_id in the path
  • Output: DeviceRecord
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED, DEVICE_NOT_ANNOUNCED
  • Servers MUST NOT return message counts, delivery counts, or any per-device traffic figure
  • Servers MUST NOT return mailbox addresses; no mapping from device to mailbox exists
  • Servers MUST return DEVICE_NOT_ANNOUNCED for an expired record rather than a partial one
GET /admin/v1/trust/devices/{device_id}
Authorization: AdminSession <token>

Devices an operator may want to verify - announced recently, still below Trusted.

  • Input: PendingQuery { min_age_hours, limit }
  • Output: Vec<DeviceRecord>
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED
  • Servers MUST order results by announced_at
  • Servers MUST exclude blocked devices
  • Servers MUST bound limit, defaulting to 100
GET /admin/v1/trust/pending?min_age_hours={n}&limit={n}
Authorization: AdminSession <token>
  • Input: BulkVerifyRequest { device_ids, reason }
  • Output: BulkResult { verified, failed }
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED, ADMIN_INSUFFICIENT_PERMISSIONS
  • Servers MUST report per-device outcomes rather than failing the batch on one error
  • Servers MUST apply the same rules as VerifyDevice to each entry
  • Servers MUST bound the batch size and return CONFIG_INVALID beyond it
POST /admin/v1/trust/verify-bulk
Authorization: AdminSession <token>

Refuses federation with a peer.

  • Input: BlockOriginRequest { server_domain, reason }
  • Output: BlockRecord { server_domain, reason, blocked_at, blocked_by }
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED, ADMIN_INSUFFICIENT_PERMISSIONS
  • Servers MUST refuse blocked origins before verifying X-Origin-Signature, so a blocked peer costs no cryptographic work
  • Servers MUST return ORIGIN_BLOCKED to a blocked peer
  • Servers MUST NOT deliver queued envelopes that arrived from the peer before the block
  • Servers MUST record the acting administrator
POST /admin/v1/federation/block/{server_domain}
Authorization: AdminSession <token>
  • Input: None - domain in the path
  • Output: None
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED, ADMIN_INSUFFICIENT_PERMISSIONS
  • Servers MUST succeed for a domain that is not currently blocked
  • Servers MUST re-fetch the peer’s discovery document before accepting traffic again, since its signing key may have rotated
DELETE /admin/v1/federation/block/{server_domain}
Authorization: AdminSession <token>
  • Input: None
  • Output: Vec<BlockRecord>
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED
  • Servers MUST include both manual and automatic blocks, distinguishing the two
GET /admin/v1/federation/blocklist
Authorization: AdminSession <token>
  • Input: None - domain in the path
  • Output: OriginReputation
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED
OriginReputation Structure
struct OriginReputation {
server_domain: String,
// 0.0 to 1.0, higher is better
reputation_score: f32,
// All observed by this server. None of it requires reading an envelope
metrics: OriginMetrics,
federation_status: FederationStatus, // Active | Limited | Blocked
// Timestamp of the first time this server was interacted with
first_contact: u64,
// Timestamp of the last known activity from this server
last_activity: u64,
}
struct OriginMetrics {
envelopes_delivered: u64,
// Rejected for malformation, version mismatch, or an unadvertised feature
envelopes_rejected: u64,
// Deliveries to addresses that were never registered. A peer guessing
// addresses produces these; a legitimate one almost never does
unknown_mailbox_attempts: u64,
uptime_percentage: f32,
certificate_valid: bool,
}
  • Reputation MUST be derived only from observed delivery behavior
  • Servers MUST NOT attribute any metric to a device
  • Servers MUST recompute on access rather than storing a stale score
score = 1.0 - (unknown_mailbox_ratio x 0.5)
- (rejection_ratio x 0.3)
- (blocklist_penalty x 0.05)
GET /admin/v1/federation/reputation/{server_domain}
Authorization: AdminSession <token>
  • Input: FederationPolicy
  • Output: FederationPolicy
  • Auth: Admin session
  • Idempotent: Yes
  • Errors: ADMIN_UNAUTHORIZED, ADMIN_INSUFFICIENT_PERMISSIONS, CONFIG_INVALID
FederationPolicy Structure
struct FederationPolicy {
// When true, federate only with servers in the allowlist
allowlist_mode: bool,
allowlist: Vec<String>,
// Auto-block peers scoring below this. None disables it
auto_block_threshold: Option<f32>,
require_certificate_validation: bool,
// Publish the blocklist at /.well-known/cryptid-blocklist
publish_blocklist: bool,
}
  • auto_block_threshold MUST be between 0.0 and 1.0 when present
  • Servers MUST return the policy as stored, so a client can confirm what applied
  • Enabling allowlist_mode with an empty allowlist MUST be rejected as CONFIG_INVALID
  • Servers MUST record the acting administrator
PUT /admin/v1/federation/policy
Authorization: AdminSession <token>

A server MAY publish which peers it blocks, letting operators share defederation decisions without a central authority.

  • Input: None
  • Output: Vec<BlockRecord>
  • Auth: None
  • Idempotent: Yes
  • Errors: UNKNOWN_OPERATION
  • Publication MUST be opt-in, via publish_blocklist
  • Servers MUST return UNKNOWN_OPERATION when publication is disabled
  • The document MUST NOT name any device or user
  • Operators MUST treat another server’s blocklist as advisory, never as automatic input
GET /.well-known/cryptid-blocklist
  • Read message content, which is always encrypted
  • Learn who sent anything, since redemption is anonymous
  • Count a device’s messages, for the same reason
  • See group membership, which is client-side state
  • Remove someone from a group, which is a moderator’s action inside MLS
  • Block a device anywhere but their own server