Skip to main content

CertmeshCore

Struct CertmeshCore 

Source
pub struct CertmeshCore { /* private fields */ }
Expand description

CertmeshCore - the main domain facade.

Wraps the shared certmesh state and exposes commands, status, and HTTP routes to the binary crate.

Clone is a cheap Arc bump — every clone shares the same underlying CertmeshState (CA, roster, auth). This lets the composition layer hold a facade while also building an AcmeState over the same state.

Implementations§

Source§

impl CertmeshCore

Source

pub async fn client_for(&self, peer: &Peer) -> Result<PeerClient, CertmeshError>

Build a PeerClient for a discovered Peer (ADR-020 §6).

Mode-transparent: an Open peer yields a plain-HTTP client; a secure peer yields an mTLS client presenting this node’s identity and pinning the mesh CA. The caller writes one code path.

Errors (loudly, not via a silent handshake failure):

  • the peer advertises no dialable address/port;
  • the peer requires authentication but this node is Open (no identity);
  • the peer anchors to a different mesh (its fp= ≠ our CA fingerprint).
Source

pub async fn tls_client_config_for( &self, peer: &Peer, ) -> Result<Option<ClientConfig>, CertmeshError>

Resolve the posture-keyed TLS config for a peer, for consumers that drive their own HTTP/transport stack (wishlist 3.1).

Returns:

  • Ok(None) — the peer is Open: dial it in plain HTTP, no TLS.
  • Ok(Some(config)) — the peer is secure: a rustls::ClientConfig presenting this node’s leaf and pinning the mesh CA, ready to hand to reqwest (use_preconfigured_tls), hyper, or a tower service.

This is the lower-level dual of client_for: koi owns the transport policy (which leaf, which pin, plain-vs-mTLS by posture); the consumer owns the request shape (verbs, headers, streaming, large bodies) — so zen can route REST + SSE + large transfers through one mode-transparent client without koi re-implementing an HTTP client. Same loud errors as client_for (missing identity, different mesh).

Source§

impl CertmeshCore

Source

pub async fn rotate_auth( &self, passphrase: &str, method: Option<&str>, ) -> Result<AuthSetup, CertmeshError>

Rotate the auth credential - generates new credential, persists, returns setup info.

If method is None, keeps the current method. If Some("totp"), switches to that method.

Source

pub async fn backup( &self, ca_passphrase: &str, backup_passphrase: &str, ) -> Result<Vec<u8>, CertmeshError>

Create an encrypted backup bundle for the certmesh state.

Source

pub async fn restore( &self, backup_bytes: &[u8], backup_passphrase: &str, new_passphrase: &str, ) -> Result<(), CertmeshError>

Restore certmesh state from an encrypted backup bundle.

Source

pub async fn revoke_member( &self, hostname: &str, operator: Option<String>, reason: Option<String>, ) -> Result<(), CertmeshError>

Revoke a member and persist the revocation list.

Source§

impl CertmeshCore

Source

pub async fn set_reload_hook( &self, hostname: &str, hook: &str, ) -> Result<(), CertmeshError>

Set the post-renewal reload hook for a member.

Source

pub async fn set_member_role( &self, hostname: &str, role: MemberRole, ) -> Result<(), CertmeshError>

Set the role of a member in the roster.

Source

pub async fn unlock(&self, passphrase: &str) -> Result<(), CertmeshError>

Unlock the CA with a passphrase.

Source

pub async fn unlock_with_master_key( &self, master_key: &[u8; 32], ) -> Result<(), CertmeshError>

Unlock the CA with a pre-unwrapped master key (TOTP or auto-unlock).

This bypasses passphrase-based auth.json decryption. The auth credential (for API gating) is not loaded - callers should use the slot table’s embedded TOTP shared_secret for verification if auth gating is needed.

Source

pub async fn unlock_with_totp(&self, code: &str) -> Result<(), CertmeshError>

Unlock the CA using a TOTP code against the unlock slot table.

Loads the slot table, verifies the TOTP code, unwraps the master key, and decrypts the CA key.

Source

pub fn save_auto_unlock_key_at( paths: &CertmeshPaths, passphrase: &str, ) -> Result<(), CertmeshError>

Save a passphrase for automatic unlock on reboot, rooted at explicit paths so the vault is co-located with the CA it unlocks.

Uses the koi-crypto vault which automatically selects the strongest available backend: platform credential store (DPAPI, Keychain, Secret Service) first, machine-bound Argon2id derivation as fallback. The counterpart reader is Self::read_auto_unlock_key.

Source

pub fn read_auto_unlock_key( paths: &CertmeshPaths, ) -> Result<Option<Zeroizing<String>>, CertmeshError>

Read the stored auto-unlock passphrase from the vault, if any.

The auto-unlock passphrase lives in the koi-crypto vault (written by Self::save_auto_unlock_key_at, which deletes any legacy plaintext file). This is the single source of truth for that location: boot paths that need to unlock the CA at construction time call this instead of reading a plaintext file that no longer exists.

Returns Ok(None) when no key is stored, Ok(Some(pp)) when one is found, and Err when the vault cannot be opened or read.

Source

pub async fn try_auto_unlock(&self) -> Result<bool, CertmeshError>

Try to auto-unlock the CA from the vault.

Returns Ok(true) if the CA was unlocked, Ok(false) if no stored key exists, and Err if the key exists but decryption failed (corrupt key, changed passphrase, etc.).

Source

pub fn configure_auto_unlock( &self, auto_unlock: bool, passphrase: &str, ) -> Result<(), CertmeshError>

Configure auto-unlock from the create-time auto_unlock decision.

This is the single source of truth for the unlock-on-boot decision. When auto_unlock is true and a passphrase is present, the passphrase is saved to the koi-crypto vault (read back at boot by Self::read_auto_unlock_key) and the slot table is marked. Call it after CA creation from any init path (direct API or ceremony).

Source§

impl CertmeshCore

Source

pub async fn enroll( &self, request: &JoinRequest, ) -> Result<JoinResponse, CertmeshError>

Process an enrollment request. Returns the join response on success.

The joining machine’s hostname comes from the request - not from hostname::get() which would return the CA server’s hostname.

Source

pub async fn self_enroll(&self) -> Result<SelfEnrollment, CertmeshError>

Self-enroll the daemon as a certmesh member.

Called automatically after CA creation (and on every daemon start) to get the server leaf the mTLS + ACME listeners use. This is the one issuance path that key-gens on the CA (the CA’s own identity, ca::issue_certificate — ADR-017 P3); member leaves only ever come from a member CSR.

Idempotent except when the on-disk leaf is within the CA policy’s renew_threshold_days: then it re-issues, so a restart refreshes the listener cert (the CA self-renews — no live mTLS reload yet; the restart is the reload point).

Source§

impl CertmeshCore

Source

pub async fn ca_fingerprint(&self) -> Option<String>

The CA certificate fingerprint, or None when no CA is initialized.

Reads the in-memory CA when unlocked, else derives it from the on-disk CA cert (the fingerprint is public). Used by the daemon to advertise the CA’s fingerprint in the _certmesh._tcp mDNS TXT (ADR-017 F12) and as a cheap preflight datum.

Source

pub async fn certmesh_status(&self) -> CertmeshStatus

Get the current certmesh status.

Source

pub fn posture(&self) -> Posture

This node’s current trust posture — the mode oracle every mode-transparent primitive consults (ADR-020 §0).

signed is true when this node holds a usable cryptographic identity: its CA-signed leaf (cert.pem/key.pem) is on disk and the node is anchored to a mesh (the CA is initialized here, or a member.json records the mesh it joined — so an orphaned leaf left after destroy does not read as secure). A cheap filesystem check, safe to call from any primitive. encrypted (the Confidential rung) stays false until the seal/open encryption rung lands (ADR-020 §4).

Posture answers “do I have an identity”, not “is it fresh” — identity health (expiry, renewal status) is reported separately by ensure_identity / diagnose (later ADR-020 phases).

Source

pub async fn local_identity(&self) -> Option<Identity>

Load this node’s live identity from disk, or None if it has none.

Read-only: loads the on-disk leaf (cert/key) for the local hostname plus the CA anchor it chains to, derives the pinned CA fingerprint, and computes the leaf’s renewal/expiry health from the CA-held policy. Returns None when the node is Open — consistent with posture.signed. Does not renew or enroll (that is ensure_identity’s job).

Source

pub async fn ensure_identity(&self) -> Option<Identity>

Ensure this node holds a current identity, then return it (None if it cannot — the node is Open with no way to enroll). ADR-020 §7.

Mode-transparent + idempotent — the consumer calls this without branching:

  • Open (no CA, not a member): returns None.
  • CA node (CA unlocked): self-enrolls if needed and re-issues a self leaf that is within the renewal threshold (local, no network).
  • Joined member: pull-renews from the CA when the leaf is due (renew_self_if_due); best-effort — on a network/CA failure it logs and returns the current (un-renewed) identity rather than erroring.

First-join identity acquisition that needs out-of-band authorization (an invite/TOTP) is not performed here — that is the explicit join flow.

Source

pub async fn sign(&self, bytes: &[u8]) -> Envelope

Sign bytes into an Envelope (ADR-020 §3).

Mode-transparent: Open posture → a freshness-stamped passthrough (no signature); Authenticated → ES256-signed, carrying this node’s leaf cert so any holder of the CA can verify it. The consumer calls this identically in both postures.

Source

pub async fn verify(&self, env: &Envelope) -> Assurance

Verify an Envelope → an Assurance (ADR-020 §3).

Self-contained (carry-cert): validates the carried leaf against this node’s pinned CA + checks freshness + best-effort revocation. Read a trusted identity only via Assurance::identity(). On an Open node (no anchor) any envelope verifies as Anonymous.

Source

pub async fn seal(&self, bytes: &[u8]) -> Sealed

Seal bytes into a Sealed (ADR-020 §4).

The confidentiality rung, shipped today as passthrough: the bytes are signed (integrity + freshness) but not encrypted. Reuses sign’s machinery — a Sealed is a signed Envelope plus a confidentiality version tag. The consumer codes against the final API now; the group-key rung lands later with no consumer change. A one-time warn! makes the passthrough (un-encrypted) state loud, not silent.

Source

pub async fn open(&self, sealed: &Sealed) -> Result<Opened, CertmeshError>

Open a SealedOpened (ADR-020 §4): the recovered bytes plus the trust state they arrived with.

Self-contained (carry-cert), reusing verify’s machinery. A tampered / unknown-signer / expired / revoked message yields an Err, never bytes — read a trusted identity via opened.assurance.identity().

Source

pub async fn diagnose(&self) -> TrustDiagnosis

Run the trust-doctor (ADR-020 §13) → a structured [TrustDiagnosis].

Aggregates this node’s real trust state — posture, identity + renewal health (reusing local_identity), on-disk-leaf integrity (chains to its CA), self-revocation, and the CA trust-install limitation — into distinct, named checks each carrying an exact remedy. The rollup exits non-zero only when something is RED (TrustDiagnosis::exit_code).

Source

pub fn require_auth(&self, router: Router) -> Router

Gate router’s routes by authentication (ADR-020 §6 require_auth).

Mode-transparent: a no-op in Open posture (homelab-open); in secure posture every request must carry an authenticated client CN (the mTLS ClientCn the listener / same-port dial injects) or it is rejected with 401. Apply once to your write routes — no per-handler boilerplate, and the same consumer code runs green in both postures.

(P2 gates on the mTLS client identity; a signed-envelope-header path is a planned refinement. For per-CN/role authorization, see require_auth_with.)

Source

pub fn require_auth_with<F>(&self, router: Router, policy: F) -> Router
where F: Fn(&str, &Request) -> bool + Send + Sync + 'static,

Gate router’s routes by authentication and a caller-supplied CN/role policy (ADR-020 §6, wishlist 4.1).

Like require_auth — a no-op in Open posture — but in secure posture, after confirming an authenticated client CN, it calls policy(cn, &request): true allows the request, false rejects it with 403. This lets a consumer express “only these CNs/roles may write” (an allowlist, a roster-role check, a path-scoped rule) without re-implementing the middleware or re-deriving the mTLS identity. Keep require_auth for the zero-config “any mesh member” default.

The policy receives the authoritative mTLS CN (derived from the client certificate, never a claimed field) and the full axum request, so it can branch on method/path as well as identity.

// Only `web-01` and `web-02` may reach the write routes.
let allow = ["web-01", "web-02"];
let router = core.require_auth_with(router, move |cn, _req| allow.contains(&cn));
Source§

impl CertmeshCore

Source

pub async fn create( &self, req: CreateCaRequest, ) -> Result<CreateCaResponse, CertmeshError>

Initialize a new CA and self-enroll this node as the primary member.

Full CA-initialization orchestration: decode entropy, create the CA, generate the TOTP auth credential, create and persist the roster, self-enroll the CA node, install the CA cert in the OS trust store (best-effort), configure auto-unlock, and update in-memory state.

This is the single source of truth for CA creation; the HTTP create_handler is a thin delegate over this method.

Source

pub fn read_audit_log(&self) -> Result<String, CertmeshError>

Read the audit log entries.

Source

pub async fn destroy(&self) -> Result<(), CertmeshError>

Destroy all certmesh state - CA key, certs, roster, and audit log.

Removes all certmesh data from disk and resets in-memory state to uninitialized. This is irreversible. Used for testing cleanup and full mesh teardown.

Source§

impl CertmeshCore

Source

pub async fn open_enrollment(&self) -> Result<(), CertmeshError>

Open the enrollment window. Stays open until explicitly closed.

Source

pub async fn close_enrollment(&self) -> Result<(), CertmeshError>

Close the enrollment window.

Source

pub async fn mint_invite( &self, hostname: &str, ttl_mins: i64, ) -> Result<InviteResponse, CertmeshError>

Mint a single-use, hostname-bound enrollment invite (ADR-015 F2).

Returns the one-time plaintext token plus its absolute expiry. The CA stores only a hash; the joining host presents the token once via POST /join (invite_token). The mesh must be initialized — the invite is an authorization to enroll into an existing CA. Posture booleans are unchanged: the token replaces the credential, not the enrollment_open / requires_approval gates.

Source

pub async fn prepare_member_csr( &self, hostname: &str, sans: &[String], ) -> Result<String, CertmeshError>

Generate this member’s keypair + CSR and persist the private key locally.

The daemon generates the keypair, writes the private key to certs/<hostname>/key.pem (0600 on Unix), and returns only the CSR. The key never leaves the daemon; the CLI carries only the public CSR to the remote CA. Paired with Self::install_member_cert.

Source

pub async fn install_member_cert( &self, hostname: &str, cert_pem: &str, ca_pem: &str, ca_endpoint: Option<&str>, ca_fingerprint: Option<&str>, sans: &[String], policy: Option<CertPolicy>, ) -> Result<String, CertmeshError>

Install a CA-signed leaf next to the member key from Self::prepare_member_csr.

Writes cert.pem, ca.pem, and fullchain.pem into certs/<hostname>/ (the key is already there) and installs the CA root in the OS trust store (best-effort). Returns the cert directory path.

When ca_endpoint + ca_fingerprint are supplied (the normal join flow), it also writes the member renewal state (certmesh/member.json) so the background loop can later pull a rotate-key renewal from the CA over mTLS (ADR-017 F6). The pinned ca_fingerprint is verified against the supplied ca_pem before arming, so a mismatched pair never arms renewal.

Source§

impl CertmeshCore

Source

pub async fn renew_self_if_due(&self) -> Result<RenewOutcome, CertmeshError>

Member-initiated, rotate-key renewal (ADR-017 F6).

A no-op (RenewOutcome::NotApplicable) unless this node has a persisted member::MemberState (i.e. it joined a mesh). When its local leaf is within the CA policy’s renew_threshold_days, it:

  1. generates a fresh keypair + CSR (rotate-on-renewal — the new private key is held in memory until the install succeeds, never on the CA),
  2. POSTs only the CSR to the CA’s mTLS /v1/certmesh/renew, presenting its current (still-valid) leaf as the client identity,
  3. verifies the returned CA fingerprint matches its pin (anti-CA-swap),
  4. installs the new key + signed leaf locally and runs its reload hook.

The CA never generates or receives a member private key — on enroll or renew. If the network call fails (CA down, cert lapsed past mTLS validity) the local files are left untouched and the loop retries next tick.

Emits CertRenewed, CertRenewalFailed, and CertExpiringSoon lifecycle events.

Source

pub async fn pull_trust_bundle(&self) -> Result<BundleOutcome, CertmeshError>

Pull, verify, and apply the CA’s signed trust bundle (ADR-017 P1/F4).

A no-op (BundleOutcome::NotApplicable) unless this node joined a mesh. Fetches the self-verifying bundle over plain HTTP, verifies the ES256 signature against the pinned CA fingerprint, and rejects a strictly older seq (anti-rollback). On a newer bundle it refreshes the member’s cached policy and last_bundle_seq, and flags whether this node has been revoked mesh-wide.

Source

pub async fn health_check( &self, request: &HealthRequest, ) -> Result<HealthResponse, CertmeshError>

Validate a member’s health heartbeat.

Source

pub async fn node_role(&self) -> Option<MemberRole>

Get the current node’s roster role (if any).

Returns None if the roster has no entry matching the local hostname.

Source

pub async fn promote_self_to_primary(&self) -> Result<bool, CertmeshError>

Promote the local member to primary and demote any existing primary. Returns true if the roster was updated.

Source

pub async fn demote_self_to_standby(&self) -> Result<bool, CertmeshError>

Demote the local member to standby. Returns true if the roster changed.

Source

pub async fn add_alias_sans( &self, hostname: &str, sans: &[String], ) -> Result<bool, CertmeshError>

Add alias SANs to a member’s roster entry (used by DNS alias feedback).

Returns true if any SANs were added.

Source

pub fn local_hostname() -> Option<String>

Get the local hostname.

Source

pub async fn pinned_ca_fingerprint(&self) -> Option<String>

Get the pinned CA fingerprint for the local node (if set).

Source

pub async fn promote( &self, client_public_key: &[u8; 32], ) -> Result<PromoteResponse, CertmeshError>

Prepare promotion material for a standby.

Called on the primary when a standby requests promotion. Uses DH key agreement to encrypt the CA key for wire transfer.

Source§

impl CertmeshCore

Source

pub fn paths(&self) -> &CertmeshPaths

The resolved filesystem paths this core operates on.

The data root is resolved once at the composition root and injected via the *_with_paths constructors; every operation reads it from here. There is no ambient fallback.

Source

pub fn new_with_paths( ca: CaState, roster: Roster, auth_state: Option<AuthState>, paths: CertmeshPaths, ) -> Self

Create a new CertmeshCore with an unlocked CA and explicit paths.

Source

pub fn locked_with_paths(roster: Roster, paths: CertmeshPaths) -> Self

Create a CertmeshCore in locked state with explicit paths.

Source

pub fn uninitialized_with_paths(paths: CertmeshPaths) -> Self

Create a CertmeshCore in uninitialized state with explicit paths.

HTTP routes are still mounted so /create is reachable on a fresh install. All operations that require an initialized CA will return CaNotInitialized.

Source

pub fn routes(&self) -> Router

Build the HTTP router for this domain.

The binary crate mounts this at /v1/certmesh/.

Source

pub fn http_routes(&self) -> Router

Build the HTTP router for external embedding.

This mirrors routes() but avoids exposing CertmeshState.

Source

pub fn inter_node_routes(&self) -> Router

Build the inter-node router for the mTLS listener.

Contains only routes that require mutual TLS between mesh members: promote, health, renew, roster, set-hook.

Source

pub async fn set_approval_channel(&self, tx: Sender<ApprovalRequest>)

Set the approval channel used for enrollment approvals.

Source

pub fn subscribe(&self) -> Receiver<CertmeshEvent>

Subscribe to certmesh events.

Source

pub fn watch_posture(&self) -> Receiver<Posture>

Watch this node’s posture (ADR-020 §5). The receiver always holds the current Posture (so a new subscriber reads it immediately) and is notified on every Open↔Authenticated transition — the signal a listener supervisor uses to flip plain↔mTLS without polling. Transitions are also surfaced as KoiEvent::PostureChanged by the embedded facade.

Source

pub fn acme_state(&self, config: AcmeStateConfig) -> Arc<AcmeState>

Build the RFC 8555 ACME server state over this CA.

The binary calls this when starting the dedicated server-auth TLS listener, passing the ACME base URL, the Koi DNS zone, and the AcmeDnsSolver bridge. The returned AcmeState shares this core’s CA and roster (so ACME issuance lands in the same roster as TOTP enrollment), and is mounted via acme::routes.

Trait Implementations§

Source§

impl Capability for CertmeshCore

Source§

fn name(&self) -> &str

Source§

fn status<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = CapabilityStatus> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

impl Clone for CertmeshCore

Source§

fn clone(&self) -> CertmeshCore

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more