sh 209f7826cb smp-server: support namespaces (#1784)
* smp-server: namespaces resolver scaffolding

* smp-server: Names resolver hardening + cleanup

* smp-server: fuse parallel dispatchers

* smp-server: JSON wire format for NameRecord + Names.hs restructure

* smp-server: redact RpcAuth in Show

* smp-server: JSON wire fixups + spec rewrite + small cleanups

* plan: prepend implementation-diverged banner

* move SimplexName into shared module

* smp-server: name + contract whitelist on RSLV

* smp-server: address audit findings (canonical JSON, INI guards, SSRF, TLD case, shutdown)

* smp-server: round 2 audit fixes (label case, response cap, ipv6 link-local)

* smp-server: round 3 audit fixes (SSRF coverage, drop noop closeManager, CSV order)

* smp-server: round 4 audit fixes (0X-hex host, expanded IPv6 forms, pingEndpoint timeout)

* smp-server: hardcode TldRegistries (drop registry_tld_* INI keys)

* smp-server: round 6 audit fixes (IPv6 SSRF, redirects, ASCII labels)

- Reject IPv6 aliases of 169.254.169.254 (IPv4-compatible / IPv4-mapped /
  6to4 / NAT64) via numeric range check on parsed IPv6.
- Disable HTTP redirects on the Eth RPC request.
- Restrict SimplexName labels to ASCII (Cyrillic/Greek/full-width otherwise
  hash to different on-chain records and diverge from UTS-46 registrars).
- pingEndpoint: only JsonRpcErr means "reachable"; transport/decode failures
  fail startup. boundedIniInt: readMaybe over partial read.
- Add 127.0.0.0/8 and 0.0.0.0 to isLoopback.
- Replace hand-rolled hex helpers with Data.ByteArray.Encoding; raise
  managerConnCount to match rpcMaxConcurrency; hex Show for NameOwner.
- Fuse parallel http/https when into unless+case; drop reverse/re-reverse
  in mkDomain TLDWeb; first AbiInvariantViolated; Nothing <$ decodeAddress;
  forM_ (eitherToMaybe ...); >>= chain in NameOwner FromJSON.
- Drop dead imports/exports/pragmas and two restating comments.
- Tests: factor unsafeOwner/unsafeLink, addr1/2/3, testNamesConfig; add
  non-ASCII label rejection coverage.

* namespace: bound parser input to 253 bytes (DoS defense)

The bare-name fallback and bareDomain parser would otherwise consume
arbitrarily many non-space bytes via takeWhile1 before any validation
or length check. A crafted multi-megabyte token would be decoded as
UTF-8 and re-parsed in full before being rejected.

Introduce `boundedNonSpace` (scan with 253-byte cap) at the two
takeWhile1 sites. Inputs longer than 253 bytes leave residue that
parseOnly's implicit endOfInput rejects, so the parser fails fast
without ever allocating the full input.

The bound is the DNS full-domain limit, chosen for being a familiar
ceiling generous enough to cover any realistic SimpleX name (longest
plausible @user.subdomain.simplex stays well under 100 bytes). No
per-label cap — SimpleX names don't go through DNS label resolution
and there's no semantic reason to constrain individual labels.

* namespace: switch to Python HTTP resolver + agent plumbing (#1796)

* namespace: relax resolver_endpoint validation (path prefix, http without auth)

validateUrl gains two operator-friendly relaxations and a regression test:

- Allow a path prefix (e.g. https://gw.example.com:443/snrc) for a resolver
  behind a reverse-proxy sub-path; /resolve/<name> and /health are appended
  (HttpResolver already strips one trailing slash, so root and sub-path
  behave identically). Query/fragment/userinfo stay rejected.

- Off-loopback, reject only http WITH resolver_auth (the Authorization header
  would travel in cleartext). http without auth is now allowed (no secret to
  leak; resolver data is public — also lets dev setups reach a host resolver
  via http://host.docker.internal). https is always allowed, with or without
  auth. Plain http has no response integrity; intended for trusted/local
  networks only.

Exports validateUrl and adds validateUrlSpec (11 cases) to SMPNamesTests.

* namespace: NameRecord links as arrays (multi-link, cap 5)

* namespace: distinct RSLV error responses

RSLV collapsed every non-hit (no resolver, malformed name, not found,
backing-store failure) to ERR AUTH, so a client iterating its configured
servers could not tell "this router has no resolver, try the next" from
"name not registered, stop", and a transient backend error read as an
authoritative miss.

Names capability is runtime config, orthogonal to the linear SMP version
(a future v21 router without [NAMES] must still advertise v21), so it is
signalled by a command-time error like allowSMPProxy, not by the version
range:

  no resolver configured -> ERR CMD PROHIBITED  (client skips, tries next)
  backing-store failure   -> ERR INTERNAL        (transient: retry/surface)
  not found / malformed   -> ERR AUTH            (authoritative "no such name")

Update the protocol spec error table and add agent tests for the
no-resolver (CMD PROHIBITED) and backend-failure (INTERNAL) paths.

* refactor(names): server role + one error type

Addresses epoberezkin's review (PR #1784). Name resolution becomes a
server role like proxy; the agent owns resolution + server selection;
one error type flows through the whole stack.

- ServerRoles gains `names`; UserServers gains `nameSrvs` (opt-in list);
  resolveSimplexName drops the explicit server arg and picks a
  names-capable server via getNextServer.
- RSLV carries SimplexNameDomain (was RslvRequest): no JSON on the wire,
  contract dropped, name validated at parse (invalid -> CMD SYNTAX).
- Version check moves from the encoder to Client.hs (no ERR to server).
- ErrorType.NAME {nameErr :: NameErrorType} (+ AgentErrorType.NAME),
  wire- and JSON-encoded; resolver errors surface with diagnostics.
  Success response renamed NAME -> RNAME to free the collision.
- NameOwner -> EthAddress (record selector); NameRecord derives FromJSON
  and gains field-ordered Encoding; per-field caps removed.
- Remove newEnvWithNames / runSMPServerBlockingWithNames test seams;
  stub resolver folded into ServerConfig.namesResolverCall_.

* test(server): update stats backup line count

NameResolverStatsData adds 6 lines to the server stats backup (the
"rslvStats:" header plus the reqs/succ/notFound/resolverErrs/disabled
fields), so testRestoreMessages' expected stats-backup line count is
95 -> 101.

* feat(names): public-namespace resolution via RSLV/RNAME

SNRC names resolver role: RSLV command -> HTTP resolver -> RNAME record.
Agent owns server selection (ServerRoles.names); NAME error family; async,
concurrency-bounded resolution; length-prefixed extensible wire; spec.

* remove comments

Co-authored-by: Evgeny <evgeny@poberezkin.com>

* simplify

* move tests name

* simplify: text addresses, Tail JSON, drop admitRslv

* fix

* remove spaghetti

* reduce diff

* async again, refactor

* different threads limit for name resolutions

* remove comment

* FromField instance for SimplexNameInfo

* remove comments

* unStrJSON

* add sameConnShortLink

* remove scheme prefix

* remove unused import

* remove connecttarget tests

* remove comment

* comment

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-06-30 22:54:55 +01:00
2026-06-06 09:04:26 +01:00
2022-11-26 14:29:59 +00:00
2026-05-21 14:14:03 +01:00
2026-06-23 16:30:44 +01:00
2023-10-22 10:50:36 +01:00
2026-05-01 17:57:19 +01:00
2025-01-30 09:22:13 +00:00
2023-10-22 09:20:14 +01:00
2020-10-13 18:48:41 +01:00
2020-10-11 11:00:25 +01:00

SimpleXMQ

GitHub build GitHub release

📢 SimpleXMQ v1 is released - with many security, privacy and efficiency improvements, new functionality - see release notes.

Please note: v1 is not backwards compatible, but it has the version negotiation built into all protocol layers for forwards compatibility of this version and backwards compatibility of the future versions, that will be backwards compatible for at least two versions back.

If you have a server deployed please deploy a new server to a new host and retire the previous version once it is no longer used.

Message broker for unidirectional (simplex) queues

SimpleXMQ is a message broker for managing message queues and sending messages over public network. It consists of SMP server, SMP client library and SMP agent that implement SMP protocol for client-server communication and SMP agent protocol to manage duplex connections via simplex queues on multiple SMP servers.

SMP protocol is inspired by Redis serialization protocol, but it is much simpler - it currently has only 10 client commands and 8 server responses.

SimpleXMQ is implemented in Haskell - it benefits from robust software transactional memory (STM) and concurrency primitives that Haskell provides.

SimpleXMQ roadmap

  • SimpleX service protocol and application template - to enable users building services and chat bots that work over SimpleX protocol stack. The first such service will be a notification service for a mobile app.
  • SMP queue redundancy and rotation in SMP agent connections.
  • SMP agents synchronization to share connections and messages between multiple agents (it would allow using multiple devices for simplex-chat).

Components

SMP server

SMP server can be run on any Linux distribution, including low power/low memory devices. OpenSSL library is required for initialization.

To initialize the server use smp-server init -n <fqdn> (or smp-server init --ip <ip> for IP based address) command - it will generate keys and certificates for TLS transport. The fingerprint of offline certificate is used as part of the server address to protect client/server connection against man-in-the-middle attacks: smp://<fingerprint>@<hostname>[:5223].

SMP server uses in-memory persistence with an optional append-only log of created queues that allows to re-start the server without losing the connections. This log is compacted on every server restart, permanently removing suspended and removed queues.

To enable store log, initialize server using smp-server -l command, or modify smp-server.ini created during initialization (uncomment enable = on option in the store log section). Use smp-server --help for other usage tips.

Starting from version 2.3.0, when store log is enabled, the server would also enable saving undelivered messages on exit and restoring them on start. This can be disabled via a separate setting restore_messages in smp-server.ini file. Saving messages would only work if the server is stopped with SIGINT signal (keyboard interrupt), if it is stopped with SIGTERM signal the messages would not be saved.

Please note: On initialization SMP server creates a chain of two certificates: a self-signed CA certificate ("offline") and a server certificate used for TLS handshake ("online"). You should store CA certificate private key securely and delete it from the server. If server TLS credential is compromised this key can be used to sign a new one, keeping the same server identity and established connections. CA private key location by default is /etc/opt/simplex/ca.key.

SMP server implements SMP protocol.

Running SMP server on MacOS

SMP server requires OpenSSL library for initialization. On MacOS OpenSSL library may be replaced with LibreSSL, which doesn't support required algorithms. Before initializing SMP server verify you have OpenSSL installed:

openssl version

If it says "LibreSSL", please install original OpenSSL:

brew update
brew install openssl
echo 'PATH="/opt/homebrew/opt/openssl@3/bin:$PATH"' >> ~/.zprofile # or follow whatever instructions brew suggests
. ~/.zprofile # or restart your terminal to start a new session

Now openssl version should be saying "OpenSSL". You can now run smp-server init to initialize your SMP server.

SMP client library

SMP client is a Haskell library to connect to SMP servers that allows to:

  • execute commands with a functional API.
  • receive messages and other notifications via STM queue.
  • automatically send keep-alive commands.

SMP agent

SMP agent library can be used to run SMP agent as part of another application and to communicate with the agent via STM queues, without serializing and parsing commands and responses.

Haskell type ACommand represents SMP agent protocol to communicate via STM queues.

See simplex-chat terminal UI for the example of integrating SMP agent into another application.

SMP agent executable can be used to run a standalone SMP agent process that implements plaintext SMP agent protocol via TCP port 5224, so it can be used via telnet. It can be deployed in private networks to share access to the connections between multiple applications and services.

Using SMP server and SMP agent

You can either run your own SMP server locally or deploy using Linode StackScript, or try local SMP agent with the deployed servers:

smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im

smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im

smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im

It's the easiest to try SMP agent via a prototype simplex-chat terminal UI.

Deploy SMP/XFTP servers on Linux

You can run your SMP/XFTP server as a Linux process, optionally using a service manager for booting and restarts.

Notice that smp-server and xftp-server requires openssl as run-time dependency (it is used to generate server certificates during initialization). Install it with your packet manager:

# For Ubuntu
apt update && apt install openssl

Install binaries

Using Docker

On Linux, you can deploy smp and xftp server using Docker. This will download image from Docker Hub.

  1. Create directories for persistent Docker configuration:

    mkdir -p $HOME/simplex/{xftp,smp}/{config,logs} && mkdir -p $HOME/simplex/xftp/files
    
  2. Run your Docker container.

    • smp-server

      You must change your_ip_or_domain. -e "pass=password" is optional variable to password-protect your smp server:

      docker run -d \
          -e "ADDR=your_ip_or_domain" \
          -e "PASS=password" \
          -p 5223:5223 \
          -v $HOME/simplex/smp/config:/etc/opt/simplex:z \
          -v $HOME/simplex/smp/logs:/var/opt/simplex:z \
          simplexchat/smp-server:latest
      
    • xftp-server

      You must change your_ip_or_domain and maximum_storage.

      docker run -d \
          -e "ADDR=your_ip_or_domain" \
          -e "QUOTA=maximum_storage" \
          -p 443:443 \
          -v $HOME/simplex/xftp/config:/etc/opt/simplex-xftp:z \
          -v $HOME/simplex/xftp/logs:/var/opt/simplex-xftp:z \
          -v $HOME/simplex/xftp/files:/srv/xftp:z \
          simplexchat/xftp-server:latest
      

Using installation script

Please note that currently, only Ubuntu distribution is supported.

You can install and setup servers automatically using our script:

curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/simplex-chat/simplexmq/stable/install.sh -o simplex-server-install.sh &&\
if echo '53fcdb4ceab324316e2c4cda7e84dbbb344f32550a65975a7895425e5a1be757 simplex-server-install.sh' | sha256sum -c; then
  chmod +x ./simplex-server-install.sh
  ./simplex-server-install.sh
  rm ./simplex-server-install.sh
else
  echo "SHA-256 checksum is incorrect!"
  rm ./simplex-server-install.sh
fi

Build from source

Using Docker

Please note: to build the app use source code from stable branch.

On Linux, you can build smp server using Docker.

  1. Build your images:

    git clone https://github.com/simplex-chat/simplexmq
    cd simplexmq
    git checkout stable
    DOCKER_BUILDKIT=1 docker build -t local/smp-server --build-arg APP="smp-server" --build-arg APP_PORT="5223" . # For xmp-server
    DOCKER_BUILDKIT=1 docker build -t local/xftp-server --build-arg APP="xftp-server" --build-arg APP_PORT="443" . # For xftp-server
    
  2. Create directories for persistent Docker configuration:

    mkdir -p $HOME/simplex/{xftp,smp}/{config,logs} && mkdir -p $HOME/simplex/xftp/files
    
  3. Run your Docker container.

    • smp-server

      You must change your_ip_or_domain. -e "pass=password" is optional variable to password-protect your smp server:

      docker run -d \
          -e "ADDR=your_ip_or_domain" \
          -e "PASS=password" \
          -p 5223:5223 \
          -v $HOME/simplex/smp/config:/etc/opt/simplex:z \
          -v $HOME/simplex/smp/logs:/var/opt/simplex:z \
          simplexchat/smp-server:latest
      
    • xftp-server

      You must change your_ip_or_domain and maximum_storage.

      docker run -d \
          -e "ADDR=your_ip_or_domain" \
          -e "QUOTA=maximum_storage" \
          -p 443:443 \
          -v $HOME/simplex/xftp/config:/etc/opt/simplex-xftp:z \
          -v $HOME/simplex/xftp/logs:/var/opt/simplex-xftp:z \
          -v $HOME/simplex/xftp/files:/srv/xftp:z \
          simplexchat/xftp-server:latest
      

Using your distribution

  1. Install dependencies and build tools (GHC, cabal and dev libs):

    # On Ubuntu. Depending on your distribution, use your package manager to determine package names.
    sudo apt-get update && apt-get install -y build-essential curl libffi-dev libffi7 libgmp3-dev libgmp10 libncurses-dev libncurses5 libtinfo5 pkg-config zlib1g-dev libnuma-dev libssl-dev
    export BOOTSTRAP_HASKELL_GHC_VERSION=9.6.3
    export BOOTSTRAP_HASKELL_CABAL_VERSION=3.10.3.0
    curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh
    ghcup set ghc "${BOOTSTRAP_HASKELL_GHC_VERSION}"
    ghcup set cabal "${BOOTSTRAP_HASKELL_CABAL_VERSION}"
    source ~/.ghcup/env
    
  2. Build the project:

    git clone https://github.com/simplex-chat/simplexmq
    cd simplexmq
    git checkout stable
    cabal update
    cabal build exe:smp-server exe:xftp-server
    
  3. List compiled binaries:

    smp-server

    cabal list-bin exe:smp-server
    

    xftp-server

    cabal list-bin exe:xftp-server
    
  • Initialize SMP server with smp-server init [-l] -n <fqdn> or smp-server init [-l] --ip <ip> - depending on how you initialize it, either FQDN or IP will be used for server's address.

  • Run smp-server start to start SMP server, or you can configure a service manager to run it as a service.

  • Optionally, smp-server can be setup for having an onion address in tor network. See: scripts/tor. In this case, the server address can have both public and onion hostname pointing to the same server, to allow two people connect when only one of them is using Tor. The server address would be: smp://<fingerprint>@<public_hostname>,<onion_hostname>

See this section for more information. Run smp-server -h and smp-server init -h for explanation of commands and options.

Linode

Deploy SMP server on Linode

* You can use free credit Linode offers when creating a new account to deploy an SMP server.

Deployment on Linode is performed via StackScripts, which serve as recipes for Linode instances, also called Linodes. To deploy SMP server on Linode:

  • Create a Linode account or login with an already existing one.
  • Open SMP server StackScript and click "Deploy New Linode".
  • You can optionally configure the following parameters:
    • SMP Server store log flag for queue persistence on server restart, recommended.
    • Linode API token to attach server address etc. as tags to Linode and to add A record to your 2nd level domain (e.g. example.com domain should be created in your account prior to deployment). The API token access scopes:
      • read/write for "linodes"
      • read/write for "domains"
    • Domain name to use instead of Linode IP address, e.g. smp1.example.com.
  • Choose the region and plan, Shared CPU Nanode with 1Gb is sufficient.
  • Provide ssh key to be able to connect to your Linode via ssh. If you haven't provided a Linode API token this step is required to login to your Linode and get the server's fingerprint either from the welcome message or from the file /etc/opt/simplex/fingerprint after server starts. See Linode's guide on ssh .
  • Deploy your Linode. After it starts wait for SMP server to start and for tags to appear (if a Linode API token was provided). It may take up to 5 minutes depending on the connection speed on the Linode. Connecting Linode IP address to provided domain name may take some additional time.
  • Get address and fingerprint either from Linode tags (click on a tag and copy it's value from the browser search panel) or via ssh.
  • Great, your own SMP server is ready! If you provided FQDN use smp://<fingerprint>@<fqdn> as SMP server address in the client, otherwise use smp://<fingerprint>@<ip_address>.

Please submit an issue if any problems occur.

DigitalOcean

Deploy SMP server on DigitalOcean

🚧 DigitalOcean snapshot is currently not up to date, it will soon be updated 🏗️

* When creating a DigitalOcean account you can use this link to get free credit. (You would still be required either to provide your credit card details or make a confirmation pre-payment with PayPal)

To deploy SMP server use SimpleX Server 1-click app from DigitalOcean marketplace:

  • Create a DigitalOcean account or login with an already existing one.
  • Click 'Create SimpleX server Droplet' button.
  • Choose the region and plan according to your requirements (Basic plan should be sufficient).
  • Finalize Droplet creation.
  • Open "Console" on your Droplet management page to get SMP server fingerprint - either from the welcome message or from /etc/opt/simplex/fingerprint. Alternatively you can manually SSH to created Droplet, see DigitalOcean instruction.
  • Great, your own SMP server is ready! Use smp://<fingerprint>@<ip_address> as SMP server address in the client.

Please submit an issue if any problems occur.

Please note: SMP server uses server address as a Common Name for server certificate generated during initialization. If you would like your server address to be FQDN instead of IP address, you can log in to your Droplet and run the commands below to re-initialize the server. Alternatively you can use Linode StackScript which allows this parameterization.

smp-server delete
smp-server init [-l] -n <fqdn>

SMP server design

SMP server design

SMP agent design

SMP agent design

License

AGPL v3

S
Description
No description provided
Readme AGPL-3.0 59 MiB
Languages
Haskell 51.4%
JavaScript 38.9%
TypeScript 3%
HTML 2.7%
Shell 1.3%
Other 2.7%