#[non_exhaustive]pub struct Config {Show 15 fields
pub service_did: String,
pub service_endpoint: String,
pub bind_addr: SocketAddr,
pub db_path: PathBuf,
pub signing_key_path: PathBuf,
pub admin: AdminConfigToml,
pub labeler: Option<LabelerConfigToml>,
pub operator: Option<OperatorConfigToml>,
pub retention: RetentionConfigToml,
pub moderation_reasons: Option<BTreeMap<String, ReasonDefToml>>,
pub strike_policy: Option<StrikePolicyToml>,
pub label_emission: Option<LabelEmissionPolicyToml>,
pub policy_automation: Option<PolicyAutomationPolicyToml>,
pub pds_admin: Option<PdsAdminConfigToml>,
pub xrpc_gateway: Option<XrpcGatewayConfigToml>,
}Expand description
Top-level Cairn configuration.
Fields grow with features; the struct is non_exhaustive so additions
are not a breaking change for downstream crates.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.service_did: StringThe service DID Cairn runs as (§5.1).
service_endpoint: StringPublicly-reachable base URL where this Cairn instance serves HTTP
and WebSocket endpoints (e.g., https://labeler.example). Emitted
as the serviceEndpoint value in the AtprotoLabeler entry of
/.well-known/did.json so consumers can discover where to call.
This is distinct from Self::bind_addr — typical production
deployments bind 127.0.0.1:3000 behind a reverse proxy but
advertise the public https://labeler.example URL here.
Validated at load time as a URL.
bind_addr: SocketAddrWhere cairn serve binds its HTTP listener. Defaults to
DEFAULT_BIND_ADDR (127.0.0.1:3000) if omitted.
db_path: PathBufSQLite database file. Parent directory must exist; the file
itself is created on first run by
crate::storage::open alongside embedded migrations.
signing_key_path: PathBufSigning key file (§5.1). Mode 0600, owned by the running user,
hex-encoded 32-byte secp256k1 private key. Env-var delivery of
the key material is explicitly rejected — see
crate::signing_key::SIGNING_KEY_ENV_REJECTED.
admin: AdminConfigTomlAdmin-endpoint policy (§F12). Defaults to an empty table,
meaning admin.applyLabel accepts any label value ≤128 bytes
(matches the existing crate::AdminConfig default).
labeler: Option<LabelerConfigToml>Labeler policy (§F1) — the app.bsky.labeler.service record
content. Required for cairn publish-service-record; other
subcommands don’t consume it, so it’s optional at load time.
When absent, publish-service-record surfaces a clear error.
operator: Option<OperatorConfigToml>Operator PDS auth surface (§F1 service record publishing). The operator’s identity is the DID that OWNS the labeler account — distinct from moderators who authenticate to Cairn (§5.2) and distinct from Cairn’s own signing key (§5.1).
retention: RetentionConfigTomlRetention sweep execution policy (§F4 sweep task). Holds
schedule + batching knobs only — the cutoff itself
(retention_days) is owned by crate::SubscribeConfig so
the read-side floor and the sweep cutoff stay tied to a
single source of truth. Defaults match §F4 prose: enabled,
04:00 UTC, 1000-row batches.
moderation_reasons: Option<BTreeMap<String, ReasonDefToml>>Reason vocabulary for the v1.4 graduated-action moderation
model (§F20, #47). TOML projection of the operator’s
[moderation_reasons.<identifier>] blocks; resolve to a
runtime crate::moderation::reasons::ReasonVocabulary via
ReasonVocabulary::from_config.
Three states:
None— operator declared no blocks; the resolver loads shipped defaults (eight reasons covering common categories).Some(empty)— operator wrote a bare[moderation_reasons]header with no sub-blocks; rejected at validate time as a probable typo.Some(non_empty)— operator’s vocabulary is the complete set; defaults are NOT merged in.
strike_policy: Option<StrikePolicyToml>Strike policy for the v1.4 graduated-action moderation
model (§F20, #48). TOML projection of the operator’s
[strike_policy] block; resolve to a runtime
crate::moderation::policy::StrikePolicy via
StrikePolicy::from_config.
Two states:
None— operator declared no block; the resolver returns shipped defaults (threshold 3, curve [1, 2], linear decay over 90 days, suspensions freeze decay).Some(_)— partial or full operator declaration. Per-field serde defaults fill any unspecified sub-fields; the resolved values are validated together (curve length convention, strict-ascending curve, positive decay window).
label_emission: Option<LabelEmissionPolicyToml>Label-emission policy for the v1.5 graduated-action moderation
model (§F21, #58). TOML projection of the operator’s
[label_emission] block; resolve to a runtime
crate::labels::policy::LabelEmissionPolicy via
LabelEmissionPolicy::from_config.
Two states:
None— operator declared no block; the resolver returns shipped defaults (emission enabled, reason labels emitted, warnings not emitted, default per-action mappings —!takedown,!hide,!warn).Some(_)— partial or full operator declaration. Per-field serde defaults fill any unspecified sub-fields; the resolved values are validated together (label-value naming conventions, no-collision across action_type overrides, valid action_type keys in override maps).
policy_automation: Option<PolicyAutomationPolicyToml>[policy_automation] block (§F22, #71). TOML projection of
the operator’s policy-automation surface; resolve to a
runtime crate::policy::automation::PolicyAutomationPolicy
via PolicyAutomationPolicy::from_config. Cross-validation
against [moderation_reasons] (rule reason_codes must exist
in the operator’s vocabulary) lives at the Config::validate
level, not on the resolver, mirroring the v1.4 / v1.5
per-block-resolver convention.
Two states:
None— operator declared no block; the resolver returns shipped defaults (engine enabled, empty rule set — the engine evaluates each recordAction and finds nothing to fire).Some(_)— operator-declared rules. Each rule gets per-field validation (positive threshold, valid action_type, valid mode, duration only on temp_suspension, reason_codes match the[moderation_reasons]vocabulary).
pds_admin: Option<PdsAdminConfigToml>[pds_admin] block (§F23, #83, v1.7). TOML projection of
the operator’s PDS-admin outbound bridge config; resolves
to a runtime crate::pds_admin::PdsAdminPolicy via
PdsAdminPolicy::from_config.
Two states:
None— operator declared no block; the resolver returns the disabled default (engine off; backend unconfigured; action_map empty).Some(_)— partial or full operator declaration. Whenenabled = true, exactly one backend subsection must be present (v1.7 supports[pds_admin.ozone]only); the[pds_admin.action_map]table must cover every cairn-mod action type. Whenenabled = false, subsections may be present (forward-compat) but are not cross-validated.
Cross-block validation against
crate::moderation::types::ActionType’s vocabulary lives
in Config::validate (every action_map key must parse
via ActionType::from_db_str); the resolver in
crate::pds_admin::PdsAdminPolicy::from_config handles
per-block validation (URL scheme, env-var resolution,
allowed-method set, with_lift_after gating).
xrpc_gateway: Option<XrpcGatewayConfigToml>[xrpc_gateway] block (§F23 inbound surface, #91, v1.7).
Operator-config-gated inbound XRPC listener for proxied
Ozone moderation calls + forwarded createReport calls.
Resolves to a runtime
crate::xrpc_gateway::XrpcGatewayConfig via
crate::xrpc_gateway::XrpcGatewayConfig::from_config;
absence (the v1.6-shape default) leaves the gateway off
and the listener does not mount.
Implementations§
Source§impl Config
impl Config
Sourcepub fn validate(&self) -> Result<()>
pub fn validate(&self) -> Result<()>
Post-load validation run by Config::load. Exposed so call
sites that construct a Config directly (e.g., tests) can
share the same rule set.
Sourcepub fn load() -> Result<Self>
pub fn load() -> Result<Self>
Load configuration from the default TOML location + env
overrides (see Self::load_from for the full precedence
rules). The default location is CAIRN_CONFIG env var, or
/etc/cairn/cairn.toml if unset.
Sourcepub fn load_from(toml_path: Option<&Path>) -> Result<Self>
pub fn load_from(toml_path: Option<&Path>) -> Result<Self>
Load configuration with an explicit TOML path (or None to
skip the file layer entirely and rely on env overrides).
Sources, low to high precedence:
- Compiled-in defaults (
bind_addrif unset, empty admin table). toml_pathifSomeand the file exists.- Environment variables prefixed
CAIRN_(e.g.CAIRN_SERVICE_DID).
cairn serve --config <path> routes through this without
mutating process env (which is unsafe under Rust 2024 and
blocked by the crate’s #![forbid(unsafe_code)]).
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Config
impl<'de> Deserialize<'de> for Config
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for Config
impl RefUnwindSafe for Config
impl Send for Config
impl Sync for Config
impl Unpin for Config
impl UnsafeUnpin for Config
impl UnwindSafe for Config
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);