pub struct ChannelConfig {
pub channel_id: ChannelId,
pub visibility: Visibility,
pub publish_caps: Option<CapabilityFilter>,
pub subscribe_caps: Option<CapabilityFilter>,
pub require_token: bool,
pub token_roots: Vec<EntityId>,
pub subscriber_origin_binding: Option<OriginBinding>,
pub queue_group_policy: QueueGroupPolicy,
pub priority: u8,
pub reliable: bool,
pub max_rate_pps: Option<u32>,
}Expand description
Channel configuration with capability-based access control.
Authorization flow:
- Node announces capabilities via
CapabilityAd - If
publish_capsis set, node’sCapabilitySetmust match the filter - If
require_tokenis true, node must also have a validPermissionToken - On success,
(origin_hash, channel_hash)is inserted into theAuthGuard
§Capability filters are advisory, not an access boundary
publish_caps / subscribe_caps match against a node’s
self-advertised CapabilitySet: a peer declares its own
capabilities in its own signed announcement, so any peer can
satisfy a cap-filter simply by advertising the required tag
(e.g. self-asserting role:admin). Treat cap-filters as
matchmaking / intent-routing, not as a security boundary.
The actual access boundary is require_token + token_roots:
a root-anchored TokenChain cannot be forged because each link
is signature-verified up to a root the channel explicitly trusts.
Any channel that must restrict who can publish or subscribe must
use token enforcement; a cap-filter alone restricts nothing.
Fields§
§channel_id: ChannelIdChannel identity (name + hash).
visibility: VisibilityVisibility scope for subnet routing.
publish_caps: Option<CapabilityFilter>Capability requirements for publishing. None = any node can
publish. Advisory only — matched against the node’s
self-advertised caps; use require_token for a real boundary.
subscribe_caps: Option<CapabilityFilter>Capability requirements for subscribing. None = any node can
subscribe. Advisory only — matched against the node’s
self-advertised caps; use require_token for a real boundary.
require_token: boolWhether a valid PermissionToken is required (in addition to capabilities).
token_roots: Vec<EntityId>Entities whose signature roots a valid token chain for this channel — the channel’s root(s) of trust.
When require_token is set, a presented TokenChain is only
honored if its root link (tokens[0].issuer) is one of these
entities. This is the anchor the bare-token path lacked: without
it check/can_subscribe only verified a token was internally
self-consistent (the named issuer signed it), so any peer could
self-issue issuer = subject = self and pass. An empty
token_roots combined with require_token = true fails
closed — there is no authority a chain could anchor to, so
nothing is authorized.
subscriber_origin_binding: Option<OriginBinding>Bind the dynamic name suffix to the subscriber’s own pinned
identity. None (default) = any peer that clears the other
gates may subscribe to any name this config covers.
Only meaningful on a prefix-registered config; see
OriginBinding. Unlike publish_caps / subscribe_caps,
this is an access boundary — it is evaluated against the
TOFU-pinned peer identity, which a peer cannot self-assert.
queue_group_policy: QueueGroupPolicyWho may join a queue group on this channel. See
QueueGroupPolicy; defaults to Unrestricted, which is the
historical behaviour.
priority: u8Default priority level for this channel’s packets (0 = lowest).
reliable: boolDefault reliability mode for streams on this channel.
max_rate_pps: Option<u32>Optional rate limit in packets per second.
Implementations§
Source§impl ChannelConfig
impl ChannelConfig
Sourcepub fn new(channel_id: ChannelId) -> Self
pub fn new(channel_id: ChannelId) -> Self
Create a new channel config with defaults (open access, global visibility).
Sourcepub fn with_visibility(self, visibility: Visibility) -> Self
pub fn with_visibility(self, visibility: Visibility) -> Self
Set visibility.
Sourcepub fn with_publish_caps(self, filter: CapabilityFilter) -> Self
pub fn with_publish_caps(self, filter: CapabilityFilter) -> Self
Set capability requirements for publishing.
Advisory matchmaking, not access control: caps are
self-advertised, so any peer can satisfy the filter by
declaring the tag. Combine with Self::with_token_roots to
actually restrict publishers.
Sourcepub fn with_subscribe_caps(self, filter: CapabilityFilter) -> Self
pub fn with_subscribe_caps(self, filter: CapabilityFilter) -> Self
Set capability requirements for subscribing.
Advisory matchmaking, not access control: caps are
self-advertised, so any peer can satisfy the filter by
declaring the tag. Combine with Self::with_token_roots to
actually restrict subscribers.
Sourcepub fn with_require_token(self, require: bool) -> Self
pub fn with_require_token(self, require: bool) -> Self
Require a valid permission token.
Sourcepub fn with_token_roots(self, roots: Vec<EntityId>) -> Self
pub fn with_token_roots(self, roots: Vec<EntityId>) -> Self
Require a token chain rooted at one of roots. Sets
require_token = true and installs the channel’s authorizing
root(s). This is the safe way to turn on token enforcement —
with_require_token(true) alone (no roots) fails every
authorization closed, since a chain has no authority to anchor
to.
Sourcepub fn token_required(&self) -> bool
pub fn token_required(&self) -> bool
Whether this channel enforces token authorization.
Enforcement is on when require_token is set or any
token_roots are configured. Coupling the two means a config
that names roots but forgot to flip require_token (e.g. built
by struct literal or direct field assignment rather than
Self::with_token_roots) still enforces, instead of silently
admitting every peer — the fields are both public, so the
invariant can’t be guaranteed at construction. All token gates
(subscribe, publish, the periodic sweep, the publish re-check)
consult this rather than require_token directly.
Sourcepub fn with_subscriber_origin_binding(self, binding: OriginBinding) -> Self
pub fn with_subscriber_origin_binding(self, binding: OriginBinding) -> Self
Bind this (prefix-registered) channel family’s dynamic suffix to
the subscribing peer’s own pinned identity — see
OriginBinding.
Callers subscribing to a bound family must have had their identity pinned on the publisher first, which happens when their signature-verified direct capability announcement arrives. A peer that has not announced is rejected (fail closed).
Sourcepub fn caps_allow_subscribe(&self, node_caps: &CapabilitySet) -> bool
pub fn caps_allow_subscribe(&self, node_caps: &CapabilitySet) -> bool
Do this node’s advertised capabilities satisfy the channel’s
subscribe_caps filter?
Split out of Self::can_subscribe for the TokenBound
queue-group path, which supplies its own token authority (the
group grant) but must still apply the capability filter.
Advisory, like every cap filter — see the type docs.
Sourcepub fn with_queue_group_policy(self, policy: QueueGroupPolicy) -> Self
pub fn with_queue_group_policy(self, policy: QueueGroupPolicy) -> Self
Restrict who may join a queue group on this channel — see
QueueGroupPolicy.
Sourcepub fn can_join_queue_group(
&self,
entity_id: &EntityId,
channel: &str,
group: &str,
chain: Option<&TokenChain>,
revocation: &RevocationRegistry,
skew_secs: u64,
) -> bool
pub fn can_join_queue_group( &self, entity_id: &EntityId, channel: &str, group: &str, chain: Option<&TokenChain>, revocation: &RevocationRegistry, skew_secs: u64, ) -> bool
Does chain authorize this peer to join queue group group on
channel?
Returns true when the channel places no restriction. Under
QueueGroupPolicy::TokenBound the chain must root at one of
this channel’s token_roots, bind at its leaf to entity_id,
and authorize SUBSCRIBE on the derived group-grant hash — a
grant naming the specific group, not the channel.
Fails closed: Deny refuses, and TokenBound with no chain (or
no roots) refuses.
Sourcepub fn with_priority(self, priority: u8) -> Self
pub fn with_priority(self, priority: u8) -> Self
Set default priority.
Sourcepub fn with_reliable(self, reliable: bool) -> Self
pub fn with_reliable(self, reliable: bool) -> Self
Set default reliability.
Sourcepub fn with_rate_limit(self, pps: u32) -> Self
pub fn with_rate_limit(self, pps: u32) -> Self
Set rate limit.
Sourcepub fn can_publish(
&self,
node_caps: &CapabilitySet,
entity_id: &EntityId,
channel_hash: ChannelHash,
chain: Option<&TokenChain>,
revocation: &RevocationRegistry,
skew_secs: u64,
) -> bool
pub fn can_publish( &self, node_caps: &CapabilitySet, entity_id: &EntityId, channel_hash: ChannelHash, chain: Option<&TokenChain>, revocation: &RevocationRegistry, skew_secs: u64, ) -> bool
Check if entity_id is authorized to publish on channel_hash,
presenting chain.
See Self::can_subscribe for the chain-verification contract
and for why channel_hash is a parameter; this is the
PUBLISH-scope counterpart.
Sourcepub fn can_subscribe(
&self,
node_caps: &CapabilitySet,
entity_id: &EntityId,
channel_hash: ChannelHash,
chain: Option<&TokenChain>,
revocation: &RevocationRegistry,
skew_secs: u64,
) -> bool
pub fn can_subscribe( &self, node_caps: &CapabilitySet, entity_id: &EntityId, channel_hash: ChannelHash, chain: Option<&TokenChain>, revocation: &RevocationRegistry, skew_secs: u64, ) -> bool
Check if entity_id is authorized to subscribe to
channel_hash, presenting chain.
When require_token is set, chain must be a TokenChain
that (a) roots at one of Self::token_roots, (b) is bound at
its leaf to entity_id (the AEAD-verified presenter), and (c)
authorizes SUBSCRIBE on channel_hash at every link with no
link revoked. A missing chain, an empty token_roots, or a
chain that fails verification all reject — fail closed.
§Why channel_hash is a parameter
It is the hash of the channel the caller actually asked for, NOT
self.channel_id.hash(). Those coincide for an exact-match
config, but a prefix-registered config’s channel_id is a
sentinel that insert_prefix itself documents as “not used for
hash lookups” — and verifying against it meant a token minted
for the sentinel authorized every channel under the prefix,
silently degrading a per-channel binding to a per-prefix one.
Taking the channel explicitly also removes the standing
temptation to reuse one config across many channels and get a
gate that answers about the wrong one.
Sourcepub fn reverify_subscribe(
&self,
chain: &TokenChain,
entity_id: &EntityId,
channel_hash: ChannelHash,
revocation: &RevocationRegistry,
skew_secs: u64,
) -> bool
pub fn reverify_subscribe( &self, chain: &TokenChain, entity_id: &EntityId, channel_hash: ChannelHash, revocation: &RevocationRegistry, skew_secs: u64, ) -> bool
Re-verify a previously-presented SUBSCRIBE chain for
channel_hash against the current clock + revocation floors,
anchored to this channel’s roots. Shared by the periodic expiry
sweep and the publish-time re-check so the root-anchoring
contract (which roots, which action, which channel hash) lives
in exactly one place instead of being re-threaded at each call
site — where it had already started to diverge (token_roots
vs. an unwrap_or(&[]) fallback).
channel_hash is the requested channel’s — see
Self::can_subscribe. Passing the config’s own hash here is
what made prefix-registered channels retain a chain under the
sentinel key that the publish path (keyed on the real channel)
could never find, so every such subscriber was accepted and then
revoked before its first delivery.
Sourcepub fn reverify_subscribe_presigned(
&self,
chain: &TokenChain,
entity_id: &EntityId,
channel_hash: ChannelHash,
revocation: &RevocationRegistry,
skew_secs: u64,
) -> bool
pub fn reverify_subscribe_presigned( &self, chain: &TokenChain, entity_id: &EntityId, channel_hash: ChannelHash, revocation: &RevocationRegistry, skew_secs: u64, ) -> bool
Like Self::reverify_subscribe but skips the per-link ed25519
signature verification — for callers re-checking a chain whose
signatures already verified once (immutable tokens). Time
bounds, revocation, anchoring, and scope are still re-checked.
See TokenChain::verify_authorizes_presigned.
Trait Implementations§
Source§impl Clone for ChannelConfig
impl Clone for ChannelConfig
Source§fn clone(&self) -> ChannelConfig
fn clone(&self) -> ChannelConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more