generated from mintlify/starter
-
Notifications
You must be signed in to change notification settings - Fork 0
add endpoint hooks page #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Frando
wants to merge
9
commits into
main
Choose a base branch
from
Frando/hooks
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
8d487d6
add endpoint hooks page
Frando a167002
add to nav
Frando 1b60127
chore: spelling
Frando 7a62f5c
update
Frando 12447b5
Update connecting/endpoint-hooks.mdx
okdistribute 644a7b9
Update connecting/endpoint-hooks.mdx
okdistribute 5cb34e3
Update connecting/endpoint-hooks.mdx
okdistribute f813f69
Add better preamble
okdistribute 683fbd1
Merge branch 'Frando/hooks' of github.com:okdistribute/docs into Fran…
okdistribute File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| --- | ||
| title: Observing & Rejecting Connections | ||
| --- | ||
|
|
||
| <Note> | ||
| Available in iroh v.96 and later. | ||
| </Note> | ||
|
|
||
| Endpoint Hooks allow you to intercept the connection-establishment process of an iroh `Endpoint`. | ||
|
|
||
| They are a lightweight, flexible mechanism for observing connection events or | ||
| rejecting connections based on conditions. The latter can be used to implement | ||
| custom authentication schemes. | ||
|
|
||
| Hooks run at two points: | ||
|
|
||
| 1. **Before an outgoing connection starts**. No packets have been sent yet. | ||
| 2. **After the QUIC/TLS handshake completes** for both incoming and outgoing connections. The remote endpoint ID, ALPN, and other metadata are available, but no application data has been sent or received yet. | ||
|
|
||
| Hooks are registered with `Endpoint::builder().hooks(...)`. If multiple hooks are installed, they run in the order they were added, and a rejection from any hook short-circuits the rest. | ||
|
|
||
| Note that hooks cannot *use* connections, they can only *observe* or *reject* them. This is an important separation of concerns: If hooks were allowed to use the connections in any way, they could interfere with the actual protocols running within these connections. Hooks can, however, *reject* connections before they are passed on to protocol handlers. This makes it possible to implement custom authentication schemes with hooks that work without any support from the protocols running in these connections. | ||
|
|
||
| > **Note:** Hooks live on the `Endpoint` instance. Never store an `Endpoint` inside your hook type (even indirectly), or it may cause reference-counting cycles and prevent clean shutdown. | ||
|
|
||
| ## Example: Observing connection events | ||
|
|
||
| This example shows a minimal hook implementation that logs the context available at each stage. It does not alter behavior, only observes. | ||
|
|
||
| ```rust | ||
| use iroh::{ | ||
| Endpoint, EndpointAddr, Watcher, | ||
| endpoint::{AfterHandshakeOutcome, BeforeConnectOutcome, ConnectionInfo, EndpointHooks}, | ||
| }; | ||
| use tracing::info; | ||
|
|
||
| /// Our hooks instance. | ||
| /// | ||
| /// As we are only observing, we don't need to hold any state, thus we use a zero-sized struct. | ||
| #[derive(Debug)] | ||
| struct LogHooks; | ||
|
|
||
| /// To use hooks, you need to implement the `EndpointHooks` trait. | ||
| impl EndpointHooks for LogHooks { | ||
| // Runs before an outgoing connection begins. | ||
| async fn before_connect( | ||
| &self, | ||
| remote_addr: &EndpointAddr, | ||
| alpn: &[u8], | ||
| ) -> BeforeConnectOutcome { | ||
| info!(?remote_addr, ?alpn, "attempting to connect"); | ||
| BeforeConnectOutcome::Accept | ||
| } | ||
|
|
||
| // Runs after the handshake for both incoming and outgoing connections. | ||
| // | ||
| // `ConnectionInfo` gives information about a connection, but doesn't allow to use it otherwise. | ||
| async fn after_handshake(&self, conn: &ConnectionInfo) -> AfterHandshakeOutcome { | ||
| // This tells us whether `conn` is an incoming or outgoing connection. | ||
| let side = conn.side(); | ||
| let remote = conn.remote_id().fmt_short(); | ||
|
|
||
| info!(%remote, alpn=?conn.alpn(), ?side, "connection established"); | ||
|
|
||
| // We can spawn a task to observe network path changes for this connection. | ||
| let mut path_updates = conn.paths(); | ||
| tokio::spawn(async move { | ||
| while let Ok(paths) = path_updates.updated().await { | ||
| info!(%remote, ?paths, "paths updated"); | ||
| } | ||
| }); | ||
|
|
||
| AfterHandshakeOutcome::Accept | ||
| } | ||
| } | ||
|
|
||
| #[tokio::main] | ||
| async fn main() -> n0_error::Result<()> { | ||
| tracing_subscriber::fmt::init(); | ||
| // Install the hooks on our endpoint. | ||
| let _endpoint = Endpoint::builder().hooks(LogHooks).bind().await?; | ||
| // Use `endpoint` normally... | ||
| Ok(()) | ||
| } | ||
| ``` | ||
|
|
||
| ## Example: Rejecting connections | ||
|
|
||
| Hooks can be used to enforce policy. If a hook returns a rejection result, the connection is immediately aborted. | ||
| The example below rejects all incoming connections after the handshake. Outgoing connections will still dial. | ||
|
|
||
| In real applications, you would inspect `ConnectionInfo` and reject connections by checking the connection's remote id or ALPN against authentication state in your app. | ||
|
|
||
| ```rust | ||
| use iroh::endpoint::{AfterHandshakeOutcome, ConnectionInfo, Endpoint, EndpointHooks, Side}; | ||
|
|
||
| #[derive(Debug)] | ||
| struct RejectIncomingHook; | ||
|
|
||
| impl EndpointHooks for RejectIncomingHook { | ||
| async fn after_handshake(&self, conn: &ConnectionInfo) -> AfterHandshakeOutcome { | ||
| // Unconditionally reject all incoming connections. | ||
| // In actual apps, you could conditionally allow or accept by checking the connection's | ||
| // ALPN and remote id. | ||
| if conn.side() == Side::Server { | ||
| AfterHandshakeOutcome::Reject { | ||
| error_code: 403u32.into(), | ||
| reason: b"rejected".into(), | ||
| } | ||
| } else { | ||
| AfterHandshakeOutcome::Accept | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[tokio::main] | ||
| async fn main() -> n0_error::Result<()> { | ||
| tracing_subscriber::fmt::init(); | ||
| let _endpoint = Endpoint::builder().hooks(RejectIncomingHook).bind().await?; | ||
| Ok(()) | ||
| } | ||
| ``` | ||
|
|
||
| ## More examples | ||
|
|
||
| There are a few fully-featured examples for using hooks in the iroh repository. | ||
|
|
||
| ### [Authentication layer](https://github.com/n0-computer/iroh/blob/feat-multipath/iroh/examples/auth-hook.rs) | ||
|
|
||
| Demonstrates how to build an authentication flow on top of hooks. This pattern keeps authentication separate from your application protocols while still integrating cleanly with iroh’s connection lifecycle. | ||
|
|
||
| * We implement a dedicated “auth” protocol (with its own ALPN) for performing a pre-authentication handshake. | ||
| * Outgoing connections run the pre-auth step before starting other connections. | ||
| * Incoming connections are checked against a set of authorized remote ids. | ||
| * If an incoming connection comes from a peer that hasn't successfully performed pre-auth, the connection is rejected. | ||
|
|
||
| ### [Monitoring connection and path events](https://github.com/n0-computer/iroh/blob/feat-multipath/iroh/examples/monitor-connections.rs) | ||
|
|
||
| This example demonstrates how hooks can feed information to external tasks, giving you flexible observability. | ||
|
|
||
| * A hook sends each `ConnectionInfo` to a monitoring task. | ||
| * The monitor can record events and stats. | ||
|
|
||
| ### [Aggregating information about remote endpoints](https://github.com/n0-computer/iroh/blob/feat-multipath/iroh/examples/remote-info.rs) | ||
|
|
||
| This example implements a `RemoteMap` that tracks and aggregates information about all remotes our endpoint knows about. | ||
| This can be useful if your app needs to choose between remotes, or for building diagnostic tools. | ||
|
|
||
| * A hook forwards `ConnectionInfo` updates into a worker task. | ||
| * The worker maintains a map of all remotes with counts of active connections and observed statistics (e.g. latency/RTT, whether relay/IP paths were used, etc). | ||
| * The `RemoteMap` exposes a simple API to query all known remotes and their aggregate metrics. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
Watcherimport is unused in this example. Consider removing it to keep the example clean and focused.