feat(server): broker agent for inter-actor coordination
Some checks failed
ci/woodpecker/push/server-vet Pipeline failed
ci/woodpecker/push/server-audit Pipeline was successful
ci/woodpecker/push/server-lint Pipeline failed
ci/woodpecker/push/server-test Pipeline was successful

This commit is contained in:
hdbg
2026-03-01 11:35:06 +01:00
parent 4beb34764d
commit cb05407bb6
13 changed files with 185 additions and 54 deletions

View File

@@ -6,7 +6,7 @@ use ed25519_dalek::VerifyingKey;
use tracing::error;
use crate::actors::client::{
ConnectionProps,
ClientConnection,
auth::state::{AuthContext, AuthStateMachine},
session::ClientSession,
};
@@ -54,7 +54,7 @@ fn parse_auth_event(payload: ClientRequestPayload) -> Result<AuthEvents, Error>
}
}
pub async fn authenticate(props: &mut ConnectionProps) -> Result<VerifyingKey, Error> {
pub async fn authenticate(props: &mut ClientConnection) -> Result<VerifyingKey, Error> {
let mut state = AuthStateMachine::new(AuthContext::new(props));
loop {
@@ -93,7 +93,7 @@ pub async fn authenticate(props: &mut ConnectionProps) -> Result<VerifyingKey, E
}
pub async fn authenticate_and_create(
mut props: ConnectionProps,
mut props: ClientConnection,
) -> Result<ClientSession, Error> {
let key = authenticate(&mut props).await?;
let session = ClientSession::new(props, key);

View File

@@ -8,7 +8,7 @@ use ed25519_dalek::VerifyingKey;
use tracing::error;
use super::Error;
use crate::{actors::client::ConnectionProps, db::schema};
use crate::{actors::client::ClientConnection, db::schema};
pub struct ChallengeRequest {
pub pubkey: VerifyingKey,
@@ -68,11 +68,11 @@ async fn create_nonce(db: &crate::db::DatabasePool, pubkey_bytes: &[u8]) -> Resu
}
pub struct AuthContext<'a> {
pub(super) conn: &'a mut ConnectionProps,
pub(super) conn: &'a mut ClientConnection,
}
impl<'a> AuthContext<'a> {
pub fn new(conn: &'a mut ConnectionProps) -> Self {
pub fn new(conn: &'a mut ClientConnection) -> Self {
Self { conn }
}
}

View File

@@ -5,7 +5,10 @@ use arbiter_proto::{
use kameo::actor::Spawn;
use tracing::{error, info};
use crate::{actors::client::session::ClientSession, db};
use crate::{
actors::{GlobalActors, client::session::ClientSession},
db,
};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ClientError {
@@ -15,27 +18,34 @@ pub enum ClientError {
UnexpectedRequestPayload,
#[error("State machine error")]
StateTransitionFailed,
#[error("Connection registration failed")]
ConnectionRegistrationFailed,
#[error(transparent)]
Auth(#[from] auth::Error),
}
pub type Transport = Box<dyn Bi<ClientRequest, Result<ClientResponse, ClientError>> + Send>;
pub struct ConnectionProps {
pub struct ClientConnection {
pub(crate) db: db::DatabasePool,
pub(crate) transport: Transport,
pub(crate) actors: GlobalActors,
}
impl ConnectionProps {
pub fn new(db: db::DatabasePool, transport: Transport) -> Self {
Self { db, transport }
impl ClientConnection {
pub fn new(db: db::DatabasePool, transport: Transport, actors: GlobalActors) -> Self {
Self {
db,
transport,
actors,
}
}
}
pub mod auth;
pub mod session;
pub async fn connect_client(props: ConnectionProps) {
pub async fn connect_client(props: ClientConnection) {
match auth::authenticate_and_create(props).await {
Ok(session) => {
ClientSession::spawn(session);

View File

@@ -4,15 +4,17 @@ use kameo::Actor;
use tokio::select;
use tracing::{error, info};
use crate::actors::client::{ClientError, ConnectionProps};
use crate::{actors::{
GlobalActors, client::{ClientError, ClientConnection}, router::RegisterClient
}, db};
pub struct ClientSession {
props: ConnectionProps,
props: ClientConnection,
key: VerifyingKey,
}
impl ClientSession {
pub(crate) fn new(props: ConnectionProps, key: VerifyingKey) -> Self {
pub(crate) fn new(props: ClientConnection, key: VerifyingKey) -> Self {
Self { props, key }
}
@@ -33,12 +35,18 @@ type Output = Result<ClientResponse, ClientError>;
impl Actor for ClientSession {
type Args = Self;
type Error = ();
type Error = ClientError;
async fn on_start(
args: Self::Args,
_: kameo::prelude::ActorRef<Self>,
this: kameo::prelude::ActorRef<Self>,
) -> Result<Self, Self::Error> {
args.props
.actors
.router
.ask(RegisterClient { actor: this })
.await
.map_err(|_| ClientError::ConnectionRegistrationFailed)?;
Ok(args)
}
@@ -80,10 +88,10 @@ impl Actor for ClientSession {
}
impl ClientSession {
pub fn new_test(db: crate::db::DatabasePool) -> Self {
pub fn new_test(db: db::DatabasePool, actors: GlobalActors) -> Self {
use arbiter_proto::transport::DummyTransport;
let transport: super::Transport = Box::new(DummyTransport::new());
let props = ConnectionProps::new(db, transport);
let props = ClientConnection::new(db, transport, actors);
let key = VerifyingKey::from_bytes(&[0u8; 32]).unwrap();
Self { props, key }
}