//! Acquiring and loading policies: the three sources `plan/policy.md` //! describes, and the machinery that turns what they observe into //! declarations [`didbot_policy::PolicyTree`] can be built from. //! //! This crate produces what `didbot-policy` consumes. It holds no //! evaluation logic of its own -- see `didbot-policy` for that -- and no //! write-path integration -- see `didbot-pds`. Its job stops at: here are //! three sources' worth of policies, already turned into //! [`didbot_policy::PolicyDeclaration`]s registered on a tree, plus a report //! of anything that did not load cleanly. //! //! # The three sources //! //! - [`builtin`] -- compiled into the binary, immutable. Each one is a //! `bot.did.policy` record written in this crate and compiled through //! the regex engine at startup, so it reads like an operator's own. //! - [`startup`] -- supplied at startup from a local file, immutable for //! the run. Uses the same provisional JSON record shape as the operator //! source. //! - [`poll`] -- the operator's own `bot.did.policy` records //! (working name; see [`record`]'s doc comment), polled by listing the //! collection on a timer. See [`poll`]'s doc comment for the //! list-vs-track decision and why deletion is observability rather than //! a decision input. //! - [`blob`] -- the second fetch a record's `predicate` requires: the //! record itself carries only a blob reference (a CID), never the //! predicate's own bytes, so the operator's record stays small and the //! lexicon stays simple. See [`blob`]'s doc comment for why this fetch //! fails independently of the listing above, what a policy document may //! be, and how content-address caching keeps a re-poll cheap. //! //! [`merge::build`] assembles all three into one tree. [`compile`] is where //! a record whose `predicate` this build cannot compile gets scoped, //! fail-closed treatment; [`record`] is where a record that is not a //! well-formed policy at all -- a malformed `applicability` -- is told //! apart from that and reported instead, enforcing nothing. //! //! # A partial view is the normal condition, not an error //! //! `plan/policy.md`: policies are not a set, they are independent records, //! and polling only ever samples a random point of that set. This crate is //! built around that: [`poll::OperatorView`] can be empty, can be missing //! records it has not yet seen, and can still be enforcing a record the //! operator has since deleted (until the next successful poll notices the //! absence) -- none of that corrupts anything else's verdict, because //! deny-only policies never interact. The one thing this crate is careful //! never to do is let an *older* observation of a record it already holds //! overwrite a newer one, and let a *failed* refresh discard anything -- //! see [`poll`]'s doc comment for both. //! //! # Provisional //! //! `plan/policy.md` leaves the operator-policy lexicon unsettled (working //! name `bot.did.policy`) and says adding a new record type is the owner's //! decision. Nothing in this crate is that lexicon: [`record`]'s wire shape //! is this crate's own guess, used only so the loading machinery has //! something concrete to load. See [`record`]'s doc comment for exactly //! what a real lexicon would need to settle. #![forbid(unsafe_code)] pub mod blob; pub mod builtin; pub mod compile; pub mod last_good; pub mod merge; #[cfg(feature = "fetch")] pub mod poll; pub mod record; pub mod startup; pub use blob::{BlobCache, PredicateBlobError, MAX_PREDICATE_BLOB_BYTES}; pub use builtin::{builtin_policies, builtin_records, BuiltinPolicy}; pub use compile::{CompileReport, Language, PolicyWarning, ScopedDenial}; pub use last_good::LastGoodPolicies; pub use merge::{build, LastGoodFallback, LoadReport}; #[cfg(feature = "fetch")] pub use poll::{list_records, OperatorPoll, OperatorView, PollError, PollEvent}; pub use record::{parse_batch, parse_record, ParsedPolicy, PredicateRef, RecordOutcome}; pub use startup::{load as load_startup, StartupError, StartupLoad};