Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1ca4677
smp server: messaging services (#1565)
epoberezkin Nov 7, 2025
3ccf854
servers: maintain xor-hash of all associated queue IDs in PostgreSQL …
epoberezkin Nov 25, 2025
5e9b164
agent: fail when per-connection transport isolation is used with serv…
epoberezkin Nov 25, 2025
38e8999
agent: service subscription events (#1671)
epoberezkin Nov 27, 2025
ff7bdbc
Merge branch 'master' into rcv-services
epoberezkin Dec 3, 2025
2ea9a9a
agent: finalize initial service subscriptions, remove associations on…
epoberezkin Dec 5, 2025
35fe5ac
Merge branch 'master' into rcv-services
epoberezkin Dec 5, 2025
8389407
Merge branch 'master' into rcv-services
epoberezkin Dec 13, 2025
f5eb735
servers: service stats and logging, allow services without option (re…
epoberezkin Dec 14, 2025
568500c
Merge branch 'master' into rcv-services
epoberezkin Dec 14, 2025
c8a7243
Merge branch 'master' into rcv-services
epoberezkin Dec 15, 2025
a1277bf
agent: remove service queue association when service ID changed, proc…
epoberezkin Dec 19, 2025
11ae20e
ntf server: use different client certs for each SMP server, remove su…
epoberezkin Dec 22, 2025
bafdbc1
smp protocol: fix encoding for SOKS/ENDS responses (#1683)
epoberezkin Dec 25, 2025
9e813c2
Merge branch 'master' into rcv-services
epoberezkin Dec 25, 2025
db4b27e
agent: create user with option to enable client service (#1684)
epoberezkin Dec 27, 2025
d908404
Merge branch 'master' into rcv-services
epoberezkin Jan 15, 2026
502d923
agent: minor fixes
epoberezkin Jan 17, 2026
ac825b0
Merge branch 'master' into rcv-services
epoberezkin Jan 24, 2026
84e8b72
docs: update protocol (#1705)
epoberezkin Jan 27, 2026
aebc01b
Merge branch 'master' into rcv-services
epoberezkin Mar 3, 2026
1d30579
Merge branch 'master' into rcv-services
epoberezkin Mar 4, 2026
8518f60
docs: agent threat model
evgeny-simplex Mar 7, 2026
c624a10
Merge branch 'master' into rcv-services
evgeny-simplex Mar 9, 2026
3a25561
Merge branch 'master' into rcv-services
epoberezkin Mar 9, 2026
3c57523
update protocol docs
evgeny-simplex Mar 9, 2026
583f4e0
update RFCs (#1730)
epoberezkin Mar 9, 2026
f745ce5
docs: fix minor issues in protocols
evgeny-simplex Mar 10, 2026
01785d5
docs: add e2e encrypted message wire encoding to PQDR spec
evgeny-simplex Mar 10, 2026
98351cf
docs: add missing encodings and other protocol corrections
evgeny-simplex Mar 10, 2026
b81670c
docs: move implemented rfcs
evgeny-simplex Mar 10, 2026
48eba59
Merge branch 'master' into rcv-services
epoberezkin Mar 12, 2026
8f42747
smp: service fixes (#1737)
epoberezkin Mar 20, 2026
1a255f2
Merge branch 'master' into rcv-services
epoberezkin Mar 20, 2026
931b1cc
smp-server: memory usage analysis
shumvgolove Mar 18, 2026
404dd10
further analysis
shumvgolove Mar 18, 2026
646476f
smp-server: add periodic memory diagnostics logging
shumvgolove Mar 19, 2026
c6d6e30
add memory-analysis-results based on the produced logs
shumvgolove Mar 19, 2026
323be9c
feat(smp-server): add per-client and RTS memory metrics
shumvgolove Mar 20, 2026
4c8ace4
update findings
shumvgolove Mar 20, 2026
2a0af04
feat(bench): add smp-server memory benchmark framework
shumvgolove Mar 20, 2026
9a0a85a
feat(bench): self-contained Docker Compose setup
shumvgolove Mar 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions bench/ClientSim.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}

module ClientSim
( SimClient (..),
connectClient,
createQueue,
subscribeQueue,
sendMessage,
receiveAndAck,
connectN,
benchKeyHash,
)
where

import Control.Concurrent.Async (mapConcurrently)
import Control.Concurrent.STM
import Control.Monad (forM_)
import Control.Monad.Except (runExceptT)
import Data.ByteString.Char8 (ByteString)
import Data.List (unfoldr)
import qualified Data.List.NonEmpty as L
import Network.Socket (ServiceName)
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Encoding
import Simplex.Messaging.Protocol
import Simplex.Messaging.Transport
import Simplex.Messaging.Transport.Client
import Simplex.Messaging.Version

data SimClient = SimClient
{ scHandle :: THandleSMP TLS 'TClient,
scRcvKey :: C.APrivateAuthKey,
scRcvId :: RecipientId,
scSndId :: SenderId,
scDhSecret :: C.DhSecret 'C.X25519
}

benchKeyHash :: C.KeyHash
benchKeyHash = "LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI="

connectClient :: TransportHost -> ServiceName -> IO (THandleSMP TLS 'TClient)
connectClient host port = do
let tcConfig = defaultTransportClientConfig {clientALPN = Just alpnSupportedSMPHandshakes}
runTransportClient tcConfig Nothing host port (Just benchKeyHash) $ \h ->
runExceptT (smpClientHandshake h Nothing benchKeyHash supportedClientSMPRelayVRange False Nothing) >>= \case
Right th -> pure th
Left e -> error $ "SMP handshake failed: " <> show e

connectN :: Int -> TransportHost -> ServiceName -> IO [THandleSMP TLS 'TClient]
connectN n host port = do
let batches = chunksOf 100 [1 .. n]
concat <$> mapM (\batch -> mapConcurrently (\_ -> connectClient host port) batch) batches

createQueue :: THandleSMP TLS 'TClient -> IO SimClient
createQueue h = do
g <- C.newRandom
(rPub, rKey) <- atomically $ C.generateAuthKeyPair C.SEd448 g
(sPub, sKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
(dhPub, dhPriv :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
-- NEW command
Resp "1" NoEntity (Ids rId sId srvDh) <- signSendRecv h rKey ("1", NoEntity, New rPub dhPub)
let dhShared = C.dh' srvDh dhPriv
-- KEY command (secure queue)
Resp "2" _ OK <- signSendRecv h rKey ("2", rId, KEY sPub)
pure SimClient {scHandle = h, scRcvKey = rKey, scRcvId = rId, scSndId = sId, scDhSecret = dhShared}

subscribeQueue :: SimClient -> IO ()
subscribeQueue SimClient {scHandle = h, scRcvKey = rKey, scRcvId = rId} = do
Resp "3" _ (SOK _) <- signSendRecv h rKey ("3", rId, SUB)
pure ()

sendMessage :: THandleSMP TLS 'TClient -> C.APrivateAuthKey -> SenderId -> ByteString -> IO ()
sendMessage h sKey sId body = do
Resp "4" _ OK <- signSendRecv h sKey ("4", sId, SEND noMsgFlags body)
pure ()

receiveAndAck :: SimClient -> IO ()
receiveAndAck SimClient {scHandle = h, scRcvKey = rKey, scRcvId = rId} = do
(_, _, Right (MSG RcvMessage {msgId = mId})) <- tGet1 h
Resp "5" _ OK <- signSendRecv h rKey ("5", rId, ACK mId)
pure ()

-- Helpers (same patterns as ServerTests.hs)

pattern Resp :: CorrId -> EntityId -> BrokerMsg -> Transmission (Either ErrorType BrokerMsg)
pattern Resp corrId queueId command <- (corrId, queueId, Right command)

pattern Ids :: RecipientId -> SenderId -> RcvPublicDhKey -> BrokerMsg
pattern Ids rId sId srvDh <- IDS (QIK rId sId srvDh _ _ Nothing Nothing)

pattern New :: RcvPublicAuthKey -> RcvPublicDhKey -> Command 'Creator
pattern New rPub dhPub = NEW (NewQueueReq rPub dhPub Nothing SMSubscribe (Just (QRMessaging Nothing)) Nothing)

signSendRecv :: (Transport c, PartyI p) => THandleSMP c 'TClient -> C.APrivateAuthKey -> (ByteString, EntityId, Command p) -> IO (Transmission (Either ErrorType BrokerMsg))
signSendRecv h pk t = do
signSend h pk t
(r L.:| _) <- tGetClient h
pure r

signSend :: (Transport c, PartyI p) => THandleSMP c 'TClient -> C.APrivateAuthKey -> (ByteString, EntityId, Command p) -> IO ()
signSend h@THandle {params} (C.APrivateAuthKey a pk) (corrId, qId, cmd) = do
let TransmissionForAuth {tForAuth, tToSend} = encodeTransmissionForAuth params (CorrId corrId, qId, cmd)
authorize t = (,Nothing) <$> case a of
C.SEd25519 -> Just . TASignature . C.ASignature C.SEd25519 $ C.sign' pk t
C.SEd448 -> Just . TASignature . C.ASignature C.SEd448 $ C.sign' pk t
C.SX25519 -> (\THAuthClient {peerServerPubKey = k} -> TAAuthenticator $ C.cbAuthenticate k pk (C.cbNonce corrId) t) <$> thAuth params
Right () <- tPut1 h (authorize tForAuth, tToSend)
pure ()

tPut1 :: Transport c => THandle v c 'TClient -> SentRawTransmission -> IO (Either TransportError ())
tPut1 h t = do
rs <- tPut h (Right t L.:| [])
case rs of
(r : _) -> pure r
[] -> error "tPut1: empty result"

tGet1 :: (ProtocolEncoding v err cmd, Transport c) => THandle v c 'TClient -> IO (Transmission (Either err cmd))
tGet1 h = do
(r L.:| _) <- tGetClient h
pure r

chunksOf :: Int -> [a] -> [[a]]
chunksOf n = unfoldr $ \xs -> if null xs then Nothing else Just (splitAt n xs)
25 changes: 25 additions & 0 deletions bench/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM haskell:9.6.3 AS build

WORKDIR /src

# Copy cabal file first for dependency caching
COPY simplexmq.cabal cabal.project* ./
RUN cabal update && cabal build --only-dependencies -f server_postgres smp-server-bench || true

# Copy full source
COPY . .
RUN cabal build -f server_postgres smp-server-bench \
&& cp $(cabal list-bin -f server_postgres smp-server-bench) /usr/local/bin/smp-server-bench

FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
libgmp10 libpq5 libffi8 zlib1g ca-certificates \
&& rm -rf /var/lib/apt/lists/*

COPY --from=build /usr/local/bin/smp-server-bench /usr/local/bin/smp-server-bench
COPY tests/fixtures /app/tests/fixtures

WORKDIR /app

ENTRYPOINT ["smp-server-bench"]
Loading
Loading